From de96684315a10bd224069c253bbefa408659dc3c Mon Sep 17 00:00:00 2001
From: Aaron <51387595+AzureAaron@users.noreply.github.com>
Date: Wed, 10 Jan 2024 17:24:53 -0500
Subject: Fix Skin Transparency applying to player skins (#477)

---
 .../java/de/hysky/skyblocker/SkyblockerMod.java    |  1 +
 .../skyblocker/mixin/PlayerSkinTextureMixin.java   | 28 ++++++++--
 .../skyblock/item/PlayerHeadHashCache.java         | 64 ++++++++++++++++++++++
 3 files changed, 89 insertions(+), 4 deletions(-)
 create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/PlayerHeadHashCache.java

(limited to 'src/main')

diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index f2359265..6e702e7b 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -84,6 +84,7 @@ public class SkyblockerMod implements ClientModInitializer {
         Tips.init();
         NEURepoManager.init();
         ItemRepository.init();
+        PlayerHeadHashCache.init();
         HotbarSlotLock.init();
         ItemTooltip.init();
         WikiLookup.init();
diff --git a/src/main/java/de/hysky/skyblocker/mixin/PlayerSkinTextureMixin.java b/src/main/java/de/hysky/skyblocker/mixin/PlayerSkinTextureMixin.java
index ea771c1e..1065bab0 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/PlayerSkinTextureMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/PlayerSkinTextureMixin.java
@@ -1,19 +1,39 @@
 package de.hysky.skyblocker.mixin;
 
+import org.spongepowered.asm.mixin.Final;
 import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
 import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import com.llamalad7.mixinextras.injector.WrapWithCondition;
 
 import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.item.PlayerHeadHashCache;
 import de.hysky.skyblocker.utils.Utils;
 import dev.cbyrne.betterinject.annotations.Inject;
+import net.minecraft.client.texture.NativeImage;
 import net.minecraft.client.texture.PlayerSkinTexture;
 
 @Mixin(PlayerSkinTexture.class)
 public class PlayerSkinTextureMixin {
+	@Shadow
+	@Final
+	private String url;
+
+	@Unique
+	private boolean isSkyblockSkinTexture;
+
+	@Inject(method = "remapTexture", at = @At("HEAD"))
+	private void skyblocker$determineSkinSource() {
+		if (Utils.isOnSkyblock()) {
+			int skinHash = PlayerHeadHashCache.getSkinHash(this.url).hashCode();
+			this.isSkyblockSkinTexture = PlayerHeadHashCache.contains(skinHash);
+		}
+	}
 
-	@Inject(method = "stripAlpha", at = @At("HEAD"), cancellable = true)
-	private static void skyblocker$dontStripAlphaValues(CallbackInfo ci) {
-		if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.dontStripSkinAlphaValues) ci.cancel();
+	@WrapWithCondition(method = "remapTexture", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/texture/PlayerSkinTexture;stripAlpha(Lnet/minecraft/client/texture/NativeImage;IIII)V"))
+	private boolean skyblocker$dontStripAlphaValues(NativeImage image, int x1, int y1, int x2, int y2) {
+		return !(SkyblockerConfigManager.get().general.dontStripSkinAlphaValues && this.isSkyblockSkinTexture);
 	}
 }
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/PlayerHeadHashCache.java b/src/main/java/de/hysky/skyblocker/skyblock/item/PlayerHeadHashCache.java
new file mode 100644
index 00000000..2a1688fc
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/PlayerHeadHashCache.java
@@ -0,0 +1,64 @@
+package de.hysky.skyblocker.skyblock.item;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Base64;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.commons.io.FilenameUtils;
+import org.slf4j.Logger;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.mojang.logging.LogUtils;
+
+import de.hysky.skyblocker.utils.Http;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+
+public class PlayerHeadHashCache {
+	private static final Logger LOGGER = LogUtils.getLogger();
+	private static final IntOpenHashSet CACHE = new IntOpenHashSet();
+
+	public static void init() {
+		CompletableFuture.runAsync(PlayerHeadHashCache::loadSkins);
+	}
+
+	private static void loadSkins() {
+		try {
+			String response = Http.sendGetRequest("https://api.hypixel.net/v2/resources/skyblock/items");
+			JsonArray items = JsonParser.parseString(response).getAsJsonObject().getAsJsonArray("items");
+
+			items.asList().stream()
+			.map(JsonElement::getAsJsonObject)
+			.filter(item -> item.get("material").getAsString().equals("SKULL_ITEM"))
+			.filter(item -> item.has("skin"))
+			.map(item -> Base64.getDecoder().decode(item.get("skin").getAsString()))
+			.map(String::new)
+			.map(profile -> JsonParser.parseString(profile).getAsJsonObject())
+			.map(profile -> profile.getAsJsonObject("textures").getAsJsonObject("SKIN").get("url").getAsString())
+			.map(PlayerHeadHashCache::getSkinHash)
+			.mapToInt(String::hashCode)
+			.forEach(CACHE::add);
+
+			LOGGER.info("[Skyblocker Player Head Hash Cache] Successfully cached the hashes of all player head items!");
+		} catch (Exception e) {
+			LOGGER.error("[Skyblocker Player Head Hash Cache] Failed to cache skin hashes!", e);
+		}
+	}
+
+	//From MinecraftProfileTexture#getHash
+	public static String getSkinHash(String url) {
+		try {
+			return FilenameUtils.getBaseName(new URL(url).getPath());
+		} catch (MalformedURLException e) {
+			LOGGER.error("[Skyblocker Player Head Hash Cache] Malformed Skin URL! URL: {}", url, e);
+		}
+
+		return "";
+	}
+
+	public static boolean contains(int hash) {
+		return CACHE.contains(hash);
+	}
+}
-- 
cgit