Merge pull request #34 from saturnclientmc/rewritten-with-common-system

Rewritten with common system (Multi version support)
This commit is contained in:
2026-03-07 09:38:50 -08:00
committed by GitHub
316 changed files with 4296 additions and 2490 deletions
-12
View File
@@ -1,12 +0,0 @@
<factorypath>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/modules-2/files-2.1/io.github.llamalad7/mixinextras-fabric/0.4.1/8d1a9e96afb990367fa1f904d17580d164da72e3/mixinextras-fabric-0.4.1.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-analysis/9.7.1/f97a3b319f0ed6a8cd944dc79060d3912a28985f/asm-analysis-9.7.1.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/modules-2/files-2.1/net.fabricmc/fabric-mixin-compile-extensions/0.6.0/6fda0c85c419f0a259da017a1424f27507eb30f0/fabric-mixin-compile-extensions-0.6.0.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/modules-2/files-2.1/net.fabricmc/mapping-io/0.2.1/2a1aeedce104191bbb9e39e26abfbfeaf9b0590b/mapping-io-0.2.1.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-tree/9.7.1/3a53139787663b139de76b627fca0084ab60d32c/asm-tree-9.7.1.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/fabric-loom/1.21.4/net.fabricmc.yarn.1_21_4.1.21.4+build.7-v2/mappings.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/modules-2/files-2.1/net.fabricmc/sponge-mixin/0.15.4+mixin.0.8.7/6a12aacc794f1078458433116e9ed42c1cc98096/sponge-mixin-0.15.4+mixin.0.8.7.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/9.7.1/f0ed132a49244b042cd0e15702ab9f2ce3cc8436/asm-9.7.1.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-util/9.7.1/9e23359b598ec6b74b23e53110dd5c577adf2243/asm-util-9.7.1.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="EXTJAR" id="/Users/klestiselimaj/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-commons/9.7.1/406c6a2225cfe1819f102a161e54cc16a5c24f75/asm-commons-9.7.1.jar" enabled="true" runInBatchMode="false"/>
</factorypath>
-45
View File
@@ -1,45 +0,0 @@
name: Automate Gradlew Build
on:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "21"
check-latest: true
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-home-cache-cleanup: false
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build (release)
run: ./gradlew clean build -x test --no-daemon --stacktrace
- name: Remove sources JARs
run: |
rm -f build/libs/*sources*.jar
echo "Removed sources JARs from build/libs"
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: saturnclient-latest
path: build/libs/saturnclient-*.jar
if-no-files-found: error
-52
View File
@@ -1,52 +0,0 @@
name: Push Builds
on:
push:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "21"
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-home-cache-cleanup: false
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build
run: ./gradlew clean build -x test --no-daemon --stacktrace
- name: Remove sources JARs
run: |
rm -f build/libs/*sources*.jar
echo "Removed sources JARs from build/libs"
- name: Create timestamped JAR
run: |
TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
MAIN_JAR=$(ls build/libs/saturnclient-*.jar | head -1)
cp "$MAIN_JAR" "saturnclient-${TIMESTAMP}.jar"
echo "Created: saturnclient-${TIMESTAMP}.jar"
- name: Upload timestamped artifact
uses: actions/upload-artifact@v4
with:
name: saturnclient-${{ github.run_number }}
path: saturnclient-*.jar
if-no-files-found: error
+1 -1
View File
@@ -1,6 +1,6 @@
# Licenses
This project is under [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0), but a lot of code taken from other open source projects that are under other licenses that need to be mentioned due to legal reasons.
This project is under [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0), but a lot of code taken from other open source projects that are under other licenses that need to be mentioned due to legal or ethical reasons.
| File | License |
| --------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
+3
View File
@@ -0,0 +1,3 @@
# Version guide
This guide will help you to add or edit minecraft versions.
+34 -187
View File
@@ -1,190 +1,37 @@
/*
* ================================================================
* Saturn Client - Fabric Mod Build Configuration
* ================================================================
*
* This build file configures:
* - Fabric Loom (Minecraft mod development)
* - Dependency management
* - Resource processing
* - Java toolchain (Java 21)
* - Publishing setup
*
* All version values are defined in gradle.properties.
* ================================================================
*/
plugins {
// Fabric mod development plugin
id 'fabric-loom' version '1.9-SNAPSHOT'
// Maven publishing support
id 'maven-publish'
}
/*
* ================================================================
* Project Metadata
* ================================================================
*/
version = project.mod_version
group = project.maven_group
/*
* ================================================================
* Repositories
* ================================================================
* All dependency sources.
*/
repositories {
// Fabric
maven { url 'https://maven.fabricmc.net/' }
// JitPack (GitHub releases)
maven { url 'https://jitpack.io' }
// Parchment mappings (if needed later)
maven { url 'https://maven.parchmentmc.org' }
// Player Animation Library
maven { name = "KoSmX Maven"; url = 'https://maven.kosmx.dev/' }
// Central repository
mavenCentral()
}
/*
* ================================================================
* Dependencies
* ================================================================
*/
dependencies {
// ─────────────────────────────────────────────
// Minecraft Core
// ─────────────────────────────────────────────
minecraft "com.mojang:minecraft:${project.minecraft_version}"
// Yarn mappings (named mappings for dev environment)
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
// Fabric Loader
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// ─────────────────────────────────────────────
// Rendering / Model Libraries
// ─────────────────────────────────────────────
// OBJ Reading Library
implementation "de.javagl:obj:0.4.0"
include "de.javagl:obj:0.4.0"
// SVG Rendering Library
include implementation("com.formdev:svgSalamander:1.1.4")
// ─────────────────────────────────────────────
// Animation
// ─────────────────────────────────────────────
// Player Animation Library
include modImplementation(
"dev.kosmx.player-anim:player-animation-lib-fabric:${project.player_anim}"
)
// Optional: BendyLib
// include modRuntimeOnly(
// "io.github.kosmx.bendy-lib:bendy-lib-fabric:${project.bendylib_version}"
// )
// ─────────────────────────────────────────────
// External Libraries
// ─────────────────────────────────────────────
// Session Java (Saturn auth/session handling)
include implementation("com.github.selimaj-dev:session-java:0.1.6")
}
/*
* ================================================================
* Resource Processing
* ================================================================
* Injects version values into fabric.mod.json.
*/
processResources {
inputs.property "version", project.version
inputs.property "minecraft_version", project.minecraft_version
inputs.property "loader_version", project.loader_version
filteringCharset "UTF-8"
filesMatching("fabric.mod.json") {
expand(
"version": project.version,
"minecraft_version": project.minecraft_version,
"loader_version": project.loader_version
)
}
}
/*
* ================================================================
* Java Configuration
* ================================================================
* Targeting Java 21.
*/
def targetJavaVersion = 21
tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
options.release.set(targetJavaVersion)
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
archivesBaseName = project.archives_base_name
withSourcesJar()
}
/*
* ================================================================
* Jar Packaging
* ================================================================
* Includes LICENSE and NOTICE inside the final jar.
*/
jar {
from("LICENSE") {
rename { "${it}_${project.archivesBaseName}" }
}
from("NOTICE") {
rename { "${it}_${project.archivesBaseName}" }
}
}
/*
* ================================================================
* Publishing
* ================================================================
* Configured but no remote repository defined yet.
*/
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
subprojects {
apply plugin: "java"
repositories {
// Add publishing repository here when needed
mavenCentral()
maven { url "https://maven.fabricmc.net/" }
maven { url "https://jitpack.io" }
}
}
dependencies {
implementation "de.javagl:obj:0.4.0"
implementation "com.formdev:svgSalamander:1.1.4"
implementation "com.github.selimaj-dev:session-java:0.1.6"
}
}
configure(subprojects.findAll { it.name != "common" }) {
dependencies {
implementation project(":common")
sourceSets {
main {
resources {
srcDir project(":common").file("src/main/resources")
}
}
}
}
}
configure(subprojects.findAll { it.name == "common" }) {
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0'
implementation 'com.fasterxml.jackson.core:jackson-core:2.17.0'
}
}
+35
View File
@@ -0,0 +1,35 @@
# 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
@@ -0,0 +1,201 @@
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
@@ -0,0 +1,2 @@
# common
The cross-version code powering Saturn Client
+7
View File
@@ -0,0 +1,7 @@
plugins {
id "java-library"
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
}
@@ -1,25 +1,22 @@
package org.saturnclient.saturnclient.client;
package org.saturnclient.client;
import java.util.UUID;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.saturnclient.cosmetics.cloak.Cloaks;
import org.saturnclient.cosmetics.hat.Hats;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.common.minecraft.bindings.SaturnClientBindings;
import dev.kosmx.playerAnim.api.layered.AnimationStack;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
import dev.selimaj.session.Session;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.util.Identifier;
public class ServiceClient {
private static Session session;
public static UUID uuid;
public static 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);
@@ -30,31 +27,30 @@ public class ServiceClient {
}
public static boolean authenticate() {
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> {
if (session != null)
SaturnClientBindings.platform().onClientStopping(() -> {
if (session != null) {
try {
session.close();
} catch (Exception e) {
SaturnClient.LOGGER.error("Unable to close Saturn session");
SaturnClientBindings.platform().logError("Unable to close Saturn session", e);
}
}
});
try {
var mcSession = SaturnClient.client.getSession();
if (mcSession == null) {
SaturnClient.LOGGER.error("No active Minecraft session found");
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;
}
String accessToken = mcSession.getAccessToken();
uuid = mcSession.getUuidOrNull();
String username = mcSession.getUsername();
SaturnClient.LOGGER.info("Authenticating with UUID: " + uuid);
SaturnClientBindings.platform().logInfo("Authenticating with UUID: " + uuid);
if (!connectTimeout()) {
SaturnClient.LOGGER.error("Unable to authenticate: Session Server Timeout");
SaturnClientBindings.platform().logError("Unable to authenticate: Session Server Timeout");
return false;
}
@@ -62,22 +58,20 @@ public class ServiceClient {
.get();
for (String availableCloak : response.cloaks()) {
Cloaks.availableCloaks.add(availableCloak);
availableCloaks.add(availableCloak);
}
for (String availableHat : response.hats()) {
Hats.availableHats.add(availableHat);
availableHats.add(availableHat);
}
eventHandlers();
SaturnPlayer.set(new SaturnPlayer(uuid, username, response.cloak(), response.hat()));
Cloaks.loadCloak(uuid);
return true;
} catch (Exception e) {
SaturnClient.LOGGER.error("Authentication failed", e);
SaturnClientBindings.platform().logError("Authentication failed", e);
return false;
}
}
@@ -97,7 +91,7 @@ public class ServiceClient {
}
});
} catch (Exception e) {
SaturnClient.LOGGER.error("Failed to set cloak (service): ", e);
SaturnClientBindings.platform().logError("Failed to set cloak (service): ", e);
}
}
@@ -116,7 +110,7 @@ public class ServiceClient {
}
});
} catch (Exception e) {
SaturnClient.LOGGER.error("Failed to set hat (service): ", e);
SaturnClientBindings.platform().logError("Failed to set hat (service): ", e);
}
}
@@ -126,11 +120,11 @@ public class ServiceClient {
if (throwable != null) {
throwable.printStackTrace();
} else {
Cloaks.availableCloaks.add(itemId);
availableCloaks.add(itemId);
}
});
} catch (Exception e) {
SaturnClient.LOGGER.error("Failed to buy cloak (service): ", e);
SaturnClientBindings.platform().logError("Failed to buy cloak (service): ", e);
}
}
@@ -140,11 +134,11 @@ public class ServiceClient {
if (throwable != null) {
throwable.printStackTrace();
} else {
Hats.availableHats.add(itemId);
availableHats.add(itemId);
}
});
} catch (Exception e) {
SaturnClient.LOGGER.error("Failed to buy hat (service): ", e);
SaturnClientBindings.platform().logError("Failed to buy hat (service): ", e);
}
}
@@ -158,27 +152,26 @@ public class ServiceClient {
}
});
} catch (Exception e) {
SaturnClient.LOGGER.error("Failed to emote (service): ", e);
SaturnClientBindings.platform().logError("Failed to emote (service): ", e);
}
}
public static void eventHandlers() {
session.onNotification(ServiceMethods.EmoteEvent, (data) -> {
for (AbstractClientPlayerEntity player : SaturnClient.client.world.getPlayers()) {
if (player.getUuidAsString().equals(data.from())) {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(player);
if (data.emote() != null && !data.emote().isEmpty()) {
if (animationStack.isActive() && animationStack.getPriority() == 1000) {
animationStack.removeLayer(1000);
}
animationStack.addAnimLayer(1000,
PlayerAnimationRegistry
.getAnimation(Identifier.of("saturnclient", data.emote()))
.playAnimation());
} else {
animationStack.removeLayer(1000);
}
}
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);
}
});
@@ -199,7 +192,7 @@ public class ServiceClient {
}
});
} catch (Exception e) {
SaturnClient.LOGGER.error("Failed to get player", e);
SaturnClientBindings.platform().logError("Failed to get player", e);
}
}
}
@@ -1,9 +1,9 @@
package org.saturnclient.saturnclient.client;
package org.saturnclient.client;
import java.util.UUID;
import org.saturnclient.saturnclient.client.ServiceMethods.Types.SendPlayerRequest;
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.client.ServiceMethods.Types.SendPlayerRequest;
import org.saturnclient.client.player.SaturnPlayer;
import dev.selimaj.session.types.Method;
@@ -110,4 +110,4 @@ public class ServiceMethods {
SendPlayerRequest.class,
Void.class,
String.class);
}
}
@@ -1,11 +1,9 @@
package org.saturnclient.saturnclient.client.player;
package org.saturnclient.client.player;
import java.util.Map;
import java.util.UUID;
import org.saturnclient.saturnclient.config.Config;
import net.minecraft.util.Formatting;
import org.saturnclient.config.Config;
public class Roles {
static enum Role {
@@ -30,32 +28,32 @@ public class Roles {
* - Contributor: Aqua
* - Other/player: White
*/
public static Formatting getIconColor(UUID uuid) {
public static int getIconColor(UUID uuid) {
Role role = ROLES.get(uuid);
if (role == null) {
return Formatting.WHITE;
return 0xFFFFFF;
}
switch (role) {
case OWNER:
return Formatting.DARK_RED;
return 0xAA0000;
case ADMIN:
return Formatting.RED;
return 0xFF5555;
case PARTNER:
return Formatting.GOLD;
return 0xFFAA00;
case CONTRIBUTOR:
return Formatting.AQUA;
return 0x55FFFF;
default:
return Formatting.WHITE;
return 0xFFFFFF;
}
}
public static String getSaturnIndicator() {
return Config.realisticLogo.value ? "" : "";
return Config.realisticLogo.value ? "\uE003" : "\uE002";
}
}
@@ -1,11 +1,10 @@
package org.saturnclient.saturnclient.client.player;
package org.saturnclient.client.player;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.common.minecraft.bindings.SaturnClientBindings;
public class SaturnPlayer {
private static Map<UUID, SaturnPlayer> PLAYERS = new HashMap<>();
@@ -23,17 +22,18 @@ public class SaturnPlayer {
this.name = name;
}
@Nullable
public static SaturnPlayer get() {
return PLAYERS.get(ServiceClient.uuid);
UUID self = SaturnClientBindings.platform().getUuid();
if (self == null) {
return null;
}
return PLAYERS.get(self);
}
@Nullable
public static SaturnPlayer get(UUID uuid) {
return PLAYERS.get(uuid);
}
@Nullable
public static SaturnPlayer get(String name) {
UUID uuid = PLAYER_NAMES.get(name);
if (uuid == null)
@@ -47,7 +47,8 @@ public class SaturnPlayer {
}
public static String[] getExternalUUIDAsString() {
return PLAYERS.keySet().stream().filter(id -> !id.equals(ServiceClient.uuid)).map(UUID::toString)
UUID self = SaturnClientBindings.platform().getUuid();
return PLAYERS.keySet().stream().filter(id -> self == null || !id.equals(self)).map(UUID::toString)
.toArray(String[]::new);
}
@@ -0,0 +1,16 @@
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();
}
@@ -0,0 +1,18 @@
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;
}
}
}
@@ -0,0 +1,34 @@
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();
}
@@ -0,0 +1,31 @@
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;
}
}
@@ -0,0 +1,7 @@
package org.saturnclient.common.minecraft.bindings;
import java.util.UUID;
public interface SaturnEmoteBindings {
void setEmote(UUID fromPlayerUuid, String emoteIdOrNull);
}
@@ -0,0 +1,33 @@
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();
}
}
@@ -0,0 +1,9 @@
package org.saturnclient.common.minecraft.bindings;
import org.saturnclient.common.minecraft.MinecraftBinding;
public class SaturnItemStack extends MinecraftBinding {
public SaturnItemStack(Object binding) {
super(binding);
}
}
@@ -0,0 +1,19 @@
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);
}
@@ -0,0 +1,9 @@
package org.saturnclient.common.minecraft.bindings;
import org.saturnclient.common.minecraft.MinecraftBinding;
public class SaturnQuaternionf extends MinecraftBinding {
public SaturnQuaternionf(Object o) {
super(o);
}
}
@@ -0,0 +1,9 @@
package org.saturnclient.common.minecraft.bindings;
import org.saturnclient.common.minecraft.MinecraftBinding;
public class SaturnSprite extends MinecraftBinding {
public SaturnSprite(Object binding) {
super(binding);
}
}
@@ -0,0 +1,25 @@
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();
}
@@ -0,0 +1,11 @@
package org.saturnclient.common.minecraft.render;
public interface IWindow {
public int getWidth();
public int getHeight();
public int getFramebufferWidth();
public int getFramebufferHeight();
}
@@ -1,7 +1,7 @@
package org.saturnclient.saturnclient.config;
package org.saturnclient.config;
import org.saturnclient.saturnclient.config.manager.ConfigManager;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.config.manager.ConfigManager;
import org.saturnclient.config.manager.Property;
public class AnimationConfig {
public static ConfigManager config;
@@ -1,11 +1,13 @@
package org.saturnclient.saturnclient.config;
package org.saturnclient.config;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.config.manager.ConfigManager;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.resources.Textures;
import java.io.File;
import net.minecraft.util.Identifier;
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;
@@ -13,15 +15,17 @@ public class 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(GLFW.GLFW_KEY_B);
public static Property<Integer> openEmoteWheel = Property.keybinding(Key.GLFW_KEY_B);
public static Property<Boolean> stagger = Property.bool(true);
public static Identifier getLogo() {
public static SaturnIdentifier getLogo() {
return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO;
}
public static void init() {
config = new ConfigManager("Saturn Client");
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);
@@ -1,7 +1,7 @@
package org.saturnclient.saturnclient.config;
package org.saturnclient.config;
import org.saturnclient.saturnclient.config.manager.ConfigManager;
import org.saturnclient.saturnclient.config.manager.Property;
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)
@@ -1,7 +1,8 @@
package org.saturnclient.saturnclient.config.manager;
package org.saturnclient.config.manager;
import com.google.gson.*;
import org.saturnclient.saturnclient.SaturnClient;
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;
@@ -11,23 +12,36 @@ import java.util.Map;
public class ConfigManager {
private static final File configFile = new File(SaturnClient.client.runDirectory, "saturn.json");
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 JsonObject cachedJson = null;
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 }; // Path is just ["namespace"]
this.path = new String[] { namespace };
ROOT.put(namespace, Property.namespace(currentMap));
}
@@ -37,7 +51,6 @@ public class ConfigManager {
public ConfigManager(ConfigManager parent, String namespace) {
this.currentMap = new LinkedHashMap<>();
// Build new path: parent path + current namespace
this.path = new String[parent.path.length + 1];
System.arraycopy(parent.path, 0, this.path, 0, parent.path.length);
this.path[this.path.length - 1] = namespace;
@@ -64,49 +77,45 @@ public class ConfigManager {
/**
* Load entire config
*/
public static void load() {
SaturnClient.LOGGER.info("Loading config...");
JsonObject json = loadAndCache();
if (json == null)
public void load() {
JsonNode json = loadAndCache();
if (json == null || !json.isObject())
return;
loadRecursive(json, ROOT);
loadRecursive((ObjectNode) json, ROOT);
}
/**
* Save entire config
*/
public static void save() {
SaturnClient.LOGGER.info("Saving config...");
JsonObject rootJson = new JsonObject();
ObjectNode rootJson = MAPPER.createObjectNode();
saveRecursive(rootJson, ROOT);
try {
String formatted = new GsonBuilder()
.setPrettyPrinting()
.create()
.toJson(rootJson);
Files.writeString(configFile.toPath(), formatted);
SaturnClient.LOGGER.info("Config saved successfully.");
MAPPER.writerWithDefaultPrettyPrinter().writeValue(configFile, rootJson);
} catch (IOException e) {
SaturnClient.LOGGER.error("Error saving config file", e);
e.printStackTrace();
}
}
/**
* Recursive save
*/
private static void saveRecursive(JsonObject json, Map<String, Property<?>> map) {
private static void saveRecursive(ObjectNode json, Map<String, Property<?>> map) {
map.forEach((name, prop) -> {
if (prop.isNamespace()) {
JsonObject child = new JsonObject();
ObjectNode child = json.objectNode();
saveRecursive(child, prop.getNamespaceMap());
json.add(name, child);
json.set(name, child);
} else {
json.add(name, prop.toJson());
json.set(name, prop.toJson());
}
});
}
@@ -114,42 +123,52 @@ public class ConfigManager {
/**
* Recursive load
*/
private static void loadRecursive(JsonObject json, Map<String, Property<?>> map) {
private static void loadRecursive(ObjectNode json, Map<String, Property<?>> map) {
map.forEach((name, prop) -> {
JsonElement element = json.get(name);
JsonNode element = json.get(name);
if (element == null)
return;
if (prop.isNamespace() && element.isJsonObject()) {
loadRecursive(element.getAsJsonObject(), prop.getNamespaceMap());
if (prop.isNamespace() && element.isObject()) {
loadRecursive((ObjectNode) element, prop.getNamespaceMap());
} else {
prop.loadFromJson(element);
}
});
}
/**
* Load only a single property immediately after creation,
* traversing the JSON tree based on this manager's path.
* Load only a single property immediately after creation
*/
private void loadSingleProperty(String name, Property<?> prop) {
JsonObject source = cachedJson != null ? cachedJson : loadAndCache();
if (source == null)
JsonNode source = cachedJson != null ? cachedJson : loadAndCache();
if (source == null || !source.isObject())
return;
// Traverse the tree to the correct namespace
JsonObject currentScope = source;
JsonNode currentScope = source;
for (String segment : path) {
JsonElement next = currentScope.get(segment);
if (next != null && next.isJsonObject()) {
currentScope = next.getAsJsonObject();
JsonNode next = currentScope.get(segment);
if (next != null && next.isObject()) {
currentScope = next;
} else {
return; // Path doesn't exist in config yet
return;
}
}
// Now look for the property inside the correctly nested object
JsonElement element = currentScope.get(name);
JsonNode element = currentScope.get(name);
if (element != null) {
prop.loadFromJson(element);
}
@@ -158,21 +177,26 @@ public class ConfigManager {
/**
* Reads file and caches json
*/
private static JsonObject loadAndCache() {
private JsonNode loadAndCache() {
try {
if (!configFile.exists()) {
Files.writeString(configFile.toPath(), "{}");
return new JsonObject();
cachedJson = MAPPER.createObjectNode();
return cachedJson;
}
String json = Files.readString(configFile.toPath());
cachedJson = JsonParser.parseString(json).getAsJsonObject();
cachedJson = MAPPER.readTree(configFile);
return cachedJson;
} catch (IOException e) {
SaturnClient.LOGGER.error("Error reading config file", e);
e.printStackTrace();
return null;
}
}
public Map<String, Property<?>> getProperties() {
@@ -0,0 +1,152 @@
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,15 +1,14 @@
package org.saturnclient.saturnclient.config.manager;
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;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClient;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
public class Property<T> {
public static class NamedProperty<T> {
public String name;
public Property<T> prop;
@@ -45,6 +44,7 @@ public class Property<T> {
}
// ---------- Factory Methods ----------
public static <T> Property<T> from(T value) {
if (value instanceof Boolean)
return new Property<>(value, PropertyType.BOOLEAN);
@@ -98,6 +98,7 @@ public class Property<T> {
}
// ---------- Select Helpers ----------
public void next() {
if (type == PropertyType.SELECT) {
int i = (Integer) value;
@@ -123,6 +124,7 @@ public class Property<T> {
}
// ---------- Lifecycle ----------
public void reset() {
value = defaultValue;
isReset = true;
@@ -141,87 +143,108 @@ public class Property<T> {
}
// ---------- JSON Serialization ----------
/*
* BOOLEAN,
* INTEGER,
* FLOAT,
* STRING,
* HEX,
* NAMESPACE,
* SELECT,
* KEYBINDING,
*/
public JsonElement toJson() {
public JsonNode toJson() {
switch (type) {
case BOOLEAN:
return new JsonPrimitive((Boolean) value);
return JsonNodeFactory.instance.booleanNode((Boolean) value);
case INTEGER:
return new JsonPrimitive((Integer) value);
return JsonNodeFactory.instance.numberNode((Integer) value);
case FLOAT:
return new JsonPrimitive((Float) value);
case NAMESPACE:
JsonObject nested = new JsonObject();
getNamespaceValue().forEach((k, v) -> nested.add(k, v.toJson()));
return nested;
return JsonNodeFactory.instance.numberNode((Float) value);
case STRING:
return JsonNodeFactory.instance.textNode((String) value);
case HEX:
return new JsonPrimitive(String.format("#%08X", (Integer) value));
return JsonNodeFactory.instance.textNode(
String.format("#%08X", (Integer) value));
case KEYBINDING:
return new JsonPrimitive((Integer) value);
case SELECT:
return new JsonPrimitive((Integer) value);
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 new JsonPrimitive(String.valueOf(value));
return JsonNodeFactory.instance.textNode(String.valueOf(value));
}
}
public void loadFromJson(JsonElement element) {
public void loadFromJson(JsonNode element) {
if (element == null)
return;
switch (type) {
case BOOLEAN:
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isBoolean())
setValue(element.getAsBoolean());
if (element.isBoolean())
setValue(element.booleanValue());
break;
case INTEGER:
if (element.isJsonPrimitive()) {
JsonPrimitive p = element.getAsJsonPrimitive();
if (p.isNumber())
setValue(p.getAsInt());
else if (p.isString())
setValue(parseHexToInt(p.getAsString()));
}
if (element.isInt())
setValue(element.intValue());
else if (element.isTextual())
setValue(parseHexToInt(element.textValue()));
break;
case FLOAT:
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber())
setValue(element.getAsFloat());
if (element.isNumber())
setValue(element.floatValue());
break;
case STRING:
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isString())
setValue(element.getAsString());
if (element.isTextual())
setValue(element.textValue());
break;
case HEX:
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isString())
setValue(parseHexToInt(element.getAsString()));
if (element.isTextual())
setValue(parseHexToInt(element.textValue()));
break;
case NAMESPACE:
if (element.isJsonObject()) {
JsonObject obj = element.getAsJsonObject();
getNamespaceValue().forEach((k, v) -> v.loadFromJson(obj.get(k)));
if (element.isObject()) {
ObjectNode obj = (ObjectNode) element;
getNamespaceValue().forEach((k, v) -> {
v.loadFromJson(obj.get(k));
});
}
break;
case SELECT:
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber())
setSelection(element.getAsInt());
if (element.isInt())
setSelection(element.intValue());
break;
case KEYBINDING:
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber())
setValue(element.getAsInt());
if (element.isInt())
setValue(element.intValue());
break;
}
}
// ---------- Utility ----------
@Override
public String toString() {
return String.valueOf(value);
@@ -246,53 +269,52 @@ public class Property<T> {
return new NamedProperty<>(name, this);
}
// ---------- Keybindings ----------
// ---------- Keybindings (no SaturnClient) ----------
public boolean isKeyPressed() {
return (Integer) value != -1 &&
GLFW.glfwGetKey(SaturnClient.client.getWindow().getHandle(), (Integer) value) == GLFW.GLFW_PRESS &&
SaturnClient.client.currentScreen == null;
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 + "\'");
throw new IllegalArgumentException(
"Hex must be 6 or 8 chars long, got '" + hex + "'");
return (int) Long.parseLong(hex, 16);
}
// Misc
// ---------- Misc ----------
public Property<T> copy() {
return new Property<>(this.value, this.type);
}
public void load(String name, JsonElement element) {
if (element != null && element.isJsonObject()) {
load(name, element.getAsJsonObject());
}
}
public void load(String name, JsonObject element) {
JsonElement value = element.get(name);
if (value != null)
this.loadFromJson(value);
}
public boolean isNamespace() {
return type == PropertyType.NAMESPACE;
}
@SuppressWarnings("unchecked")
public Map<String, Property<?>> getNamespaceMap() {
return isNamespace() ? (Map<String, Property<?>>) (Object) value : null;
return isNamespace() ? (Map<String, Property<?>>) value : null;
}
}
}
@@ -1,11 +1,11 @@
package org.saturnclient.cosmetics.hat;
package org.saturnclient.cosmetics;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
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" };
@@ -1,6 +1,6 @@
package org.saturnclient.modules;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui.RenderScope;
public interface HudMod {
public ModDimensions getDimensions();
@@ -2,8 +2,8 @@ package org.saturnclient.modules;
import java.util.Map;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.saturnclient.config.manager.Property.NamedProperty;
import org.saturnclient.config.manager.Property;
import org.saturnclient.config.manager.Property.NamedProperty;
public class ModDimensions {
public Property<Integer> x = Property.integer(0);
@@ -0,0 +1,31 @@
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,11 +1,10 @@
package org.saturnclient.modules;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.saturnclient.config.manager.ConfigManager;
import org.saturnclient.saturnclient.config.manager.Property.NamedProperty;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.util.Identifier;
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;
@@ -38,7 +37,7 @@ public abstract class Module {
return details.name;
}
public Identifier getIconTexture() {
public SaturnIdentifier getIconTexture() {
return Textures.getModIcon(details.namespace);
}
@@ -0,0 +1,32 @@
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);
}
@@ -0,0 +1,20 @@
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);
}
@@ -0,0 +1,11 @@
package org.saturnclient.modules.interfaces;
public interface CoordinatesInterface {
int getPlayerX();
int getPlayerY();
int getPlayerZ();
}
@@ -0,0 +1,7 @@
package org.saturnclient.modules.interfaces;
public interface CrosshairInterface {
boolean isTargetingLivingEntity();
}
@@ -0,0 +1,7 @@
package org.saturnclient.modules.interfaces;
public interface DayCounterInterface {
long getDay();
}
@@ -0,0 +1,7 @@
package org.saturnclient.modules.interfaces;
public interface FpsInterface {
int getFps();
}
@@ -0,0 +1,11 @@
package org.saturnclient.modules.interfaces;
public interface FreelookInterface {
boolean isFirstPerson();
void setFirstPerson();
void setThirdPersonBack();
}
@@ -0,0 +1,7 @@
package org.saturnclient.modules.interfaces;
public interface HealthDisplayInterface {
float getPlayerHealth();
}
@@ -0,0 +1,13 @@
package org.saturnclient.modules.interfaces;
public interface KeystrokesInterface {
boolean isForwardPressed();
boolean isBackPressed();
boolean isLeftPressed();
boolean isRightPressed();
boolean isJumpPressed();
boolean isAttackPressed();
boolean isUsePressed();
}
@@ -0,0 +1,23 @@
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
}
}
@@ -0,0 +1,8 @@
package org.saturnclient.modules.interfaces;
public interface PingInterface {
/** Returns current player ping in milliseconds */
int getPing();
}
@@ -0,0 +1,20 @@
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;
}
}
}
@@ -0,0 +1,36 @@
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; }
}
}
@@ -0,0 +1,118 @@
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;
}
}
@@ -0,0 +1,50 @@
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;
}
}
@@ -7,9 +7,9 @@ import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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);
@@ -19,14 +19,13 @@ public class Clock extends Module implements HudMod {
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()
);
.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
@@ -45,17 +44,23 @@ public class Clock extends Module implements HudMod {
switch (format.value) {
case 0:
if (seconds.value) { text = currentTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")); }
else { text = currentTime.format(DateTimeFormatter.ofPattern("HH:mm")); }
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")); }
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);
0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
dimensions.height = 18 * text.split("\n").length;
}
@@ -4,27 +4,36 @@ import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
import net.minecraft.util.math.Vec3d;
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 Property<Integer> displayMethod = Property.select(
0,
"Flat",
"Flat annotated",
"Newline",
"Newline annotated");
private static ModDimensions dimensions = new ModDimensions(40, 18);
public Coordinates() {
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()
);
.description("Displays your current coordinates")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
displayMethod.named("Display Method"),
dimensions.prop());
this.minecraft = minecraft;
}
@Override
@@ -34,26 +43,40 @@ public class Coordinates extends Module implements HudMod {
@Override
public void renderHud(RenderScope scope) {
Vec3d pos = SaturnClient.client.player.getPos();
renderCoords((int) pos.x, (int) pos.y, (int) pos.z, 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"; // Flat
String f = "%d %d %d";
switch (displayMethod.value) {
case 1: // Flat annotated
case 1:
f = "X: %d Y: %d Z: %d";
break;
case 2: // Newline
case 2:
f = "%d\n%d\n%d";
break;
case 3: // Newline annotated
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);
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;
}
@@ -72,4 +95,4 @@ public class Coordinates extends Module implements HudMod {
public void onEnabled(boolean e) {
enabled.value = e;
}
}
}
@@ -0,0 +1,55 @@
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;
}
}
@@ -4,24 +4,29 @@ import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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() {
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()
);
.description("Displays the number of days that have passed")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
this.minecraft = minecraft;
}
@Override
@@ -31,14 +36,20 @@ public class DayCounter extends Module implements HudMod {
@Override
public void renderHud(RenderScope scope) {
day = (SaturnClient.client.world.getTimeOfDay() / 24000L);
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);
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;
}
@@ -57,5 +68,4 @@ public class DayCounter extends Module implements HudMod {
public void onEnabled(boolean e) {
enabled.value = e;
}
}
}
@@ -4,35 +4,46 @@ import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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());
public Fps() {
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());
.description("Displays current FPS")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
this.minecraft = minecraft;
}
@Override
public void renderHud(RenderScope scope) {
String text = String.valueOf(SaturnClient.client.getCurrentFps()) + " FPS";
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);
}
@@ -50,4 +61,4 @@ public class Fps extends Module implements HudMod {
public ModDimensions getDimensions() {
return dimensions;
}
}
}
@@ -1,61 +1,78 @@
package org.saturnclient.modules.mods;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.manager.Property;
import net.minecraft.client.option.Perspective;
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(GLFW.GLFW_KEY_H);
public static Property<Integer> freelookKey = Property.keybinding(Key.GLFW_KEY_H);
public static boolean isFreeLooking = false;
private static boolean was1stPerson = false;
public Freelook() {
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")
);
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 = SaturnClient.client.options.getPerspective() == Perspective.FIRST_PERSON;
SaturnClient.client.options.setPerspective(Perspective.THIRD_PERSON_BACK);
was1stPerson = minecraft.isFirstPerson();
minecraft.setThirdPersonBack();
}
private void stopFreelook() {
if (was1stPerson) {
SaturnClient.client.options.setPerspective(Perspective.FIRST_PERSON);
minecraft.setFirstPerson();
}
isFreeLooking = false;
}
@@ -68,4 +85,4 @@ public class Freelook extends Module {
public boolean isEnabled() {
return enabled.value;
}
}
}
@@ -2,7 +2,7 @@ package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.config.manager.Property;
public class Fullbright extends Module {
private static Property<Boolean> enabled = Property.bool(false);
@@ -10,12 +10,12 @@ public class Fullbright extends Module {
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 %"));
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
@@ -27,11 +27,11 @@ public class Fullbright extends Module {
public void onEnabled(boolean e) {
enabled.value = e;
}
public static boolean shouldOverrideBrightness() {
return enabled.value;
}
public static float getBrightnessValue() {
return brightness.value / 10;
}
@@ -4,32 +4,32 @@ import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
import net.minecraft.util.math.Vec3d;
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);
double speed = 0.;
Vec3d velocity;
double y = 0.;
public HealthDisplay() {
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()
);
.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
@@ -39,31 +39,43 @@ public class HealthDisplay extends Module implements HudMod {
@Override
public void renderHud(RenderScope scope) {
float health = SaturnClient.client.player.getHealth();
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:
case 0:
text = String.format("%.0f ", health);
break;
case 1:
case 1:
text = String.format("%.1f ", health);
break;
case 2:
case 2:
text = String.format("%.2f ", health);
break;
}
scope.drawText(text,
0, 0, dimensions.font.value, dimensions.fgColor.value);
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;
}
@@ -82,4 +94,4 @@ public class HealthDisplay extends Module implements HudMod {
public void onEnabled(boolean e) {
enabled.value = e;
}
}
}
@@ -3,10 +3,10 @@ package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.RenderScope;
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 {
@@ -19,6 +19,8 @@ public class Keystrokes extends Module implements HudMod {
private static final ModDimensions dimensions = new ModDimensions(78, 54);
private final KeystrokesInterface minecraft;
// Key states
private boolean w, a, s, d, lmb, rmb, space;
@@ -29,7 +31,7 @@ public class Keystrokes extends Module implements HudMod {
private static final int MOUSE_HEIGHT = 24;
private static final int SPACE_HEIGHT = 19;
public Keystrokes() {
public Keystrokes(KeystrokesInterface minecraft) {
super(new ModuleDetails("Keystrokes", "keystrokes")
.description("Displays movement keystrokes")
.version("v0.2.0")
@@ -40,21 +42,22 @@ public class Keystrokes extends Module implements HudMod {
showSpace.named("Show space"),
dimensions.prop(),
clickFg.named("Clicked fg"),
clickBg.named("Clicked bg")
);
clickBg.named("Clicked bg"));
this.minecraft = minecraft;
dimensions.renderBackground = false;
}
@Override
public void tick() {
w = SaturnClient.client.options.forwardKey.isPressed();
a = SaturnClient.client.options.leftKey.isPressed();
s = SaturnClient.client.options.backKey.isPressed();
d = SaturnClient.client.options.rightKey.isPressed();
lmb = SaturnClient.client.options.attackKey.isPressed();
rmb = SaturnClient.client.options.useKey.isPressed();
space = SaturnClient.client.options.jumpKey.isPressed();
w = minecraft.isForwardPressed();
a = minecraft.isLeftPressed();
s = minecraft.isBackPressed();
d = minecraft.isRightPressed();
lmb = minecraft.isAttackPressed();
rmb = minecraft.isUsePressed();
space = minecraft.isJumpPressed();
updateHeight();
}
@@ -0,0 +1,121 @@
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;
}
}
@@ -2,7 +2,7 @@ package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.config.manager.Property;
public class NoFog extends Module {
private static Property<Boolean> enabled = Property.bool(false);
@@ -10,12 +10,12 @@ public class NoFog extends Module {
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"));
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
@@ -4,34 +4,33 @@ import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
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());
public Ping() {
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());
.description("Displays ping")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
this.pingProvider = pingProvider;
}
@Override
public void renderHud(RenderScope scope) {
MinecraftClient client = MinecraftClient.getInstance();
ClientPlayNetworkHandler handler = client.getNetworkHandler();
String text = "0 ms";
if (handler != null) {
text = String.valueOf(handler.getPlayerListEntry(client.player.getUuid()).getLatency()) + " ms";
}
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);
}
@@ -57,4 +56,4 @@ public class Ping extends Module implements HudMod {
public ModDimensions getDimensions() {
return dimensions;
}
}
}
@@ -0,0 +1,96 @@
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;
}
}
@@ -0,0 +1,86 @@
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;
}
}
@@ -4,9 +4,9 @@ import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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);
@@ -26,12 +26,11 @@ public class Tps extends Module implements HudMod {
public Tps() {
super(new ModuleDetails("TPS Display", "tps")
.description("Displays the TPS")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop()
);
.description("Displays the TPS")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
}
public static void onTimePacket(long worldAge) {
@@ -50,7 +49,8 @@ public class Tps extends Module implements HudMod {
samplesFilled = Math.min(samplesFilled + 1, SAMPLE_COUNT);
double sum = 0;
for (int i = 0; i < samplesFilled; i++) sum += samples[i];
for (int i = 0; i < samplesFilled; i++)
sum += samples[i];
currentTps = sum / samplesFilled;
}
}
@@ -64,8 +64,9 @@ public class Tps extends Module implements HudMod {
lastRealTime = -1;
currentTps = 20.0;
sampleIndex = 0;
samplesFilled = SAMPLE_COUNT;
for (int i = 0; i < SAMPLE_COUNT; i++) samples[i] = 20.0;
samplesFilled = SAMPLE_COUNT;
for (int i = 0; i < SAMPLE_COUNT; i++)
samples[i] = 20.0;
}
@Override
@@ -88,12 +89,12 @@ public class Tps extends Module implements HudMod {
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
public void onEnabled(boolean e) {
enabled.value = e;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
public ModDimensions getDimensions() {
return dimensions;
}
}
@@ -1,31 +1,30 @@
package org.saturnclient.modules.mods;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.config.manager.Property;
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(GLFW.GLFW_KEY_C);
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")
);
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) {
@@ -41,7 +40,7 @@ public class Zoom extends Module {
return zoomLevel.value;
}
public static boolean shouldZoom() {
public static boolean shouldZoom() {
return enabled.value;
}
@@ -1,6 +1,6 @@
package org.saturnclient.ui2;
package org.saturnclient.ui;
import org.saturnclient.ui2.anim.Animation;
import org.saturnclient.ui.anim.Animation;
public class Element {
public int x;
@@ -1,4 +1,4 @@
package org.saturnclient.ui2;
package org.saturnclient.ui;
public class ElementContext {
public int mouseX, mouseY;
@@ -0,0 +1,27 @@
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);
}
@@ -0,0 +1,67 @@
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);
}
@@ -0,0 +1,121 @@
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() {
}
}
@@ -0,0 +1,62 @@
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,9 +1,9 @@
package org.saturnclient.ui2.anim;
package org.saturnclient.ui.anim;
import java.util.function.Function;
import org.saturnclient.saturnclient.config.AnimationConfig;
import org.saturnclient.ui2.Element;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public abstract class Animation {
public int duration;
@@ -1,4 +1,4 @@
package org.saturnclient.ui2.anim;
package org.saturnclient.ui.anim;
public class Curve {
public static double easeOutCubic(double x) {
@@ -1,7 +1,7 @@
package org.saturnclient.ui2.anim;
package org.saturnclient.ui.anim;
import org.saturnclient.saturnclient.config.AnimationConfig;
import org.saturnclient.ui2.Element;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public class Fade extends Animation {
float targetOpacity;
@@ -1,7 +1,7 @@
package org.saturnclient.ui2.anim;
package org.saturnclient.ui.anim;
import org.saturnclient.saturnclient.config.AnimationConfig;
import org.saturnclient.ui2.Element;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public class FadeOut extends Animation {
float targetOpacity;
@@ -1,7 +1,7 @@
package org.saturnclient.ui2.anim;
package org.saturnclient.ui.anim;
import org.saturnclient.saturnclient.config.AnimationConfig;
import org.saturnclient.ui2.Element;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public class SlideFade extends Animation {
public Fade fade;
@@ -1,7 +1,7 @@
package org.saturnclient.ui2.anim;
package org.saturnclient.ui.anim;
import org.saturnclient.saturnclient.config.AnimationConfig;
import org.saturnclient.ui2.Element;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public class SlideX extends Animation {
int offset = 20;
@@ -1,7 +1,7 @@
package org.saturnclient.ui2.anim;
package org.saturnclient.ui.anim;
import org.saturnclient.saturnclient.config.AnimationConfig;
import org.saturnclient.ui2.Element;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public class SlideY extends Animation {
int offset = 20;
@@ -1,21 +1,19 @@
package org.saturnclient.ui2.components;
package org.saturnclient.ui.components;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
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;
Identifier sprite;
SaturnIdentifier sprite;
private Runnable onClick;
private boolean isSelected;
public CosmeticPreview(boolean isSelected, Identifier sprite, Runnable onClick) {
public CosmeticPreview(boolean isSelected, SaturnIdentifier sprite, Runnable onClick) {
this.sprite = sprite;
this.onClick = onClick;
this.isSelected = isSelected;
@@ -29,8 +27,6 @@ public class CosmeticPreview extends Element {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
isSelected ? Theme.ACCENT.value : Theme.PRIMARY.value);
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
int texWidth = width - padding;
int texHeight = height - padding;
@@ -1,15 +1,16 @@
package org.saturnclient.ui2.components;
package org.saturnclient.ui.components;
import java.util.ArrayList;
import java.util.List;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.ElementRenderer;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.Utils;
public class ElementRenderer {
public static void draw(List<Element> elements, Element element) {
public class ElementRendererImpl extends ElementRenderer {
public void draw(List<Element> elements, Element element) {
synchronized (elements) {
elements.add(element);
}
@@ -19,21 +20,20 @@ public class ElementRenderer {
}
}
public static void render(List<Element> elements, long elapsed, RenderScope renderScope, int mouseX, int mouseY) {
public void render(List<Element> elements, long elapsed, RenderScope renderScope, int mouseX, int mouseY) {
for (Element element : elements) {
element.playAnimationFrame(elapsed);
renderScope.matrices.push();
renderScope.getMatrixStack().push();
renderScope.setOpacity(element.opacity);
renderScope.matrices.translate(element.x, element.y, 0);
renderScope.matrices.scale(element.scale, element.scale, 1.0f);
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.matrices.pop();
renderScope.setRenderLayer(null);
renderScope.getMatrixStack().pop();
}
}
public static void mouseClicked(List<Element> elements, double mouseX, double mouseY, int button) {
public void mouseClicked(List<Element> elements, double mouseX, double mouseY, int button) {
if (button == 0) {
for (Element element : new ArrayList<>(elements)) {
element.focused = false;
@@ -52,7 +52,7 @@ public class ElementRenderer {
}
}
public static void mouseScrolled(
public void mouseScrolled(
List<Element> elements,
double mouseX,
double mouseY,
@@ -70,7 +70,7 @@ public class ElementRenderer {
}
}
public static void keyPressed(List<Element> elements, int keyCode, int scanCode, int modifiers) {
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);
@@ -83,14 +83,14 @@ public class ElementRenderer {
}
}
public static void mouseDragged(List<Element> elements, double mouseX, double mouseY, int button, double deltaX,
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 static void mouseReleased(List<Element> elements, double mouseX, double mouseY, int button) {
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,17 +1,15 @@
package org.saturnclient.ui2.components;
package org.saturnclient.ui.components;
import org.saturnclient.modules.Module;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.resources.Fonts;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.screens.ConfigEditor;
import net.minecraft.client.render.RenderLayer;
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 {
@@ -41,8 +39,6 @@ public class SaturnModule extends Element {
getSettingsHitBox(),
getSettingsHitBox());
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
// Resolve theme colors
int bgColor = Theme.PRIMARY.value;
int fgColor = hovered && !settingsHover ? Theme.ACCENT.value : Theme.PRIMARY_FG.value;
@@ -145,7 +141,7 @@ public class SaturnModule extends Element {
1.0f);
if (settingsHover) {
SaturnClient.client.setScreen(new ConfigEditor(mod.getConfig()));
MinecraftProvider.PROVIDER.setScreen(new ConfigEditor(mod.getConfig()));
} else {
mod.setEnabled(!mod.isEnabled());
}
@@ -1,28 +1,26 @@
package org.saturnclient.ui2.components;
package org.saturnclient.ui.components;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Config;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.screens.ConfigEditor;
import org.saturnclient.ui2.screens.ModMenu;
import org.saturnclient.ui2.screens.cosmetics.CloakMenu;
import org.saturnclient.ui2.screens.store.CloakStore;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
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;
Identifier sprite;
SaturnIdentifier sprite;
public SidebarComponent(Identifier sprite, Runnable onClick, boolean end) {
public SidebarComponent(SaturnIdentifier sprite, Runnable onClick, boolean end) {
this.onClick = onClick;
this.end = end;
this.sprite = sprite;
@@ -32,14 +30,14 @@ public class Sidebar extends Element {
Runnable onClose;
SidebarComponent[] components = {
new Sidebar.SidebarComponent(Textures.MODS_TAB, () -> {
SaturnClient.client.setScreen(new ModMenu());
MinecraftProvider.PROVIDER.setScreen(new ModMenu());
}, false),
new Sidebar.SidebarComponent(Textures.SETTINGS, () -> {
SaturnClient.client.setScreen(new ConfigEditor(Config.config));
MinecraftProvider.PROVIDER.setScreen(new ConfigEditor(Config.config));
}, false),
new Sidebar.SidebarComponent(Textures.SHIRT, () -> {
SaturnClient.client.setScreen(new CloakMenu());
MinecraftProvider.PROVIDER.setScreen(new CloakMenu());
}, false),
new Sidebar.SidebarComponent(Textures.CLOSE, () -> {
@@ -47,7 +45,7 @@ public class Sidebar extends Element {
}, true),
new Sidebar.SidebarComponent(Textures.STORE, () -> {
SaturnClient.client.setScreen(new CloakStore());
MinecraftProvider.PROVIDER.setScreen(new CloakStore());
}, true),
};
int active = 0;
@@ -70,8 +68,6 @@ public class Sidebar extends Element {
int s = 30 - (padding * 2);
int s2 = s + padding;
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, Theme.BACKGROUND.value);
int i = 0;
@@ -0,0 +1,26 @@
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,8 +1,8 @@
package org.saturnclient.ui2.components.inputs;
package org.saturnclient.ui.components.inputs;
import org.saturnclient.saturnclient.config.manager.ConfigManager;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.resources.Fonts;
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;
@@ -1,11 +1,11 @@
package org.saturnclient.ui2.components.inputs;
package org.saturnclient.ui.components.inputs;
import java.awt.Color;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
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;
@@ -1,12 +1,11 @@
package org.saturnclient.ui2.components.inputs;
package org.saturnclient.ui.components.inputs;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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 = "";
@@ -22,18 +21,18 @@ public abstract class Input extends Element {
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_BACKSPACE) {
if (keyCode == Key.GLFW_KEY_BACKSPACE) {
if (cursorPosition > 0 && validateBackspace()) {
text = text.substring(0, cursorPosition - 1) +
text.substring(cursorPosition);
cursorPosition--;
backspace();
}
} else if (keyCode == GLFW.GLFW_KEY_LEFT) {
} else if (keyCode == Key.GLFW_KEY_LEFT) {
if (cursorPosition > 0) {
cursorPosition--;
}
} else if (keyCode == GLFW.GLFW_KEY_RIGHT) {
} else if (keyCode == Key.GLFW_KEY_RIGHT) {
if (cursorPosition < text.length()) {
cursorPosition++;
}
@@ -49,7 +48,7 @@ public abstract class Input extends Element {
float textWidth = 0;
for (int i = 0; i < visibleText.length(); i++) {
textWidth = SaturnClient.client.textRenderer.getWidth(visibleText.substring(0, i + 1)) * 0.6f;
textWidth = Fonts.getWidth(visibleText.substring(0, i + 1), Theme.FONT.value) * 0.6f;
if (textWidth > mouseX) {
break;
@@ -1,8 +1,8 @@
package org.saturnclient.ui2.components.inputs;
package org.saturnclient.ui.components.inputs;
import org.saturnclient.saturnclient.config.manager.ConfigManager;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.resources.Fonts;
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;
@@ -1,12 +1,13 @@
package org.saturnclient.ui2.components.inputs;
package org.saturnclient.ui.components.inputs;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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;
@@ -21,7 +22,7 @@ public class KeybindingSelector extends Element {
public void render(RenderScope renderScope, ElementContext ctx) {
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
String name = GLFW.glfwGetKeyName(prop.value, GLFW.glfwGetKeyScancode(prop.value));
String name = MinecraftProvider.PROVIDER.getKeyName(prop.value);
renderScope.drawText(0.6f,
prop.value == -1 ? "<NONE>"
@@ -31,7 +32,7 @@ public class KeybindingSelector extends Element {
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_BACKSPACE) {
if (keyCode == Key.GLFW_KEY_BACKSPACE) {
prop.value = -1;
} else {
prop.value = keyCode;
@@ -1,13 +1,12 @@
package org.saturnclient.ui2.components.inputs;
package org.saturnclient.ui.components.inputs;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.resources.Fonts;
import net.minecraft.client.render.RenderLayer;
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;
@@ -20,8 +19,6 @@ public class Select extends Element {
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawText(0.8f, "<", 0, 0, Theme.FONT.value, Theme.FOREGROUND.value);
String text = prop.getSelection();
@@ -1,10 +1,10 @@
package org.saturnclient.ui2.components.inputs;
package org.saturnclient.ui.components.inputs;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
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;
@@ -1,14 +1,14 @@
package org.saturnclient.ui2.elements;
package org.saturnclient.ui.elements;
import java.util.ArrayList;
import java.util.List;
import org.saturnclient.saturnclient.config.AnimationConfig;
import org.saturnclient.saturnclient.config.Config;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.components.ElementRenderer;
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<>();
@@ -31,7 +31,7 @@ public class AnimationStagger extends Element {
element.animation.delay = delay * children.size();
}
ElementRenderer.draw(children, element);
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);
@@ -39,28 +39,28 @@ public class AnimationStagger extends Element {
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
ElementRenderer.render(children, ctx.elapsed, renderScope,
ElementRenderer.INSTANCE.render(children, ctx.elapsed, renderScope,
ctx.mouseX,
ctx.mouseY);
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
ElementRenderer.mouseClicked(children, mouseX, mouseY, button);
ElementRenderer.INSTANCE.mouseClicked(children, mouseX, mouseY, button);
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
ElementRenderer.keyPressed(children, keyCode, scanCode, modifiers);
ElementRenderer.INSTANCE.keyPressed(children, keyCode, scanCode, modifiers);
}
@Override
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
ElementRenderer.mouseDragged(children, mouseX, mouseY, button, deltaX, deltaY);
ElementRenderer.INSTANCE.mouseDragged(children, mouseX, mouseY, button, deltaX, deltaY);
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {
ElementRenderer.mouseReleased(children, mouseX, mouseY, button);
ElementRenderer.INSTANCE.mouseReleased(children, mouseX, mouseY, button);
}
}
@@ -1,10 +1,10 @@
package org.saturnclient.ui2.elements;
package org.saturnclient.ui.elements;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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;
@@ -1,25 +1,22 @@
package org.saturnclient.ui2.elements;
package org.saturnclient.ui.elements;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
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 {
Identifier sprite;
SaturnIdentifier sprite;
private Runnable onClick;
public ClickableTexture(Identifier sprite, Runnable onClick) {
public ClickableTexture(SaturnIdentifier sprite, Runnable onClick) {
this.sprite = sprite;
this.onClick = onClick;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height,
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
}
@@ -0,0 +1,38 @@
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,13 +1,13 @@
package org.saturnclient.ui2.elements;
package org.saturnclient.ui.elements;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.manager.Property;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.anim.SlideX;
import org.saturnclient.ui2.resources.Fonts;
import org.saturnclient.ui2.resources.Textures;
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);

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