From 6cedb984feb3b784ede5af7421020f4f82526c40 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 11 Aug 2020 22:10:21 -0400 Subject: [PATCH] 1.0.1 --- CHANGELOG.md | 3 + gradle.properties | 2 +- .../gestus/entity/FakePlayerEntity.java | 31 ++++- .../thebrokenrail/gestus/skin/SkinColor.java | 116 ++++++++++++++++++ .../thebrokenrail/gestus/skin/SkinJSON.java | 79 ++++++++++++ 5 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/thebrokenrail/gestus/skin/SkinColor.java create mode 100644 src/main/java/com/thebrokenrail/gestus/skin/SkinJSON.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 40f7511..a09aab6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ # Changelog +**1.0.1** +* Average Skin Color In Default Leather Armor + **1.0.0** * Initial Release \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index ecf51ca..41f4dbf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ org.gradle.jvmargs = -Xmx1G fabric_loader_version = 0.9.0+build.204 # Mod Properties - mod_version = 1.0.0 + mod_version = 1.0.1 maven_group = com.thebrokenrail # Dependencies diff --git a/src/main/java/com/thebrokenrail/gestus/entity/FakePlayerEntity.java b/src/main/java/com/thebrokenrail/gestus/entity/FakePlayerEntity.java index ec35ccc..f8e96af 100644 --- a/src/main/java/com/thebrokenrail/gestus/entity/FakePlayerEntity.java +++ b/src/main/java/com/thebrokenrail/gestus/entity/FakePlayerEntity.java @@ -4,6 +4,7 @@ import com.thebrokenrail.gestus.Gestus; import com.thebrokenrail.gestus.emote.EmoteLayer; import com.thebrokenrail.gestus.emote.EmotePart; import com.thebrokenrail.gestus.mixin.ArmorStandEntityAccessor; +import com.thebrokenrail.gestus.skin.SkinColor; import com.thebrokenrail.gestus.util.ServerPlayerEntityExtension; import net.minecraft.entity.EntityType; import net.minecraft.entity.EquipmentSlot; @@ -11,6 +12,8 @@ import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.decoration.ArmorStandEntity; import net.minecraft.item.CrossbowItem; +import net.minecraft.item.DyeableItem; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.nbt.CompoundTag; @@ -202,6 +205,25 @@ public class FakePlayerEntity extends ArmorStandEntity { } } + private SkinColor skinColor = null; + + private void updateSkinColor() { + if (skinColor == null) { + skinColor = SkinColor.get(player.getUuid()); + if (skinColor == null) { + skinColor = SkinColor.blank(); + } + } + } + + private ItemStack colorItem(Item item, int color) { + ItemStack stack = new ItemStack(item); + if (color != -1) { + ((DyeableItem) item).setColor(stack, color); + } + return stack; + } + private ItemStack getFallbackItem(EquipmentSlot slot, ItemStack stack, boolean invisible) { if (stack.isEmpty() && !invisible) { switch (slot) { @@ -210,13 +232,16 @@ public class FakePlayerEntity extends ArmorStandEntity { return head; } case CHEST: { - return new ItemStack(Items.LEATHER_CHESTPLATE); + updateSkinColor(); + return colorItem(Items.LEATHER_CHESTPLATE, skinColor.chest); } case LEGS: { - return new ItemStack(Items.LEATHER_LEGGINGS); + updateSkinColor(); + return colorItem(Items.LEATHER_LEGGINGS, skinColor.legs); } case FEET: { - return new ItemStack(Items.LEATHER_BOOTS); + updateSkinColor(); + return colorItem(Items.LEATHER_BOOTS, skinColor.boots); } default: { return ItemStack.EMPTY; diff --git a/src/main/java/com/thebrokenrail/gestus/skin/SkinColor.java b/src/main/java/com/thebrokenrail/gestus/skin/SkinColor.java new file mode 100644 index 0000000..2299e4c --- /dev/null +++ b/src/main/java/com/thebrokenrail/gestus/skin/SkinColor.java @@ -0,0 +1,116 @@ +package com.thebrokenrail.gestus.skin; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.UUID; + +public final class SkinColor { + public final int chest; + public final int legs; + public final int boots; + + private SkinColor(int chest, int legs, int boots) { + this.chest = chest; + this.legs = legs; + this.boots = boots; + } + + private static class Color { + private final int data; + + private Color(int data) { + this.data = data; + } + + private Color(int red, int green, int blue) { + data = 255 << 24 | (red & 255) << 16 | (green & 255) << 8 | (blue & 255); + } + + private Color(long red, long green, long blue) { + this((int) red, (int) green, (int) blue); + } + + public int getRed() { + return data >> 16 & 255; + } + + public int getGreen() { + return data >> 8 & 255; + } + + public int getBlue() { + return data & 255; + } + + public int getData() { + return (getRed() << 16) | (getGreen() << 8) | getBlue(); + } + } + + private static Color averageColor(BufferedImage image, int x0, int y0, int w, int h) { + int x1 = x0 + w; + int y1 = y0 + h; + + long sumRed = 0; + long sumGreen = 0; + long sumBlue = 0; + + int num = 0; + + for (int x = x0; x < x1; x++) { + for (int y = y0; y < y1; y++) { + int data = image.getRGB(x, y); + if ((data >> 24 & 255) == 255) { + Color pixel = new Color(data); + sumRed += pixel.getRed(); + sumGreen += pixel.getGreen(); + sumBlue += pixel.getBlue(); + num++; + } + } + } + + return new Color(sumRed / num, sumGreen / num, sumBlue / num); + } + + private static Color averageColor(Color color1, Color color2) { + int sumRed = color1.getRed() + color2.getRed(); + long sumGreen = color1.getGreen() + color2.getGreen(); + long sumBlue = color1.getBlue() + color2.getBlue(); + return new Color(sumRed / 2, sumGreen / 2, sumBlue / 2); + } + + public static SkinColor get(UUID uuid) { + String skin = SkinJSON.get(uuid); + if (skin != null) { + URL url; + try { + url = new URL(skin); + } catch (MalformedURLException e) { + return null; + } + + BufferedImage image; + try { + image = ImageIO.read(url); + } catch (IOException e) { + return null; + } + + int chest = averageColor(image, 16, 20, 23, 11).getData(); + int legs = averageColor(averageColor(image, 16, 52, 11, 11), averageColor(image, 0, 20, 11, 11)).getData(); + int boots = averageColor(averageColor(image, 20, 48, 7, 3), averageColor(image, 44, 16, 7, 3)).getData(); + + return new SkinColor(chest, legs, boots); + } else { + return null; + } + } + + public static SkinColor blank() { + return new SkinColor(-1, -1, -1); + } +} diff --git a/src/main/java/com/thebrokenrail/gestus/skin/SkinJSON.java b/src/main/java/com/thebrokenrail/gestus/skin/SkinJSON.java new file mode 100644 index 0000000..d52a0fc --- /dev/null +++ b/src/main/java/com/thebrokenrail/gestus/skin/SkinJSON.java @@ -0,0 +1,79 @@ +package com.thebrokenrail.gestus.skin; + +import com.google.gson.Gson; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.Base64; +import java.util.Map; +import java.util.UUID; + +final class SkinJSON { + private static class Response { + public Property[] properties = null; + } + + private static class Property { + public String name = null; + public String value = null; + } + + private static class Data { + public Map textures; + } + + private static class Texture { + public String url = null; + } + + private static String urlToString(String urlStr) { + try { + StringBuilder stringBuilder = new StringBuilder(); + URL url = new URL(urlStr); + + try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream()))) { + String line; + while ((line = in.readLine()) != null) { + stringBuilder.append(line); + stringBuilder.append('\n'); + } + } + + return stringBuilder.toString(); + } catch (IOException e) { + return null; + } + } + + private static final String SKIN_TEXTURE_KEY = "SKIN"; + + static String get(UUID uuid) { + String url = String.format("https://sessionserver.mojang.com/session/minecraft/profile/%s", uuid.toString().replace("-", "")); + String data = urlToString(url); + + if (data != null) { + Gson gson = new Gson(); + Response response = gson.fromJson(data, Response.class); + + if (response != null && response.properties != null) { + for (Property property : response.properties) { + if ("textures".equals(property.name)) { + String texturesJSON = new String(Base64.getDecoder().decode(property.value)); + Data textures = gson.fromJson(texturesJSON, Data.class); + + if (textures.textures.containsKey(SKIN_TEXTURE_KEY)) { + Texture skin = textures.textures.get(SKIN_TEXTURE_KEY); + return skin.url; + } + + break; + } + } + } + } + + return null; + } +}