diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomSkullsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomSkullsEvent.java index e6e48d9d4..e568f5c31 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomSkullsEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomSkullsEvent.java @@ -16,12 +16,12 @@ public abstract class GeyserDefineCustomSkullsEvent implements Event { USERNAME, UUID, PROFILE, - SKIN_URL + SKIN_HASH } /** - * Registers the given username, UUID, or base64 encoded profile as a custom skull blocks - * @param texture the username, UUID, or base64 encoded profile + * Registers the given username, UUID, base64 encoded profile, or skin hash as a custom skull blocks + * @param texture the username, UUID, base64 encoded profile, or skin hash * @param type the type of texture provided */ public abstract void registerCustomSkull(@NonNull String texture, @NonNull SkullTextureType type); diff --git a/core/src/main/java/org/geysermc/geyser/Constants.java b/core/src/main/java/org/geysermc/geyser/Constants.java index 589bb66a7..588a8ad51 100644 --- a/core/src/main/java/org/geysermc/geyser/Constants.java +++ b/core/src/main/java/org/geysermc/geyser/Constants.java @@ -43,6 +43,8 @@ public final class Constants { public static final String GEYSER_NAMESPACE = "geyser_custom"; + public static final String MINECRAFT_SKIN_SERVER_URL = "http://textures.minecraft.net/texture/"; + static { URI wsUri = null; try { diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java index 6199dfcb8..1af3578a3 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java @@ -44,8 +44,8 @@ public class GeyserCustomSkullConfiguration { @JsonProperty("player-profiles") private List playerProfiles; - @JsonProperty("skin-urls") - private List skinUrls; + @JsonProperty("skin-hashes") + private List skinHashes; public List getPlayerUsernames() { return Objects.requireNonNullElse(playerUsernames, Collections.emptyList()); @@ -59,7 +59,7 @@ public class GeyserCustomSkullConfiguration { return Objects.requireNonNullElse(playerProfiles, Collections.emptyList()); } - public List getPlayerSkinUrls() { - return Objects.requireNonNullElse(skinUrls, Collections.emptyList()); + public List getPlayerSkinHashes() { + return Objects.requireNonNullElse(skinHashes, Collections.emptyList()); } } diff --git a/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java b/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java index 4c938ee2b..821251454 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java +++ b/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.pack; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.CustomSkull; @@ -99,7 +100,8 @@ public class SkullResourcePackManager { return null; } - public static void cacheSkullSkin(String skinUrl, String skinHash) throws IOException { + public static void cacheSkullSkin(String skinHash) throws IOException { + String skinUrl = Constants.MINECRAFT_SKIN_SERVER_URL + skinHash; Path skinPath = SKULL_SKINS.get(skinHash); if (skinPath != null) { return; diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java index 89bbb46a3..6210f1081 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java @@ -72,12 +72,9 @@ public class CustomSkullRegistryPopulator { BlockRegistries.CUSTOM_SKULLS.set(new Object2ObjectOpenHashMap<>()); List profiles = new ArrayList<>(skullConfig.getPlayerProfiles()); - List usernames = new ArrayList<>(skullConfig.getPlayerUsernames()); - List uuids = new ArrayList<>(skullConfig.getPlayerUUIDs()); - - List skinUrls = new ArrayList<>(skullConfig.getPlayerSkinUrls()); + List skinHashes = new ArrayList<>(skullConfig.getPlayerSkinHashes()); GeyserImpl.getInstance().getEventBus().fire(new GeyserDefineCustomSkullsEvent() { @Override @@ -86,63 +83,69 @@ public class CustomSkullRegistryPopulator { case USERNAME -> usernames.add(texture); case UUID -> uuids.add(texture); case PROFILE -> profiles.add(texture); - case SKIN_URL -> skinUrls.add(texture); + case SKIN_HASH -> skinHashes.add(texture); } } }); - for (String username : usernames) { + usernames.forEach((username) -> { String profile = getProfileFromUsername(username); if (profile != null) { - String skinUrl = getSkinUrl(profile); - if (skinUrl != null) { - skinUrls.add(skinUrl); + String skinHash = getSkinHash(profile); + if (skinHash != null) { + skinHashes.add(skinHash); } } - } - for (String uuid : uuids) { + }); + + uuids.forEach((uuid) -> { String profile = getProfileFromUuid(uuid); if (profile != null) { - String skinUrl = getSkinUrl(profile); - if (skinUrl != null) { - skinUrls.add(skinUrl); + String skinHash = getSkinHash(profile); + if (skinHash != null) { + skinHashes.add(skinHash); } } - } + }); - for (String profile : profiles) { - String skinUrl = getSkinUrl(profile); - if (skinUrl != null) { - skinUrls.add(skinUrl); + profiles.forEach((profile) -> { + String skinHash = getSkinHash(profile); + if (skinHash != null) { + skinHashes.add(skinHash); + } + }); + + skinHashes.forEach((skinHash) -> { + if (!skinHash.matches("^[a-fA-F0-9]{64}$")) { + GeyserImpl.getInstance().getLogger().error("Skin hash " + skinHash + " does not match required format ^[a-fA-F0-9]{64}$ and will not be added as a custom block."); + return; } - } - for (String skinUrl : skinUrls) { try { - String skinHash = skinUrl.substring(skinUrl.lastIndexOf("/") + 1); - SkullResourcePackManager.cacheSkullSkin(skinUrl, skinHash); + SkullResourcePackManager.cacheSkullSkin(skinHash); BlockRegistries.CUSTOM_SKULLS.register(skinHash, new CustomSkull(skinHash)); } catch (IOException e) { - GeyserImpl.getInstance().getLogger().error("Failed to cache skin for skull texture " + skinUrl + " This skull will not be added as a custom block.", e); + GeyserImpl.getInstance().getLogger().error("Failed to cache skin for skull texture " + skinHash + " This skull will not be added as a custom block.", e); } - } + }); GeyserImpl.getInstance().getLogger().info("Registered " + BlockRegistries.CUSTOM_SKULLS.get().size() + " custom skulls as custom blocks."); } /** - * Gets the skin URL from a base64 encoded profile + * Gets the skin hash from a base64 encoded profile * @param profile the base64 encoded profile - * @return the skin URL or null if the profile is invalid + * @return the skin hash or null if the profile is invalid */ - private static String getSkinUrl(String profile) { + private static String getSkinHash(String profile) { try { SkinManager.GameProfileData profileData = SkinManager.GameProfileData.loadFromJson(profile); if (profileData == null) { GeyserImpl.getInstance().getLogger().warning("Skull texture " + profile + " contained no skins and will not be added as a custom block."); return null; } - return profileData.skinUrl(); + String skinUrl = profileData.skinUrl(); + return skinUrl.substring(skinUrl.lastIndexOf("/") + 1); } catch (IOException e) { GeyserImpl.getInstance().getLogger().error("Skull texture " + profile + " is invalid and will not be added as a custom block.", e); return null; diff --git a/core/src/main/resources/custom-skulls.yml b/core/src/main/resources/custom-skulls.yml index 4f5e2ce1b..22d08d263 100644 --- a/core/src/main/resources/custom-skulls.yml +++ b/core/src/main/resources/custom-skulls.yml @@ -24,6 +24,6 @@ player-uuids: player-profiles: # - ewogICJ0aW1lc3RhbXAiIDogMTY1NzMyMjIzOTgzMywKICAicHJvZmlsZUlkIiA6ICJjZGRiZTUyMGQwNDM0YThiYTFjYzlmYzkyZmRlMmJjZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbWJlcmljaHUiLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTkwNzkwYzU3ZTE4MWVkMTNhZGVkMTRjNDdlZTJmN2M4ZGUzNTMzZTAxN2JhOTU3YWY3YmRmOWRmMWJkZTk0ZiIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9CiAgfQp9 -# The URL of the skin on Minecraft's skin server -skin-urls: -# - http://textures.minecraft.net/texture/a90790c57e181ed13aded14c47ee2f7c8de3533e017ba957af7bdf9df1bde94f \ No newline at end of file +# The hash of the skin on Minecraft's skin server (http://textures.minecraft.net/texture/HASH) +skin-hashes: +# - a90790c57e181ed13aded14c47ee2f7c8de3533e017ba957af7bdf9df1bde94f \ No newline at end of file