From 9e0af6e098d00636d37511ab36552bcf3d2b065a Mon Sep 17 00:00:00 2001 From: davchoo Date: Fri, 8 Jul 2022 14:49:19 -0400 Subject: [PATCH] Auto generate skull resource pack Change `davchoo` to `geyser` in geometry --- .../geysermc/geyser/pack/ResourcePack.java | 21 +- .../geyser/pack/ResourcePackManifest.java | 2 +- .../geyser/pack/SkullResourcePackManager.java | 208 ++++++++++++++++++ .../geyser/registry/type/CustomSkull.java | 15 +- .../geyser/session/cache/SkullCache.java | 3 +- .../geysermc/geyser/skin/SkinProvider.java | 11 +- .../animations/disable.animation.json | 14 ++ .../animations/player_skull.animation.json | 80 +++++++ .../attachables/template_attachable.json | 57 +++++ .../bedrock/skull_resource_pack/manifest.json | 17 ++ .../models/blocks/player_skull.geo.json | 50 +++++ .../blocks/player_skull_floor_a.geo.json | 29 +++ .../blocks/player_skull_floor_b.geo.json | 30 +++ .../blocks/player_skull_floor_c.geo.json | 30 +++ .../blocks/player_skull_floor_d.geo.json | 30 +++ .../models/blocks/player_skull_hand.geo.json | 29 +++ .../models/blocks/player_skull_wall.geo.json | 29 +++ .../bedrock/skull_resource_pack/pack_icon.png | Bin 0 -> 36264 bytes .../textures/terrain_texture.json | 8 + .../bedrock/skull_resource_pack_files.txt | 12 + 20 files changed, 651 insertions(+), 24 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/animations/disable.animation.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/animations/player_skull.animation.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/attachables/template_attachable.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/manifest.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull.geo.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_a.geo.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_b.geo.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_c.geo.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_d.geo.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_hand.geo.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_wall.geo.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/pack_icon.png create mode 100644 core/src/main/resources/bedrock/skull_resource_pack/textures/terrain_texture.json create mode 100644 core/src/main/resources/bedrock/skull_resource_pack_files.txt diff --git a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java index bef5c7418..55a95b53c 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java +++ b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.pack; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent; +import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.text.GeyserLocale; @@ -77,13 +78,18 @@ public class ResourcePack { } List resourcePacks; - try { - resourcePacks = Files.walk(directory).collect(Collectors.toList()); + try (Stream stream = Files.walk(directory)) { + resourcePacks = stream.collect(Collectors.toList()); } catch (IOException e) { GeyserImpl.getInstance().getLogger().error("Could not list packs directory", e); return; } + Path skullResourcePack = SkullResourcePackManager.createResourcePack(BlockRegistries.CUSTOM_SKULLS.get().keySet()); + if (skullResourcePack != null) { + resourcePacks.add(skullResourcePack); + } + GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks); GeyserImpl.getInstance().eventBus().fire(event); @@ -95,11 +101,8 @@ public class ResourcePack { pack.sha256 = FileUtils.calculateSHA256(file); - Stream stream = null; - try { - ZipFile zip = new ZipFile(file); - - stream = zip.stream(); + try (ZipFile zip = new ZipFile(file); + Stream stream = zip.stream()) { stream.forEach((x) -> { if (x.getName().contains("manifest.json")) { try { @@ -121,10 +124,6 @@ public class ResourcePack { } catch (Exception e) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", file.getName())); e.printStackTrace(); - } finally { - if (stream != null) { - stream.close(); - } } } } diff --git a/core/src/main/java/org/geysermc/geyser/pack/ResourcePackManifest.java b/core/src/main/java/org/geysermc/geyser/pack/ResourcePackManifest.java index 2b14eade3..0c3abf86d 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/ResourcePackManifest.java +++ b/core/src/main/java/org/geysermc/geyser/pack/ResourcePackManifest.java @@ -71,7 +71,7 @@ public class ResourcePackManifest { @ToString public static class Module { private String description; - private String name; + private String type; private UUID uuid; private int[] version; } diff --git a/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java b/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java new file mode 100644 index 000000000..819811c9b --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.pack; + +import it.unimi.dsi.fastutil.Pair; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.skin.SkinProvider; +import org.geysermc.geyser.util.FileUtils; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.function.UnaryOperator; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +public class SkullResourcePackManager { + + private static final long RESOURCE_PACK_VERSION = 4; + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static Path createResourcePack(Set skins) { + Path packPath = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("player_skulls.mcpack"); + File packFile = packPath.toFile(); + if (skins.isEmpty()) { + packFile.delete(); // No need to keep resource pack + GeyserImpl.getInstance().getLogger().debug("No skins to create player skull resource pack."); + return null; + } + if (packFile.exists() && canReusePack(skins, packFile)) { + GeyserImpl.getInstance().getLogger().info("Reusing cached player skull resource pack."); + return packPath; + } + + // We need to create the resource pack from scratch + GeyserImpl.getInstance().getLogger().info("Creating skull resource pack."); + packFile.delete(); + try (ZipOutputStream zipOS = new ZipOutputStream(Files.newOutputStream(packPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE))) { + addBaseResources(zipOS, skins); + addSkinTextures(zipOS, skins); + addAttachables(zipOS, skins); + GeyserImpl.getInstance().getLogger().info("Finished creating skull resource pack."); + return packPath; + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().error("Unable to create player skull resource pack!", e); + packFile.delete(); + } + return null; + } + + private static void addBaseResources(ZipOutputStream zipOS, Set skins) throws IOException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(GeyserImpl.getInstance().getBootstrap().getResource("bedrock/skull_resource_pack_files.txt")))) { + List lines = reader.lines().toList(); + for (String path : lines) { + ZipEntry entry = new ZipEntry(path); + + zipOS.putNextEntry(entry); + String resourcePath = "bedrock/" + path; + switch (path) { + case "skull_resource_pack/manifest.json" -> + fillTemplate(zipOS, resourcePath, template -> fillManifestJson(template, skins)); + case "skull_resource_pack/textures/terrain_texture.json" -> + fillTemplate(zipOS, resourcePath, template -> fillTerrainTextureJson(template, skins)); + default -> zipOS.write(FileUtils.readAllBytes(resourcePath)); + } + zipOS.closeEntry(); + } + } + } + + private static void addAttachables(ZipOutputStream zipOS, Set skins) throws IOException { + String template = new String(FileUtils.readAllBytes("bedrock/skull_resource_pack/attachables/template_attachable.json"), StandardCharsets.UTF_8); + for (String skinHash : skins) { + ZipEntry entry = new ZipEntry("skull_resource_pack/attachables/" + skinHash + ".json"); + zipOS.putNextEntry(entry); + zipOS.write(fillAttachableJson(template, skinHash).getBytes(StandardCharsets.UTF_8)); + zipOS.closeEntry(); + } + } + + private static void addSkinTextures(ZipOutputStream zipOS, Set skins) throws IOException { + Path skullSkinCache = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("player_skulls"); + Files.createDirectories(skullSkinCache); + for (String skin : skins) { + Path skinPath = skullSkinCache.resolve(skin + ".png"); + if (!Files.exists(skinPath)) { + // TODO this should go somewhere else and be async somehow + BufferedImage image = SkinProvider.requestImage("http://textures.minecraft.net/texture/" + skin, null); + ImageIO.write(image, "png", skinPath.toFile()); + GeyserImpl.getInstance().getLogger().debug("Cached player skull to file " + skinPath + " for " + skin); + } + + ZipEntry entry = new ZipEntry("skull_resource_pack/textures/blocks/" + skin + ".png"); + + zipOS.putNextEntry(entry); + zipOS.write(FileUtils.readAllBytes(skinPath.toFile())); + zipOS.closeEntry(); + } + } + + private static void fillTemplate(ZipOutputStream zipOS, String path, UnaryOperator filler) throws IOException { + String template = new String(FileUtils.readAllBytes(path), StandardCharsets.UTF_8); + String result = filler.apply(template); + zipOS.write(result.getBytes(StandardCharsets.UTF_8)); + } + + private static String fillAttachableJson(String template, String skinHash) { + return template.replace("${identifier}", "geyser:player_skull_" + skinHash) // TOOD use CustomSkull for this + .replace("${texture}", skinHash); + } + + private static String fillManifestJson(String template, Set skins) { + Pair uuids = generatePackUUIDs(skins); + return template.replace("${uuid1}", uuids.first().toString()) + .replace("${uuid2}", uuids.second().toString()); + } + + private static String fillTerrainTextureJson(String template, Set skins) { + StringBuilder textures = new StringBuilder(); + for (String skinHash : skins) { + String texture = String.format("\"geyser.%s_player_skin\":{\"textures\":\"textures/blocks/%s\"},", skinHash, skinHash); + textures.append(texture); + } + if (textures.length() != 0) { + // Remove trailing comma + textures.deleteCharAt(textures.length() - 1); + } + return template.replace("${texture_data}", textures); + } + + private static Pair generatePackUUIDs(Set skins) { + UUID uuid1 = UUID.randomUUID(); + UUID uuid2 = UUID.randomUUID(); + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + for (int i = 0; i < 8; i++) { + md.update((byte) ((RESOURCE_PACK_VERSION >> (i * 8)) & 0xFF)); + } + skins.stream() + .sorted() + .map(hash -> hash.getBytes(StandardCharsets.UTF_8)) + .forEach(md::update); + + ByteBuffer skinHashes = ByteBuffer.wrap(md.digest()); + uuid1 = new UUID(skinHashes.getLong(), skinHashes.getLong()); + uuid2 = new UUID(skinHashes.getLong(), skinHashes.getLong()); + } catch (NoSuchAlgorithmException e) { + GeyserImpl.getInstance().getLogger().severe("Unable to get SHA-256 Message Digest instance! Bedrock players will have to re-downloaded the player skull resource pack after each server restart.", e); + } + + return Pair.of(uuid1, uuid2); + } + + private static boolean canReusePack(Set skins, File packFile) { + Pair uuids = generatePackUUIDs(skins); + try (ZipFile zipFile = new ZipFile(packFile)) { + Optional manifestEntry = zipFile.stream() + .filter(entry -> entry.getName().contains("manifest.json")) + .findFirst(); + if (manifestEntry.isPresent()) { + ResourcePackManifest manifest = FileUtils.loadJson(zipFile.getInputStream(manifestEntry.get()), ResourcePackManifest.class); + if (!uuids.first().equals(manifest.getHeader().getUuid())) { + return false; + } + Optional resourceUUID = manifest.getModules().stream() + .filter(module -> "resources".equals(module.getType())) + .findFirst() + .map(ResourcePackManifest.Module::getUuid); + return resourceUUID.isPresent() && uuids.second().equals(resourceUUID.get()); + } + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().debug("Cached player skull resource pack was invalid! The pack will be recreated."); + } + return false; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/CustomSkull.java b/core/src/main/java/org/geysermc/geyser/registry/type/CustomSkull.java index 54bbc6835..49486dcdc 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/CustomSkull.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/CustomSkull.java @@ -22,6 +22,7 @@ * @author GeyserMC * @link https://github.com/GeyserMC/Geyser */ + package org.geysermc.geyser.registry.type; import lombok.Data; @@ -47,8 +48,8 @@ public class CustomSkull { private CustomBlockData customBlockData; - private static final String DIRECTION_PROPERTY = "davchoo:direction"; - private static final String TYPE_PROPERTY = "davchoo:type"; + private static final String DIRECTION_PROPERTY = "geyser_skull:direction"; + private static final String TYPE_PROPERTY = "geyser_skull:type"; private static final int[] ROTATIONS = {0, -90, 180, 90}; @@ -57,7 +58,7 @@ public class CustomSkull { CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder() .destroyTime(1.5f) - .materialInstances(Map.of("*", new MaterialInstance("davchoo." + skinHash + "_player_skin", "alpha_test", true, true))) + .materialInstances(Map.of("*", new MaterialInstance("geyser." + skinHash + "_player_skin", "alpha_test", true, true))) .lightFilter(0) .build(); @@ -67,7 +68,7 @@ public class CustomSkull { addWallPermutations(permutations); customBlockData = new GeyserCustomBlockData.CustomBlockDataBuilder() - .name("player_head_" + skinHash) + .name("player_skull_" + skinHash) .components(components) .intProperty(DIRECTION_PROPERTY, IntStream.rangeClosed(0, 15).boxed().toList()) .intProperty(TYPE_PROPERTY, IntStream.rangeClosed(0, 2).boxed().toList()) @@ -106,7 +107,7 @@ public class CustomSkull { private void addDefaultPermutation(List permutations) { CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder() - .geometry("geometry.davchoo.player_head_hand") + .geometry("geometry.geyser.player_skull_hand") .rotation(new RotationComponent(0, 180, 0)) .build(); @@ -132,7 +133,7 @@ public class CustomSkull { CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder() .aimCollision(box) .entityCollision(box) - .geometry("geometry.davchoo.player_head_floor_" + quadrantNames[i]) + .geometry("geometry.geyser.player_skull_floor_" + quadrantNames[i]) .rotation(rotation) .build(); @@ -158,7 +159,7 @@ public class CustomSkull { CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder() .aimCollision(box) .entityCollision(box) - .geometry("geometry.davchoo.player_head_wall") + .geometry("geometry.geyser.player_skull_wall") .rotation(rotation) .build(); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java index 291790ed8..bd39143c4 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java @@ -82,6 +82,7 @@ public class SkullCache { Skull skull = skulls.computeIfAbsent(position, Skull::new); if (!texturesProperty.equals(skull.texturesProperty)) { skull.texturesProperty = texturesProperty; + skull.skinHash = null; try { SkinManager.GameProfileData gameProfileData = SkinManager.GameProfileData.loadFromJson(texturesProperty); if (gameProfileData != null && gameProfileData.skinUrl() != null) { @@ -89,14 +90,12 @@ public class SkullCache { skull.skinHash = skinUrl.substring(skinUrl.lastIndexOf('/') + 1); } else { session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + position + " Textures: " + texturesProperty); - skull.skinHash = null; } } catch (IOException e) { session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + position + " Textures: " + texturesProperty); if (GeyserImpl.getInstance().getConfig().isDebugMode()) { e.printStackTrace(); } - skull.skinHash = null; } } skull.blockState = blockState; diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 43cf30b47..d6b16f6fd 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -406,7 +406,7 @@ public class SkinProvider { private static Skin supplySkin(UUID uuid, String textureUrl) { try { - byte[] skin = requestImage(textureUrl, null); + byte[] skin = requestImageData(textureUrl, null); return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false, false); } catch (Exception ignored) {} // just ignore I guess @@ -416,7 +416,7 @@ public class SkinProvider { private static Cape supplyCape(String capeUrl, CapeProvider provider) { byte[] cape = EMPTY_CAPE.getCapeData(); try { - cape = requestImage(capeUrl, provider); + cape = requestImageData(capeUrl, provider); } catch (Exception ignored) { } // just ignore I guess @@ -473,7 +473,7 @@ public class SkinProvider { } @SuppressWarnings("ResultOfMethodCallIgnored") - private static byte[] requestImage(String imageUrl, CapeProvider provider) throws Exception { + public static BufferedImage requestImage(String imageUrl, CapeProvider provider) throws IOException { BufferedImage image = null; // First see if we have a cached file. We also update the modification stamp so we know when the file was last used @@ -533,6 +533,11 @@ public class SkinProvider { // TODO remove alpha channel } + return image; + } + + private static byte[] requestImageData(String imageUrl, CapeProvider provider) throws Exception { + BufferedImage image = requestImage(imageUrl, provider); byte[] data = bufferedImageToImageData(image); image.flush(); return data; diff --git a/core/src/main/resources/bedrock/skull_resource_pack/animations/disable.animation.json b/core/src/main/resources/bedrock/skull_resource_pack/animations/disable.animation.json new file mode 100644 index 000000000..70a348409 --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/animations/disable.animation.json @@ -0,0 +1,14 @@ +{ + "format_version": "1.8.0", + "animations": { + "animation.geyser.disable": { + "loop": true, + "override_previous_animation": true, + "bones": { + "root": { + "scale": 0 + } + } + } + } +} diff --git a/core/src/main/resources/bedrock/skull_resource_pack/animations/player_skull.animation.json b/core/src/main/resources/bedrock/skull_resource_pack/animations/player_skull.animation.json new file mode 100644 index 000000000..6da469665 --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/animations/player_skull.animation.json @@ -0,0 +1,80 @@ +{ + "format_version": "1.8.0", + "animations": { + "animation.geyser.player_skull.head": { + "loop": true, + "bones": { + "root_x": { + "position": [0, 15.95, 0], + "scale": [1.1875, 1.1875, 1.1875] + } + } + }, + "animation.geyser.player_skull.thirdperson_main_hand": { + "loop": true, + "bones": { + "root": { + "scale": [0.5, 0.5, 0.5], + "position": [0, 12, -4] + }, + "root_x": { + "rotation": [45, 0, 0] + }, + "root_y": { + "rotation": [0, 135, 0] + } + } + }, + "animation.geyser.player_skull.thirdperson_off_hand": { + "loop": true, + "bones": { + "root": { + "scale": [0.5, 0.5, 0.5], + "position": [0, 12, -4] + }, + "root_x": { + "rotation": [45, 0, 0] + }, + "root_y": { + "rotation": [0, -135, 0] + } + } + }, + "animation.geyser.player_skull.firstperson_main_hand": { + "loop": true, + "bones": { + "root": { + "scale": [0.5, 0.5, 0.5], + "position": [2, 16, 4] + }, + "root_x": { + "rotation": [180, 0, 0] + }, + "root_y": { + "rotation": [0, 135, 0] + }, + "root_z": { + "rotation": [45, 0, 0] + } + } + }, + "animation.geyser.player_skull.firstperson_off_hand": { + "loop": true, + "bones": { + "root": { + "scale": [0.5, 0.5, 0.5], + "position": [2, 16, 4] + }, + "root_x": { + "rotation": [180, 0, 0] + }, + "root_y": { + "rotation": [0, 135, 0] + }, + "root_z": { + "rotation": [45, 0, 0] + } + } + } + } +} diff --git a/core/src/main/resources/bedrock/skull_resource_pack/attachables/template_attachable.json b/core/src/main/resources/bedrock/skull_resource_pack/attachables/template_attachable.json new file mode 100644 index 000000000..169138191 --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/attachables/template_attachable.json @@ -0,0 +1,57 @@ +{ + "format_version": "1.10.0", + "minecraft:attachable": { + "description": { + "identifier": "${identifier}", + "materials": { + "default": "entity_alphatest", + "enchanted": "entity_alphatest" + }, + "textures": { + "default": "textures/blocks/${texture}", + "enchanted": "textures/misc/enchanted_item_glint" + }, + "geometry": { + "default": "geometry.geyser.player_skull" + }, + "scripts": { + "pre_animation": [ + "v.main_hand = c.item_slot == 'main_hand';", + "v.off_hand = c.item_slot == 'off_hand';", + "v.head = c.item_slot == 'head';" + ], + "animate": [ + { + "thirdperson_main_hand": "v.main_hand && !c.is_first_person" + }, + { + "thirdperson_off_hand": "v.off_hand && !c.is_first_person" + }, + { + "thirdperson_head": "v.head && !c.is_first_person" + }, + { + "firstperson_main_hand": "v.main_hand && c.is_first_person" + }, + { + "firstperson_off_hand": "v.off_hand && c.is_first_person" + }, + { + "firstperson_head": "v.head && c.is_first_person || query.is_owner_identifier_any('minecraft:player')" + } + ] + }, + "animations": { + "thirdperson_main_hand": "animation.geyser.player_skull.thirdperson_main_hand", + "thirdperson_off_hand": "animation.geyser.player_skull.thirdperson_off_hand", + "thirdperson_head": "animation.geyser.player_skull.head", + "firstperson_main_hand": "animation.geyser.player_skull.firstperson_main_hand", + "firstperson_off_hand": "animation.geyser.player_skull.firstperson_off_hand", + "firstperson_head": "animation.geyser.disable" + }, + "render_controllers": [ + "controller.render.item_default" + ] + } + } +} diff --git a/core/src/main/resources/bedrock/skull_resource_pack/manifest.json b/core/src/main/resources/bedrock/skull_resource_pack/manifest.json new file mode 100644 index 000000000..a7a6f9c1c --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/manifest.json @@ -0,0 +1,17 @@ +{ + "format_version": 2, + "header": { + "name": "Geyser Player Skull Resource Pack", + "description": "Auto-generated resource pack to support player skulls as custom blocks", + "uuid": "${uuid1}", + "version": [1, 0, 0], + "min_engine_version": [1, 16, 0] + }, + "modules": [ + { + "type": "resources", + "uuid": "${uuid2}", + "version": [1, 0, 0] + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull.geo.json b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull.geo.json new file mode 100644 index 000000000..2681b406f --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull.geo.json @@ -0,0 +1,50 @@ +{ + "format_version": "1.16.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.geyser.player_skull", + "texture_width": 64, + "texture_height": 64 + }, + "bones": [ + { + "name": "root", + "binding": "c.item_slot == 'head' ? 'head' : q.item_slot_to_bone_name(c.item_slot)", + "pivot": [0, 8, 0] + }, + { + "name": "root_x", + "parent": "root", + "pivot": [0, 8, 0] + }, + { + "name": "root_y", + "parent": "root_x", + "pivot": [0, 8, 0] + }, + { + "name": "root_z", + "parent": "root_y", + "pivot": [0, 8, 0] + }, + { + "name": "player_skull", + "parent": "root_z", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 8, -4], "size": [8, 8, 8], "uv": [0, 0]} + ] + }, + { + "name": "player_skull_hat", + "parent": "player_skull", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 8, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.25} + ] + } + ] + } + ] +} diff --git a/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_a.geo.json b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_a.geo.json new file mode 100644 index 000000000..743236a5d --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_a.geo.json @@ -0,0 +1,29 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.geyser.player_skull_floor_a", + "texture_width": 64, + "texture_height": 64 + }, + "bones": [ + { + "name": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 0.5, -4], "size": [8, 8, 8], "uv": [0, 0]} + ] + }, + { + "name": "hat", + "parent": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 0.5, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_b.geo.json b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_b.geo.json new file mode 100644 index 000000000..0bbe1c1f6 --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_b.geo.json @@ -0,0 +1,30 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.geyser.player_skull_floor_b", + "texture_width": 64, + "texture_height": 64 + }, + "bones": [ + { + "name": "head", + "pivot": [0, 24, 0], + "rotation": [0, 22.5, 0], + "cubes": [ + {"origin": [-4, 0.5, -4], "size": [8, 8, 8], "uv": [0, 0]} + ] + }, + { + "name": "hat", + "parent": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 0.5, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_c.geo.json b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_c.geo.json new file mode 100644 index 000000000..c633977ab --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_c.geo.json @@ -0,0 +1,30 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.geyser.player_skull_floor_c", + "texture_width": 64, + "texture_height": 64 + }, + "bones": [ + { + "name": "head", + "pivot": [0, 24, 0], + "rotation": [0, 45, 0], + "cubes": [ + {"origin": [-4, 0.5, -4], "size": [8, 8, 8], "uv": [0, 0]} + ] + }, + { + "name": "hat", + "parent": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 0.5, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_d.geo.json b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_d.geo.json new file mode 100644 index 000000000..7436944e7 --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_floor_d.geo.json @@ -0,0 +1,30 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.geyser.player_skull_floor_d", + "texture_width": 64, + "texture_height": 64 + }, + "bones": [ + { + "name": "head", + "pivot": [0, 24, 0], + "rotation": [0, 67.5, 0], + "cubes": [ + {"origin": [-4, 0.5, -4], "size": [8, 8, 8], "uv": [0, 0]} + ] + }, + { + "name": "hat", + "parent": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 0.5, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_hand.geo.json b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_hand.geo.json new file mode 100644 index 000000000..6ad5bea1b --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_hand.geo.json @@ -0,0 +1,29 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.geyser.player_skull_hand", + "texture_width": 64, + "texture_height": 64 + }, + "bones": [ + { + "name": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 4, -4], "size": [8, 8, 8], "uv": [0, 0]} + ] + }, + { + "name": "hat", + "parent": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 4, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_wall.geo.json b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_wall.geo.json new file mode 100644 index 000000000..5c39733dd --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/models/blocks/player_skull_wall.geo.json @@ -0,0 +1,29 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.geyser.player_skull_wall", + "texture_width": 64, + "texture_height": 64 + }, + "bones": [ + { + "name": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 4, -0.5], "size": [8, 8, 8], "uv": [0, 0]} + ] + }, + { + "name": "hat", + "parent": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"origin": [-4, 4, -0.5], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5} + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/skull_resource_pack/pack_icon.png b/core/src/main/resources/bedrock/skull_resource_pack/pack_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f64fdce4b33f39466c03336dec679c025d4fe9e4 GIT binary patch literal 36264 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 literal 0 HcmV?d00001 diff --git a/core/src/main/resources/bedrock/skull_resource_pack/textures/terrain_texture.json b/core/src/main/resources/bedrock/skull_resource_pack/textures/terrain_texture.json new file mode 100644 index 000000000..c10c4a1c8 --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack/textures/terrain_texture.json @@ -0,0 +1,8 @@ +{ + "num_mip_levels": 4, + "padding": 8, + "resource_pack_name": "Geyser Player Skull Resource Pack", + "texture_data": { + ${texture_data} + } +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/skull_resource_pack_files.txt b/core/src/main/resources/bedrock/skull_resource_pack_files.txt new file mode 100644 index 000000000..a614edf2a --- /dev/null +++ b/core/src/main/resources/bedrock/skull_resource_pack_files.txt @@ -0,0 +1,12 @@ +skull_resource_pack/animations/disable.animation.json +skull_resource_pack/animations/player_skull.animation.json +skull_resource_pack/models/blocks/player_skull.geo.json +skull_resource_pack/models/blocks/player_skull_floor_a.geo.json +skull_resource_pack/models/blocks/player_skull_floor_c.geo.json +skull_resource_pack/models/blocks/player_skull_floor_b.geo.json +skull_resource_pack/models/blocks/player_skull_floor_d.geo.json +skull_resource_pack/models/blocks/player_skull_hand.geo.json +skull_resource_pack/models/blocks/player_skull_wall.geo.json +skull_resource_pack/textures/terrain_texture.json +skull_resource_pack/pack_icon.png +skull_resource_pack/manifest.json