working ish

This commit is contained in:
2025-02-28 18:15:44 +01:00
parent 33b96b9a1a
commit 9928854f88
9 changed files with 174 additions and 853 deletions
@@ -3,6 +3,7 @@ package org.auraclient.auraclient;
import net.fabricmc.api.ModInitializer;
import org.auraclient.auraclient.cloaks.Cloaks;
import org.auraclient.auraclient.event.KeyInputHandler;
import org.auraclient.auraclient.auth.AuraApi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,8 +18,14 @@ public class AuraClient implements ModInitializer {
@Override
public void onInitialize() {
LOGGER.info("Initializing " + MOD_ID);
KeyInputHandler.register();
Cloaks.initialize();
LOGGER.info(MOD_ID + " initialization complete");
// Attempt authentication before initializing other features
if (AuraApi.authenticate()) {
KeyInputHandler.register();
Cloaks.initialize();
LOGGER.info(MOD_ID + " initialization complete");
} else {
LOGGER.error("Failed to authenticate with the server");
}
}
}
@@ -0,0 +1,118 @@
package org.auraclient.auraclient.auth;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.minecraft.client.MinecraftClient;
import org.auraclient.auraclient.AuraClient;
import org.auraclient.auraclient.cloaks.Cloaks;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class AuraApi {
private static final String URL = "http://localhost:3000";
private static String token = null;
private static String uuid = null;
public static boolean authenticate() {
try {
MinecraftClient client = MinecraftClient.getInstance();
if (client.getSession() == null) {
AuraClient.LOGGER.error("No active Minecraft session found");
return false;
}
String accessToken = client.getSession().getAccessToken();
token = URLEncoder.encode(accessToken, StandardCharsets.UTF_8);
String authUrl = String.format("%s/auth?token=%s", URL, token);
AuraClient.LOGGER.info("Authenticating with URL: " + authUrl);
URL url = URI.create(authUrl).toURL();
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
JsonObject jsonResponse = JsonParser.parseString(response.toString()).getAsJsonObject();
boolean success = jsonResponse.get("success").getAsBoolean();
uuid = jsonResponse.get("uuid").getAsString();
if (success) {
AuraClient.LOGGER.info("Successfully authenticated");
}
// Update available cloaks list
Cloaks.availableCloaks.clear();
jsonResponse.get("cloaks").getAsJsonArray()
.forEach(element -> Cloaks.availableCloaks.add(element.getAsString()));
String cloak = jsonResponse.get("cloak").getAsString();
if (cloak != null) {
AuraClient.LOGGER.info("Setting cloak to " + cloak + " for " + uuid);
Cloaks.playerCapes.put(uuid, cloak);
}
return success;
} catch (Exception e) {
AuraClient.LOGGER.error("Authentication failed", e);
return false;
}
}
public static void setCloak(String cloak) {
if (token == null) {
AuraClient.LOGGER.error("No token found");
return;
}
try {
String setCloakUrl = String.format("%s/cloak/%s?token=%s", URL, cloak, token);
URL url = URI.create(setCloakUrl).toURL();
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
JsonObject jsonResponse = JsonParser.parseString(response.toString()).getAsJsonObject();
boolean success = jsonResponse.get("success").getAsBoolean();
if (success) {
AuraClient.LOGGER.info("Successfully set cloak");
} else {
AuraClient.LOGGER.error("Failed to set cloak");
AuraClient.LOGGER.error(jsonResponse.get("error").getAsString());
}
} catch (Exception e) {
AuraClient.LOGGER.error("Failed to set cloak", e);
}
}
}
@@ -1,11 +1,9 @@
package org.auraclient.auraclient.cloaks;
import at.dhyan.open_imaging.GifDecoder;
import net.minecraft.util.Identifier;
import org.auraclient.auraclient.AuraClient;
import org.auraclient.auraclient.cloaks.utils.AnimatedCapeData;
import org.auraclient.auraclient.auth.AuraApi;
import org.auraclient.auraclient.cloaks.utils.IdentifierUtils;
import org.auraclient.auraclient.cloaks.utils.RandomUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@@ -15,140 +13,59 @@ import java.util.*;
/**
* Manages the cloak/cape system for Aura Client.
* Handles loading, caching, and animating player capes.
* Handles loading and caching of player capes.
*/
public class Cloaks {
private static final String CAPES_RESOURCE_PATH = "assets/auraclient/textures/capes/";
private static final List<AnimatedCapeData> animatedCapeFrames = new ArrayList<>();
private static int currentFrameIndex = 0;
private static long lastFrameTime = System.currentTimeMillis();
private static String lastCachedCape = "NoCache";
public static final List<String> availableCapes = new ArrayList<>();
public static final List<String> availableCloaks = new ArrayList<>();
public static Identifier capeCacheIdentifier = null;
public static final Map<String, String> playerCapes = new HashMap<>();
/**
* Initializes the cloak system.
* Loads cape textures from resources.
*/
public static void initialize() {
availableCapes.clear();
availableCapes.add("");
loadCloakTextures();
}
/**
* Loads cloak textures from the resources.
* Supports both static (.png) and animated (.gif) capes.
*/
private static void loadCloakTextures() {
// In a resource-based system, we would need to manually list available capes
// For now, we can add default capes that we know exist in resources
// This would need to be updated when adding new capes to resources
availableCapes.add("glitch.png");
}
/**
* Updates the cape animation state.
* Called every tick to handle animated capes.
*/
public static void tick() {
String currentCape = playerCapes.values().stream().findFirst().orElse("");
if (!lastCachedCape.equals(currentCape)) {
handleCapeChange(currentCape);
} else if (currentCape.endsWith(".gif")) {
updateAnimatedCape();
}
availableCloaks.add(0, "");
}
/**
* Handles loading and caching of a new cape texture.
*
* @param capeName Name of the cape file to load
*/
private static void handleCapeChange(String capeName) {
if (!capeName.isEmpty()) {
if (capeName.endsWith(".gif")) {
loadAnimatedCape(capeName);
} else {
loadStaticCape(capeName);
}
lastCachedCape = capeName;
}
}
public static void setCape(String uuid, String capeName) {
AuraApi.setCloak(capeName);
Cloaks.playerCapes.put(uuid, capeName);
/**
* Loads and processes an animated cape from a GIF file in resources.
* @param fileName Name of the GIF file to load
*/
private static void loadAnimatedCape(String fileName) {
try {
animatedCapeFrames.clear();
String resourcePath = CAPES_RESOURCE_PATH + fileName;
InputStream inputStream = Cloaks.class.getClassLoader().getResourceAsStream(resourcePath);
if (inputStream != null) {
GifDecoder.GifImage gif = GifDecoder.read(inputStream);
int frameCount = gif.getFrameCount();
for (int i = 0; i < frameCount; i++) {
BufferedImage frame = gif.getFrame(i);
int delay = gif.getDelay(i);
Identifier frameId = Identifier.of(AuraClient.MOD_ID,
"capes_" + RandomUtils.randomStringLowercase(10));
IdentifierUtils.registerBufferedImageTexture(frameId, frame);
animatedCapeFrames.add(new AnimatedCapeData(frameId, delay));
}
currentFrameIndex = 0;
capeCacheIdentifier = animatedCapeFrames.get(0).getTextureId();
}
} catch (IOException e) {
AuraClient.LOGGER.error("Failed to load animated cape from resources: " + fileName, e);
if (!capeName.isEmpty()) {
loadStaticCape(capeName);
}
}
/**
* Loads and processes a static cape from a PNG file in resources.
*
* @param fileName Name of the PNG file to load
*/
private static void loadStaticCape(String fileName) {
try {
String resourcePath = CAPES_RESOURCE_PATH + fileName;
InputStream inputStream = Cloaks.class.getClassLoader().getResourceAsStream(resourcePath);
if (inputStream != null) {
BufferedImage image = ImageIO.read(inputStream);
String safeFileName = fileName.toLowerCase(Locale.ROOT)
.replace(' ', '_')
.replaceAll("[^a-z0-9/._-]", "");
.replace(' ', '_')
.replaceAll("[^a-z0-9/._-]", "");
capeCacheIdentifier = Identifier.of(AuraClient.MOD_ID, "capes_" + safeFileName);
IdentifierUtils.registerBufferedImageTexture(capeCacheIdentifier, image);
}
} catch (IOException e) {
AuraClient.LOGGER.error("Failed to load static cape from resources: " + fileName, e);
}
}
/**
* Updates the current frame of an animated cape.
*/
private static void updateAnimatedCape() {
if (currentFrameIndex >= animatedCapeFrames.size()) {
currentFrameIndex = 0;
}
AnimatedCapeData currentFrame = animatedCapeFrames.get(currentFrameIndex);
if (System.currentTimeMillis() > lastFrameTime + currentFrame.getDelayMs()) {
lastFrameTime = System.currentTimeMillis();
capeCacheIdentifier = currentFrame.getTextureId();
currentFrameIndex++;
}
}
}
@@ -1,43 +1,26 @@
package org.auraclient.auraclient.menus;
import org.auraclient.auraclient.AuraClient;
import org.auraclient.auraclient.cloaks.Cloaks;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text;
public class CloakSelector extends Screen {
private TextFieldWidget playerNameField;
public CloakSelector() {
super(Text.literal("Cloaks"));
}
@Override
protected void init() {
// Add player name text field at the top
this.playerNameField = new TextFieldWidget(this.textRenderer,
(this.width - 200) / 2, 20, 200, 20, Text.literal("Player Name"));
this.playerNameField.setMaxLength(16);
addDrawableChild(this.playerNameField);
int y = ((this.height - (35 * (Cloaks.availableCloaks.size()))) / 2) + 30;
// Calculate starting Y position for cloak buttons, accounting for text field
int y = ((this.height - (35 * (Cloaks.availableCapes.size()))) / 2) + 30;
for (String cloak : Cloaks.availableCapes) {
for (String cloak : Cloaks.availableCloaks) {
addDrawableChild(ButtonWidget.builder(Text.literal(cloak), (__) -> {
String playerName = this.playerNameField.getText();
if (!playerName.isEmpty() && !playerName.equals("Player Name")) {
if (cloak.isEmpty()) {
Cloaks.playerCapes.remove(playerName);
AuraClient.LOGGER.info("Cape removed for player: " + playerName);
} else {
Cloaks.playerCapes.put(playerName, cloak);
AuraClient.LOGGER.info("Cape set for player: " + playerName + " to " + cloak);
}
}
String uuid = client.player.getUuidAsString().replace("-", "");
Cloaks.setCape(uuid, cloak);
// Cloaks.playerCapes.put(uuid, cloak);
// AuraApi.setCloak(cloak);
}).dimensions((this.width - 50) / 2, y, 50, 30).build());
y += 35;
@@ -6,7 +6,7 @@ import net.minecraft.text.Text;
public class MainMenu extends Screen {
public MainMenu() {
super(Text.literal("Custom Screen"));
super(Text.literal("Aura Client"));
}
@Override
@@ -4,6 +4,7 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.util.SkinTextures;
import net.minecraft.util.Identifier;
import org.auraclient.auraclient.cloaks.Cloaks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -17,31 +18,31 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(AbstractClientPlayerEntity.class)
@Environment(EnvType.CLIENT)
public class AbstractClientPlayerEntityMixin {
/**
* Injects custom cape textures into the player's skin textures.
*
* @param cir Callback containing the original skin textures
*/
@Inject(
method = "getSkinTextures",
at = @At("RETURN"),
cancellable = true
)
@Inject(method = "getSkinTextures", at = @At("RETURN"), cancellable = true)
private void getSkinTextures(CallbackInfoReturnable<SkinTextures> cir) {
AbstractClientPlayerEntity player = (AbstractClientPlayerEntity) (Object) this;
if (Cloaks.playerCapes.containsKey(player.getName().getString()) &&
Cloaks.capeCacheIdentifier != null) {
SkinTextures textures = cir.getReturnValue();
cir.setReturnValue(new SkinTextures(
textures.texture(),
textures.textureUrl(),
Cloaks.capeCacheIdentifier,
textures.elytraTexture(),
textures.model(),
textures.secure()
));
String uuid = player.getUuidAsString().replace("-", "");
if (Cloaks.playerCapes.containsKey(uuid)) {
String cloak = Cloaks.playerCapes.get(uuid);
if (!cloak.isEmpty()) {
SkinTextures textures = cir.getReturnValue();
cir.setReturnValue(new SkinTextures(
textures.texture(),
textures.textureUrl(),
Identifier.of("auraclient", "textures/capes/" + cloak + ".png"),
textures.elytraTexture(),
textures.model(),
textures.secure()));
}
}
}
}
@@ -1,31 +0,0 @@
package org.auraclient.auraclient.mixin;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import org.auraclient.auraclient.cloaks.Cloaks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Mixin for the MinecraftClient class.
* Handles the periodic updating of cloaks system.
*/
@Mixin(MinecraftClient.class)
@Environment(EnvType.CLIENT)
public class MinecraftClientMixin {
/**
* Injects into the client tick to update cloaks.
* @param ci Callback info
*/
@Inject(
method = "tick",
at = @At("HEAD")
)
private void tick(CallbackInfo ci) {
Cloaks.tick();
}
}