Compare commits

...
14 Commits
Author SHA1 Message Date
selimaj-dev 720ee3d8bd change version to 0.0.4-beta1 2025-08-18 14:00:40 +02:00
selimaj-dev b4b348a54c change version to 0.0.2-beta1 2025-08-18 14:00:23 +02:00
selimaj-dev a3be72c443 Loading in theme manager with refactor 2025-08-18 11:58:14 +02:00
selimaj-dev 1c553d14bf Fixed theme manager bug 2025-08-18 09:25:38 +02:00
selimaj-dev cd967a907c Changed emote icons 2025-08-17 20:34:40 +02:00
selimaj-dev ba04c45b75 readme change 2025-08-17 15:26:45 +02:00
selimaj-dev ecb07ce730 readme change 2025-08-17 15:25:41 +02:00
selimaj-dev 6cd10240fc Merge branch 'refactored-clientcom' 2025-08-17 15:23:24 +02:00
selimaj-dev d162db3464 Fixed keypress and hud rendering 2025-08-17 15:22:00 +02:00
Leo Codes 7e3b6cf7f8 Merge pull request #3 from saturnclientmc/refactored-clientcom
Refactored clientcom
2025-08-16 10:39:06 -04:00
selimaj-dev 6fb38e4f9b Updated ip 2025-08-16 16:33:19 +02:00
selimaj-dev 997fbbe9a9 Working emote system 2025-08-16 16:29:48 +02:00
selimaj-dev 01a8ea15a0 Better cosmetic update system 2025-08-16 16:05:44 +02:00
selimaj-dev 08cee2172c Removed the reload system 2025-08-16 14:17:55 +02:00
28 changed files with 269 additions and 353 deletions
+2 -1
View File
@@ -8,11 +8,12 @@
<b>Saturn Client is a free and open source Minecraft client</b> <b>Saturn Client is a free and open source Minecraft client</b>
</p> </p>
![image](https://github.com/user-attachments/assets/b0b23e72-c2eb-49aa-b49c-4d3cc4d60fcc)
# Credits # Credits
- Cloak rendering: `IIpho3nix` & `Dhyan Blum` - Cloak rendering: `IIpho3nix` & `Dhyan Blum`
- Who Am I: `FokshaWasTaken` - Who Am I: `FokshaWasTaken`
- Panton font (ui): `Ivan Petrov` & `Fontfabric`
- Panorama: https://www.planetminecraft.com/texture-pack/minecraft-cavesand-cliffs-part-ii-main-menu-panorama/ - Panorama: https://www.planetminecraft.com/texture-pack/minecraft-cavesand-cliffs-part-ii-main-menu-panorama/
If any of the code or assets are in your name and you want to remove them, please submit an issue, we are still in alpha to have any artists so we would also appreciate if you could help us make some cosmetics If any of the code or assets are in your name and you want to remove them, please submit an issue, we are still in alpha to have any artists so we would also appreciate if you could help us make some cosmetics
+2 -2
View File
@@ -6,8 +6,8 @@ yarn_mappings=1.21.4+build.7
loader_version=0.16.10 loader_version=0.16.10
fabric_version=0.114.0+1.21.4 fabric_version=0.114.0+1.21.4
mod_version = 0.0.3-beta0 mod_version = 0.0.4-beta1
maven_group = org.saturnclient maven_group = org.saturnclient
archives_base_name = saturnclient archives_base_name = saturnclient
player_anim = 2.0.3+1.21.4 player_anim = 2.0.3+1.21.4
@@ -1,26 +1,19 @@
package org.saturnclient.modules; package org.saturnclient.modules;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.render.RenderLayer;
import org.saturnclient.modules.mods.*; import org.saturnclient.modules.mods.*;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.menus.HudEditor;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui2.RenderScope;
public class ModManager { public class ModManager {
public static Module[] MODS = { public static Module[] MODS = {
new Crosshair(), new Crosshair(),
new AutoSprint(), new AutoSprint(),
new ArmorDisplay(), new ArmorDisplay(),
new Fps(), new Fps(),
new PotionDisplay(), new PotionDisplay(),
new Coordinates(), new Coordinates(),
new Freelook(), new Freelook(),
new Keystrokes() new Keystrokes()
}; };
public static void init() { public static void init() {
@@ -31,39 +24,5 @@ public class ModManager {
} }
} }
}); });
HudRenderCallback.EVENT.register((context, _o) -> {
TextRenderer textRenderer = SaturnClient.client.textRenderer;
if (textRenderer != null &&
!(SaturnClient.client.currentScreen instanceof HudEditor)) {
RenderScope renderScope = new RenderScope(context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers());
for (Module m : MODS) {
if (m instanceof HudMod && m.isEnabled()) {
ModDimensions dim = ((HudMod) m).getDimensions();
renderScope.matrices.push();
renderScope.matrices.translate(dim.x.value, (float) dim.y.value, 0);
renderScope.matrices.scale(dim.scale.value, dim.scale.value, 1.0f);
if (dim.renderBackground) {
renderScope.drawRoundedRectangle(0, 0, dim.width, dim.height, dim.radius.value, dim.bgColor.value);
}
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
((HudMod) m).renderHud(renderScope);
renderScope.matrices.pop();
} else {
m.render(renderScope);
}
}
}
});
} }
} }
@@ -0,0 +1,58 @@
package org.saturnclient.modules.mods.mixins;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.ModManager;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.menus.HudEditor;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui2.RenderScope;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderTickCounter;
@Mixin(InGameHud.class)
public class RenderMixin {
@Inject(method = "renderChat", at = @At("TAIL"))
public void renderMods(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) {
TextRenderer textRenderer = SaturnClient.client.textRenderer;
if (textRenderer != null &&
!(SaturnClient.client.currentScreen instanceof HudEditor)) {
RenderScope renderScope = new RenderScope(context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers());
for (org.saturnclient.modules.Module m : ModManager.MODS) {
if (m instanceof HudMod && m.isEnabled()) {
ModDimensions dim = ((HudMod) m).getDimensions();
renderScope.matrices.push();
renderScope.matrices.translate(dim.x.value, (float) dim.y.value, 0);
renderScope.matrices.scale(dim.scale.value, dim.scale.value, 1.0f);
if (dim.renderBackground) {
renderScope.drawRoundedRectangle(0, 0, dim.width, dim.height, dim.radius.value,
dim.bgColor.value);
}
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
((HudMod) m).renderHud(renderScope);
renderScope.matrices.pop();
} else {
m.render(renderScope);
}
}
}
}
}
@@ -39,9 +39,9 @@ public class SaturnClient implements ModInitializer {
client = MinecraftClient.getInstance(); client = MinecraftClient.getInstance();
SaturnClientConfig.init(); SaturnClientConfig.init();
ModManager.init(); ModManager.init();
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save()); ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save());
SpecialModelLoaderEvents.LOAD_SCOPE.register(() -> { SpecialModelLoaderEvents.LOAD_SCOPE.register(() -> {
return (resourceManager, location) -> MOD_ID.equals(location.getNamespace()); return (resourceManager, location) -> MOD_ID.equals(location.getNamespace());
}); });
@@ -49,11 +49,12 @@ public class SaturnClient implements ModInitializer {
ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
if (screen != null && !(screen instanceof TitleMenu)) { if (screen != null && !(screen instanceof TitleMenu)) {
if (screen instanceof SaturnScreen) { if (screen instanceof SaturnScreen) {
((SaturnScreen) screen).draw(new ImageTexture(Textures.LOGO_TEXT_BIG).dimensions(180, 18).position(screen.width - 180 - 20, screen.height - 18 - 20)); ((SaturnScreen) screen).draw(new ImageTexture(Textures.LOGO_TEXT_BIG).dimensions(180, 18)
.position(screen.width - 180 - 20, screen.height - 18 - 20));
} else { } else {
ScreenEvents.afterRender(screen).register((screen1, context, mouseX, mouseY, tickDelta) -> { ScreenEvents.afterRender(screen).register((screen1, context, mouseX, mouseY, tickDelta) -> {
RenderScope renderScope = new RenderScope(context.getMatrices(), RenderScope renderScope = new RenderScope(context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers()); ((DrawContextAccessor) context).getVertexConsumers());
int width = context.getScaledWindowWidth(); int width = context.getScaledWindowWidth();
int height = context.getScaledWindowHeight(); int height = context.getScaledWindowHeight();
@@ -71,7 +72,7 @@ public class SaturnClient implements ModInitializer {
} }
} }
}); });
KeyInputHandler.register(); KeyInputHandler.register();
Emotes.initialize(); Emotes.initialize();
if (Auth.authenticate()) { if (Auth.authenticate()) {
@@ -2,12 +2,22 @@ package org.saturnclient.saturnclient.auth;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.cosmetics.Hats; import org.saturnclient.saturnclient.cosmetics.Hats;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks; import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
import org.saturnclient.ui2.SaturnScreen; import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.elements.Notification; import org.saturnclient.ui2.elements.Notification;
import org.saturnclient.ui2.elements.Notification.NotificationKind; import org.saturnclient.ui2.elements.Notification.NotificationKind;
import dev.kosmx.playerAnim.api.layered.AnimationStack;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
public class Auth { public class Auth {
@@ -98,24 +108,72 @@ public class Auth {
playerNames.entrySet().removeIf(entry -> entry.getValue().equals(uuid)); playerNames.entrySet().removeIf(entry -> entry.getValue().equals(uuid));
} }
break; break;
case "successful_transaction": case "successful_transaction":
String cloak = parser.getStringOrNull("cloak"); String cloak = parser.getStringOrNull("cloak");
String hat = parser.getStringOrNull("hat"); String hat = parser.getStringOrNull("hat");
if (cloak != null) { if (cloak != null) {
Cloaks.availableCloaks.add(cloak); Cloaks.availableCloaks.add(cloak);
} }
if (hat != null) { if (hat != null) {
Hats.availableHats.add(hat); Hats.availableHats.add(hat);
} }
case "update_cloak":
String targetUuid = parser.getStringOrNull("uuid");
String targetCloak = parser.getStringOrNull("cloak");
if (targetUuid != null && targetCloak != null) {
SaturnPlayer player = players.get(targetUuid);
if (player != null) {
player.cloak = targetCloak;
}
}
break;
case "update_hat":
targetUuid = parser.getStringOrNull("uuid");
String targetHat = parser.getStringOrNull("hat");
if (targetUuid != null && targetHat != null) {
SaturnPlayer player = players.get(targetUuid);
if (player != null) {
player.hat = targetHat;
}
}
break;
case "emote":
targetUuid = parser.getStringOrNull("uuid");
String emote = parser.getStringOrNull("name");
for (AbstractClientPlayerEntity player : SaturnClient.client.world.getPlayers()) {
if (player.getUuidAsString().replace("-", "").equals(targetUuid)) {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(player);
if (emote != null && !emote.isEmpty()) {
if (animationStack.isActive() && animationStack.getPriority() == 1000) {
animationStack.removeLayer(1000);
}
animationStack.addAnimLayer(1000,
PlayerAnimationRegistry
.getAnimation(Identifier.of("saturnclient", emote))
.playAnimation());
} else {
animationStack.removeLayer(1000);
}
}
}
break;
default: default:
if (parser.error != null) { if (parser.error != null) {
SaturnClient.LOGGER.error("Error from the server: " + parser.error); SaturnClient.LOGGER.error("Error from the server: " + parser.error);
if (SaturnClient.client.currentScreen instanceof SaturnScreen) { if (SaturnClient.client.currentScreen instanceof SaturnScreen) {
String[] a = parser.error.replace("!", "").split(": "); String[] a = parser.error.replace("!", "").split(": ");
((SaturnScreen) SaturnClient.client.currentScreen).draw(new Notification(SaturnClient.client.currentScreen.width, SaturnClient.client.currentScreen.height, NotificationKind.Error, a[0], a[1])); ((SaturnScreen) SaturnClient.client.currentScreen)
.draw(new Notification(SaturnClient.client.currentScreen.width,
SaturnClient.client.currentScreen.height, NotificationKind.Error,
a[0], a[1]));
} }
} }
break; break;
@@ -179,7 +237,7 @@ public class Auth {
public static void setCloak(String cloakName) { public static void setCloak(String cloakName) {
try { try {
Network.write("set_cloak@cloak=" + cloakName); Network.write("set_cloak@cloak=" + cloakName + "@notify=" + stringifyPlayers());
} catch (Exception e) { } catch (Exception e) {
SaturnClient.LOGGER.error("Request failed", e); SaturnClient.LOGGER.error("Request failed", e);
} }
@@ -187,12 +245,26 @@ public class Auth {
public static void setHat(String hatName) { public static void setHat(String hatName) {
try { try {
Network.write("set_hat@hat=" + hatName); Network.write("set_hat@hat=" + hatName + "@notify=" + stringifyPlayers());
} catch (Exception e) { } catch (Exception e) {
SaturnClient.LOGGER.error("Request failed", e); SaturnClient.LOGGER.error("Request failed", e);
} }
} }
public static void emote(String name) {
try {
Network.write("emote@name=" + name + "@notify=" + stringifyPlayers());
} catch (Exception e) {
SaturnClient.LOGGER.error("Request failed", e);
}
}
public static String stringifyPlayers() {
return players.keySet().stream()
.filter(key -> key != uuid)
.collect(Collectors.joining("$"));
}
public static void player(String name, String uuid) { public static void player(String name, String uuid) {
try { try {
playerNames.put(name, uuid.replaceAll("-", "")); playerNames.put(name, uuid.replaceAll("-", ""));
@@ -202,41 +274,6 @@ public class Auth {
} }
} }
public static void sendReload() {
for (Map.Entry<String, String> player : playerNames.entrySet()) {
if (players.containsKey(player.getValue()) && !player.getValue().equals(uuid)) {
SaturnClient.client.player.networkHandler
.sendChatCommand("msg " + player.getKey() + " "
+ "$SATURN_RELOAD if you are seeing this as a player, please report this to https://github.com/saturnclientmc/saturnclient/issues");
;
}
}
}
public static void sendEmote(String emote) {
String uuid = SaturnClient.client.player.getUuidAsString().replace("-", "");
for (Map.Entry<String, String> player : playerNames.entrySet()) {
if (players.containsKey(player.getValue()) && !player.getValue().equals(uuid)) {
SaturnClient.client.player.networkHandler
.sendChatCommand("msg " + player.getKey() + " "
+ "$SATURN_EMOTE&@"+uuid+"&@"+emote+"&@ if you are seeing this as a player, please report this to https://github.com/saturnclientmc/saturnclient/issues");
;
}
}
}
public static void cancelEmote() {
String uuid = SaturnClient.client.player.getUuidAsString().replace("-", "");
for (Map.Entry<String, String> player : playerNames.entrySet()) {
if (players.containsKey(player.getValue()) && !player.getValue().equals(uuid)) {
SaturnClient.client.player.networkHandler
.sendChatCommand("msg " + player.getKey() + " "
+ "$SATURN_CANCEL_EMOTE&@"+uuid+"&@ if you are seeing this as a player, please report this to https://github.com/saturnclientmc/saturnclient/issues");
;
}
}
}
public static void buyCloak(String name) { public static void buyCloak(String name) {
try { try {
Network.write("buy_cloak@cloak=" + name); Network.write("buy_cloak@cloak=" + name);
@@ -46,7 +46,6 @@ public class ConfigManager {
// Generic method to store any type of property // Generic method to store any type of property
public <T> Property<T> property(String name, Property<T> value) { public <T> Property<T> property(String name, Property<T> value) {
System.out.println("Adding property: " + name);
currentMap.put(name, value); currentMap.put(name, value);
loadProp(name, value); loadProp(name, value);
return value; return value;
@@ -98,7 +97,6 @@ public class ConfigManager {
new String(Files.readAllBytes(configFile.toPath()))).getAsJsonObject(); new String(Files.readAllBytes(configFile.toPath()))).getAsJsonObject();
for (String namespace : properties.keySet()) { for (String namespace : properties.keySet()) {
SaturnClient.LOGGER.info("Loading namespace: " + namespace);
JsonElement configElement = jsonObject.get(namespace); JsonElement configElement = jsonObject.get(namespace);
if (configElement == null) if (configElement == null)
@@ -120,8 +118,6 @@ public class ConfigManager {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static void loadProperties(JsonObject config, Map<String, Property<?>> propertyMap) { private static void loadProperties(JsonObject config, Map<String, Property<?>> propertyMap) {
SaturnClient.LOGGER.info("Loading properties: " + propertyMap);
for (String propertyName : propertyMap.keySet()) { for (String propertyName : propertyMap.keySet()) {
JsonElement c = config.get(propertyName); JsonElement c = config.get(propertyName);
@@ -50,6 +50,10 @@ public class Property<T> {
} }
} }
public Property<T> copy() {
return new Property<>(this.value, this.type);
}
public static Property<Integer> font(int value) { public static Property<Integer> font(int value) {
return select(value, "Default", "Inter", "Inter bold"); return select(value, "Default", "Inter", "Inter bold");
} }
@@ -89,13 +93,17 @@ public class Property<T> {
} }
public void next() { public void next() {
if ((Integer) value < availableValues.length-1) setValue((Integer)value+1); if ((Integer) value < availableValues.length - 1)
else setValue(0); setValue((Integer) value + 1);
else
setValue(0);
} }
public void prev() { public void prev() {
if ((Integer) value > 0) setValue((Integer)value-1); if ((Integer) value > 0)
else setValue(availableValues.length - 1); setValue((Integer) value - 1);
else
setValue(availableValues.length - 1);
} }
public void setSelection(int selection) { public void setSelection(int selection) {
@@ -156,12 +164,8 @@ public class Property<T> {
return true; return true;
if (value instanceof String && primitive.isString()) if (value instanceof String && primitive.isString())
return true; return true;
if (type == PropertyType.HEX && primitive.isString())
// Check if JSON string represents a valid hex integer return true;
if (value instanceof String && primitive.isString()) {
String str = primitive.getAsString();
return str.matches("0x[0-9A-Fa-f]+");
}
} else if (element.isJsonObject()) { } else if (element.isJsonObject()) {
return isNamespace(value); return isNamespace(value);
} }
@@ -197,18 +201,58 @@ public class Property<T> {
} }
public boolean isKeyPressed() { public boolean isKeyPressed() {
return (Integer) value == -1 ? false : GLFW.glfwGetKey(SaturnClient.client.getWindow().getHandle(), (Integer) (Object) value) == GLFW.GLFW_PRESS; return (Integer) value == -1 ? false
: GLFW.glfwGetKey(SaturnClient.client.getWindow().getHandle(),
(Integer) (Object) value) == GLFW.GLFW_PRESS && SaturnClient.client.currentScreen == null;
} }
public boolean wasKeyPressed() { public boolean wasKeyPressed() {
long window = SaturnClient.client.getWindow().getHandle(); boolean isPressed = this.isKeyPressed();
int key = (Integer) value; wasPressedLastTick = isPressed && !wasPressedLastTick;
return wasPressedLastTick;
}
boolean isPressed = key != -1 && GLFW.glfwGetKey(window, key) == GLFW.GLFW_PRESS; @SuppressWarnings("unchecked")
public void load(String name, JsonElement element) {
if (element != null && element.isJsonObject()) {
JsonElement value = element.getAsJsonObject().get(name);
if (value != null) {
switch (this.getType()) {
case BOOLEAN:
((Property<Boolean>) this).setValue(value.getAsBoolean());
break;
case INTEGER:
JsonPrimitive primitive = value.getAsJsonPrimitive();
if (primitive.isNumber())
((Property<Integer>) this).setValue(value.getAsInt());
else if (primitive.isString())
((Property<Integer>) this).setValue(parseHexToInt(value.getAsString()));
break;
case FLOAT:
((Property<Float>) this).setValue(value.getAsFloat());
break;
case STRING:
((Property<String>) this).setValue(value.getAsString());
break;
case HEX:
((Property<Integer>) this).setValue(parseHexToInt(value.getAsString()));
break;
default:
break;
}
}
}
}
boolean wasJustPressed = isPressed && !wasPressedLastTick; public static int parseHexToInt(String hex) {
wasPressedLastTick = isPressed; hex = hex.replaceAll("#", "");
return wasJustPressed; if (hex.length() == 6) {
hex = "FF" + hex;
} else if (hex.length() != 8) {
throw new IllegalArgumentException("Hex string must be 6 or 8 characters long");
}
return (int) Long.parseLong(hex, 16);
} }
} }
@@ -3,127 +3,75 @@ package org.saturnclient.saturnclient.config;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.HashMap; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
public class ThemeManager { public class ThemeManager {
private static File themeFile = new File( private static File themeFile = new File(
SaturnClient.client.runDirectory, SaturnClient.client.runDirectory,
"saturn.theme.json"); "saturn.theme.json");
private static Map<String, Map<String, Property<?>>> properties = new HashMap<>(); private static List<ThemeManager> theme = new ArrayList<>();
private static JsonObject cachedThemeJson = null; private static JsonObject cachedThemeJson = null;
// private Map<String, Map<String, Property<?>>> states; private Map<String, Map<String, Object>> states;
private Map<String, Property<?>> currentStyling; private Map<String, Property<?>> currentStyling;
private String state;
private String[] states;
private String namespace; private String namespace;
public ThemeManager(String namespace, String... states) { public ThemeManager(String namespace, String... states) {
// this.states = new LinkedHashMap<>();
this.namespace = namespace; this.namespace = namespace;
this.states = states; this.states = new LinkedHashMap<>();
currentStyling = new LinkedHashMap<>(); this.currentStyling = new LinkedHashMap<>();
this.states.put(null, new LinkedHashMap<>());
properties.put(namespace, new LinkedHashMap<>());
for (String s : states) { for (String s : states) {
properties.put(namespace+"@"+s, new LinkedHashMap<>()); this.states.put(s, new LinkedHashMap<>());
} }
theme.add(this);
} }
public void setState(String state) { public void setState(String state) {
this.state = state; for (Map.Entry<String, Object> tProp : states.get(state).entrySet()) {
if (this.state != null) { currentStyling.get(tProp.getKey()).setValue(tProp.getValue());
for (Map.Entry<String, Property<?>> tProp : properties.get(namespace+"@"+state).entrySet()) {
currentStyling.get(tProp.getKey()).setValue(tProp.getValue().value);
}
} else {
for (Map.Entry<String, Property<?>> tProp : properties.get(namespace).entrySet()) {
Property<?> p = currentStyling.get(tProp.getKey());
if (p != null) p.setValue(tProp.getValue().value);
}
} }
} }
public void applyState(String state) { public void applyState(String state) {
if (this.state != null) { for (Map.Entry<String, Object> tProp : states.get(state).entrySet()) {
for (Map.Entry<String, Property<?>> tProp : properties.get(namespace+"@"+state).entrySet()) { if (!states.get(null).get(tProp.getKey()).equals(tProp.getValue()))
Property<?> p = currentStyling.get(tProp.getKey()); currentStyling.get(tProp.getKey()).setValue(tProp.getValue());
Property<?> currProp = properties.get(namespace).get(tProp.getKey());
if (currProp != null && p.value.equals(currProp.value)) {
p.setValue(tProp.getValue().value);
}
}
} else {
setState(state);
} }
} }
// Generic method to store any type of property
public <T> Property<T> property(String name, Property<T> prop) { public <T> Property<T> property(String name, Property<T> prop) {
for (String state : states) { for (Map.Entry<String, Map<String, Object>> state : this.states.entrySet()) {
Property<?> property = Property.from(prop.value); Property<?> property = prop.copy();
properties.get(namespace+"@"+state).put(name, property); if (cachedThemeJson == null) {
loadProp(state, name, property); load();
}
String stateName = state.getKey();
property.load(name, cachedThemeJson.get(stateName != null ? namespace + "@" + stateName : namespace));
state.getValue().put(name, property.value);
} }
loadProp(null, name, prop);
properties.get(namespace).put(name, Property.from(prop.value));
currentStyling.put(name, prop); currentStyling.put(name, prop);
return prop; return prop;
} }
public <T> void propertyStateDefault(String state, String name, T value) { public <T> void propertyStateDefault(String state, String name, T value) {
Property<?> prop = properties.get(namespace+"@"+state).get(name); Property<?> prop = currentStyling.get(name).copy();
prop.setValue(value, value); // sets the current and default value prop.setValue(value, value);
loadProp(state, name, prop);
}
@SuppressWarnings("unchecked")
public <T> void loadProp(String state, String name, Property<T> prop) {
if (cachedThemeJson == null) { if (cachedThemeJson == null) {
load(); load();
} }
prop.load(name, cachedThemeJson.get(state != null ? namespace + "@" + state : namespace));
JsonElement element = cachedThemeJson.get(state != null ? namespace+"@"+state : namespace); states.get(state).put(name, prop.value);
if (element != null && element.isJsonObject()) {
JsonObject theme = element.getAsJsonObject();
JsonElement value = theme.get(name);
if (value != null && prop.matchesJson(value)) {
switch (prop.getType()) {
case BOOLEAN:
((Property<Boolean>) prop).setValue(value.getAsBoolean());
break;
case INTEGER:
((Property<Integer>) prop).setValue(value.getAsInt());
break;
case FLOAT:
((Property<Float>) prop).setValue(value.getAsFloat());
break;
case STRING:
((Property<String>) prop).setValue(value.getAsString());
break;
case HEX:
((Property<Integer>) prop).setValue(value.getAsInt());
break;
default:
break;
}
}
}
} }
public static void load() { public static void load() {
@@ -135,85 +83,11 @@ public class ThemeManager {
} }
cachedThemeJson = JsonParser.parseString( cachedThemeJson = JsonParser.parseString(
new String(Files.readAllBytes(themeFile.toPath()))).getAsJsonObject(); new String(Files.readAllBytes(themeFile.toPath()))).getAsJsonObject();
SaturnClient.LOGGER.info("Theme loaded"); SaturnClient.LOGGER.info("Theme loaded");
} catch (IOException e) { } catch (IOException e) {
SaturnClient.LOGGER.error("Error reading the theme file", e); SaturnClient.LOGGER.error("Error reading the theme file", e);
} }
} }
public static void save() {
try {
SaturnClient.LOGGER.info("Starting to save theme...");
JsonObject jsonObject = new JsonObject();
// Iterate through all namespaces and their properties
for (String namespace : properties.keySet()) {
JsonObject namespaceTheme = new JsonObject();
Map<String, Property<?>> propertyMap = properties.get(namespace);
// Save properties for this namespace
saveProperties(namespaceTheme, propertyMap);
// Add the namespace theme to the main JSON object
jsonObject.add(namespace, namespaceTheme);
}
// Format the json so it can easily be edited
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String formattedJson = gson.toJson(jsonObject);
// Write the JSON object to the theme file
Files.write(themeFile.toPath(), formattedJson.getBytes());
SaturnClient.LOGGER.info("Theme saved successfully.");
} catch (IOException e) {
SaturnClient.LOGGER.error("Error saving the theme file", e);
}
}
@SuppressWarnings("unchecked")
private static void saveProperties(JsonObject theme, Map<String, Property<?>> propertyMap) {
// Iterate through each property in the namespace
for (String propertyName : propertyMap.keySet()) {
Property<?> property = propertyMap.get(propertyName);
JsonElement propertyValue = null;
if (property.getType() == Property.PropertyType.NAMESPACE) {
// Handle nested namespace
JsonObject nestedTheme = new JsonObject();
Map<String, Property<?>> nestedProperties = (Map<String, Property<?>>) property.value;
saveProperties(nestedTheme, nestedProperties);
propertyValue = nestedTheme;
} else {
// Convert the property value based on its type
switch (property.getType()) {
case BOOLEAN:
propertyValue = new JsonPrimitive((Boolean) property.value);
break;
case INTEGER:
propertyValue = new JsonPrimitive((Integer) property.value);
break;
case FLOAT:
propertyValue = new JsonPrimitive((Float) property.value);
break;
case STRING:
propertyValue = new JsonPrimitive((String) property.value);
break;
case HEX:
propertyValue = new JsonPrimitive((Integer) property.value);
break;
default:
SaturnClient.LOGGER.warn(
"Unknown property type for: " + propertyName);
break;
}
}
if (propertyValue != null) {
theme.add(propertyName, propertyValue);
}
}
}
} }
@@ -16,7 +16,6 @@ public class Hats {
public static void setHat(String uuid, String hatName) { public static void setHat(String uuid, String hatName) {
setHatSilent(uuid, hatName); setHatSilent(uuid, hatName);
Auth.setHat(hatName); Auth.setHat(hatName);
Auth.sendReload();
} }
public static void setHatSilent(String uuid, String hatName) { public static void setHatSilent(String uuid, String hatName) {
@@ -45,7 +45,6 @@ public class Cloaks {
public static void setCloak(String uuid, String cloakName) { public static void setCloak(String uuid, String cloakName) {
Auth.setCloak(cloakName); Auth.setCloak(cloakName);
setCloakSilent(uuid, cloakName); setCloakSilent(uuid, cloakName);
Auth.sendReload();
SaturnClient.LOGGER.info("Cloak set to " + cloakName); SaturnClient.LOGGER.info("Cloak set to " + cloakName);
} }
@@ -113,20 +112,25 @@ public class Cloaks {
List<AnimatedCloakData> animatedFrames = new ArrayList<>(); List<AnimatedCloakData> animatedFrames = new ArrayList<>();
for (int i = 0; i < frameCount; i++) { for (int i = 0; i < frameCount; i++) {
String frameId = fileName.replace(".gif", "") + "_frame_" + i; String frameId = fileName.replace(".gif", "") + "_frame_" + i;
Identifier frameIdentifier = Identifier.of(SaturnClient.MOD_ID, "cloaks_" + frameId); Identifier frameIdentifier = Identifier.of(SaturnClient.MOD_ID,
IdentifierUtils.registerBufferedImageTexture(frameIdentifier, frames.get(i)); "cloaks_" + frameId);
IdentifierUtils.registerBufferedImageTexture(frameIdentifier,
frames.get(i));
animatedFrames.add(new AnimatedCloakData(frameIdentifier, delays.get(i))); animatedFrames.add(new AnimatedCloakData(frameIdentifier, delays.get(i)));
} }
animatedCloaks.put(uuid, animatedFrames); animatedCloaks.put(uuid, animatedFrames);
lastFrameTime.put(uuid, System.currentTimeMillis()); lastFrameTime.put(uuid, System.currentTimeMillis());
SaturnClient.LOGGER.info("Loaded " + frames.size() + " frames for animated cloak: " + fileName); SaturnClient.LOGGER.info(
"Loaded " + frames.size() + " frames for animated cloak: " + fileName);
} catch (Exception e) { } catch (Exception e) {
SaturnClient.LOGGER.error("Failed to register animated cloak textures: " + fileName, e); SaturnClient.LOGGER
.error("Failed to register animated cloak textures: " + fileName, e);
} }
}); });
} }
} catch (IOException e) { } catch (IOException e) {
SaturnClient.LOGGER.error("Failed to load animated cloak from resources: " + player.cloak + ".gif", e); SaturnClient.LOGGER
.error("Failed to load animated cloak from resources: " + player.cloak + ".gif", e);
} }
}).start(); }).start();
} else { } else {
@@ -7,7 +7,7 @@ import net.minecraft.client.util.InputUtil;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig; import org.saturnclient.saturnclient.SaturnClientConfig;
// import org.saturnclient.saturnclient.auth.Auth; import org.saturnclient.saturnclient.auth.Auth;
import org.saturnclient.ui2.screens.EmoteWheel; import org.saturnclient.ui2.screens.EmoteWheel;
import org.saturnclient.ui2.screens.ShiftMenu; import org.saturnclient.ui2.screens.ShiftMenu;
@@ -39,7 +39,7 @@ public class KeyInputHandler {
client.setScreen(new ShiftMenu()); client.setScreen(new ShiftMenu());
} }
if (SaturnClientConfig.openEmoteWheel.wasKeyPressed()) { if (SaturnClientConfig.openEmoteWheel.wasKeyPressed() && client.currentScreen == null) {
client.setScreen(new EmoteWheel()); client.setScreen(new EmoteWheel());
} }
@@ -47,7 +47,7 @@ public class KeyInputHandler {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(SaturnClient.client.player); AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(SaturnClient.client.player);
if (animationStack.isActive() && animationStack.getPriority() == 1000) { if (animationStack.isActive() && animationStack.getPriority() == 1000) {
animationStack.removeLayer(1000); animationStack.removeLayer(1000);
// Auth.cancelEmote(); Auth.emote("");
} }
} }
}); });
@@ -1,62 +0,0 @@
package org.saturnclient.saturnclient.mixin;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.auth.Auth;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import dev.kosmx.playerAnim.api.layered.AnimationStack;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.network.message.MessageSignatureData;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
@Mixin(ChatHud.class)
public class SaturnReload {
@Inject(method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", at = @At("HEAD"), cancellable = true)
public void onAddMessage(Text message, @Nullable MessageSignatureData signatureData,
@Nullable MessageIndicator indicator, CallbackInfo ci) {
try {
String msg = message.toString();
if (msg.contains("$SATURN_RELOAD")) {
for (Map.Entry<String, String> player : Auth.playerNames.entrySet()) {
String name = player.getKey();
if (msg.contains(name)) {
Auth.player(name, player.getValue());
}
ci.cancel();
}
} else if (msg.contains("$SATURN_EMOTE")) {
String[] args = msg.split("&@");
for (AbstractClientPlayerEntity player : SaturnClient.client.world.getPlayers()) {
if (player.getUuidAsString().replace("-", "").equals(args[1])) {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(player);
animationStack.addAnimLayer(1000, PlayerAnimationRegistry.getAnimation(Identifier.of("saturnclient", args[2])).playAnimation());
ci.cancel();
break;
}
}
} else if (msg.contains("$SATURN_CANCEL_EMOTE")) {
String[] args = msg.split("&@");
for (AbstractClientPlayerEntity player : SaturnClient.client.world.getPlayers()) {
if (player.getUuidAsString().replace("-", "").equals(args[1])) {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(player);
animationStack.removeLayer(1000);
ci.cancel();
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -27,6 +27,8 @@ public class Toggle extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
theme.setState(null);
if (prop.value) { if (prop.value) {
theme.setState("enabled"); theme.setState("enabled");
} }
@@ -38,8 +40,6 @@ public class Toggle extends Element {
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value); renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.drawRoundedRectangle(prop.value ? 30 : 0, 0, 30, 30, 30, fgColor.value); renderScope.drawRoundedRectangle(prop.value ? 30 : 0, 0, 30, 30, 30, fgColor.value);
theme.setState(null);
} }
@Override @Override
@@ -2,6 +2,7 @@ package org.saturnclient.ui2.screens;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig; import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.auth.Auth;
// import org.saturnclient.saturnclient.auth.Auth; // import org.saturnclient.saturnclient.auth.Auth;
import org.saturnclient.saturnclient.cosmetics.Emotes; import org.saturnclient.saturnclient.cosmetics.Emotes;
import org.saturnclient.ui2.SaturnScreen; import org.saturnclient.ui2.SaturnScreen;
@@ -29,8 +30,10 @@ public class EmoteWheel extends SaturnScreen {
int row = 0; int row = 0;
int col = 0; int col = 0;
draw(new ImageTexture(Textures.LOGO_TEXT).dimensions(98, 10).centerOffset(width, height, 0, -105).animation(new Fade(700))); draw(new ImageTexture(Textures.LOGO_TEXT).dimensions(98, 10).centerOffset(width, height, 0, -105)
draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -149).animation(new SlideY(700, -20))); .animation(new Fade(700)));
draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -149)
.animation(new SlideY(700, -20)));
draw(new TextureButton(Textures.LEFT, () -> { draw(new TextureButton(Textures.LEFT, () -> {
if (page > 1) { if (page > 1) {
@@ -47,7 +50,7 @@ public class EmoteWheel extends SaturnScreen {
ui(); ui();
} }
}).dimensions(30, 30).centerOffset(width, height, 20, 45).animation(new Fade(700))); }).dimensions(30, 30).centerOffset(width, height, 20, 45).animation(new Fade(700)));
while (row < 3) { while (row < 3) {
int idx = (row * 3 + col) * page; int idx = (row * 3 + col) * page;
@@ -64,12 +67,14 @@ public class EmoteWheel extends SaturnScreen {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(SaturnClient.client.player); AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(SaturnClient.client.player);
if (animationStack.isActive() && animationStack.getPriority() == 1000) { if (animationStack.isActive() && animationStack.getPriority() == 1000) {
animationStack.removeLayer(1000); animationStack.removeLayer(1000);
// Auth.cancelEmote();
} }
animationStack.addAnimLayer(1000, PlayerAnimationRegistry.getAnimation(Identifier.of("saturnclient", emote)).playAnimation()); animationStack.addAnimLayer(1000,
// Auth.sendEmote(emote); PlayerAnimationRegistry.getAnimation(Identifier.of("saturnclient",
emote)).playAnimation());
Auth.emote(emote);
close(); close();
}).dimensions(70, 70).centerOffset(width, height, -80 + (col * 80), -35 + (row * 80)).animation(new Fade(700))); }).dimensions(70, 70).centerOffset(width, height, -80 + (col * 80), -35 + (row * 80))
.animation(new Fade(700)));
if (col < 2) { if (col < 2) {
col++; col++;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

+2 -1
View File
@@ -5,7 +5,8 @@
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "JAVA_21",
"client": [ "client": [
"CameraMixin", "CameraMixin",
"EntityMixin" "EntityMixin",
"RenderMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
@@ -8,7 +8,6 @@
"NameTagMixin", "NameTagMixin",
"PlayerEntityRendererMixin", "PlayerEntityRendererMixin",
"WhoAmI", "WhoAmI",
"SaturnReload",
"DrawContextAccessor", "DrawContextAccessor",
"TitleScreenMixin" "TitleScreenMixin"
], ],