From 98dceee5e31a704d87cece29df221495b8d6443e Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Tue, 11 Apr 2023 20:32:31 +0200 Subject: [PATCH] Mapping option for handheld display & fix for #3346 (#3672) * Mapping option for handheld display and fix for #3346 * Fix custom items --- .../api/item/custom/CustomItemData.java | 9 +++++++ .../api/item/custom/CustomItemOptions.java | 10 ++++++++ .../item/custom/NonVanillaCustomItemData.java | 24 +++++++++++++++++-- .../geyser/item/GeyserCustomItemData.java | 24 ++++++++++++++++++- .../geyser/item/GeyserCustomItemOptions.java | 16 +++++++++++-- .../item/GeyserNonVanillaCustomItemData.java | 18 +++++--------- .../mappings/versions/MappingsReader_v1.java | 15 +++++++++--- .../CustomItemRegistryPopulator.java | 12 +++++----- .../inventory/item/CustomItemTranslator.java | 4 ++++ 9 files changed, 106 insertions(+), 26 deletions(-) diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java index 17763fb77..d256b9ac0 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java @@ -68,6 +68,13 @@ public interface CustomItemData { */ boolean allowOffhand(); + /** + * Gets if the item should be displayed as handheld, like a tool. + * + * @return true if the item should be displayed as handheld, false otherwise + */ + boolean displayHandheld(); + /** * Gets the item's texture size. This is to resize the item if the texture is not 16x16. * @@ -100,6 +107,8 @@ public interface CustomItemData { Builder allowOffhand(boolean allowOffhand); + Builder displayHandheld(boolean displayHandheld); + Builder textureSize(int textureSize); Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets); diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java index ec26a6e37..2ca19e20e 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java @@ -56,6 +56,14 @@ public interface CustomItemOptions { */ @NonNull OptionalInt damagePredicate(); + /** + * Gets if this mapping should just translate to the default item. + * This is used for the damage predicate of damaged 1 damage 0 that is required to allow the default item to exist. + * + * @return true if this mapping should just translate to the default item, false otherwise + */ + boolean defaultItem(); + /** * Checks if the item has at least one option set * @@ -78,6 +86,8 @@ public interface CustomItemOptions { Builder damagePredicate(int damagePredicate); + Builder defaultItem(boolean defaultItem); + CustomItemOptions build(); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java index d2cef637a..317c240b3 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java @@ -130,17 +130,22 @@ public interface NonVanillaCustomItemData extends CustomItemData { boolean isHat(); /** + * @deprecated Use {@link #displayHandheld()} instead. * Gets if the item is a tool. This is used to set the render type of the item, if the item is handheld. * * @return if the item is a tool */ - boolean isTool(); + @Deprecated + default boolean isTool() { + return displayHandheld(); + } static NonVanillaCustomItemData.Builder builder() { return GeyserApi.api().provider(NonVanillaCustomItemData.Builder.class); } interface Builder extends CustomItemData.Builder { + @Override Builder name(@NonNull String name); Builder identifier(@NonNull String identifier); @@ -169,14 +174,29 @@ public interface NonVanillaCustomItemData extends CustomItemData { Builder hat(boolean isHat); - Builder tool(boolean isTool); + /** + * @deprecated Use {@link #displayHandheld(boolean)} instead. + */ + @Deprecated + default Builder tool(boolean isTool) { + return displayHandheld(isTool); + } + + @Override + Builder customItemOptions(@NonNull CustomItemOptions customItemOptions); @Override Builder displayName(@NonNull String displayName); + @Override + Builder icon(@NonNull String icon); + @Override Builder allowOffhand(boolean allowOffhand); + @Override + Builder displayHandheld(boolean displayHandheld); + @Override Builder textureSize(int textureSize); diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java index ddea9937c..3535eaf4d 100644 --- a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java @@ -41,6 +41,7 @@ public class GeyserCustomItemData implements CustomItemData { private final String displayName; private final String icon; private final boolean allowOffhand; + private final boolean displayHandheld; private final int textureSize; private final CustomRenderOffsets renderOffsets; @@ -49,6 +50,7 @@ public class GeyserCustomItemData implements CustomItemData { String displayName, String icon, boolean allowOffhand, + boolean displayHandheld, int textureSize, CustomRenderOffsets renderOffsets) { this.name = name; @@ -56,34 +58,47 @@ public class GeyserCustomItemData implements CustomItemData { this.displayName = displayName; this.icon = icon; this.allowOffhand = allowOffhand; + this.displayHandheld = displayHandheld; this.textureSize = textureSize; this.renderOffsets = renderOffsets; } + @Override public @NotNull String name() { return name; } + @Override public CustomItemOptions customItemOptions() { return customItemOptions; } + @Override public @NotNull String displayName() { return displayName; } + @Override public @NotNull String icon() { return icon; } + @Override public boolean allowOffhand() { return allowOffhand; } + @Override + public boolean displayHandheld() { + return this.displayHandheld; + } + + @Override public int textureSize() { return textureSize; } + @Override public CustomRenderOffsets renderOffsets() { return renderOffsets; } @@ -95,6 +110,7 @@ public class GeyserCustomItemData implements CustomItemData { protected String displayName = null; protected String icon = null; protected boolean allowOffhand = true; // Bedrock doesn't give items offhand allowance unless they serve gameplay purpose, but we want to be friendly with Java + protected boolean displayHandheld = false; protected int textureSize = 16; protected CustomRenderOffsets renderOffsets = null; @@ -128,6 +144,12 @@ public class GeyserCustomItemData implements CustomItemData { return this; } + @Override + public Builder displayHandheld(boolean displayHandheld) { + this.displayHandheld = displayHandheld; + return this; + } + @Override public Builder textureSize(int textureSize) { this.textureSize = textureSize; @@ -152,7 +174,7 @@ public class GeyserCustomItemData implements CustomItemData { if (this.icon == null) { this.icon = this.name; } - return new GeyserCustomItemData(this.name, this.customItemOptions, this.displayName, this.icon, this.allowOffhand, this.textureSize, this.renderOffsets); + return new GeyserCustomItemData(this.name, this.customItemOptions, this.displayName, this.icon, this.allowOffhand, this.displayHandheld, this.textureSize, this.renderOffsets); } } } diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java index dd4ae01de..cebad261e 100644 --- a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java @@ -32,12 +32,18 @@ import java.util.OptionalInt; public record GeyserCustomItemOptions(TriState unbreakable, OptionalInt customModelData, - OptionalInt damagePredicate) implements CustomItemOptions { + OptionalInt damagePredicate, + boolean defaultItem) implements CustomItemOptions { + + public GeyserCustomItemOptions(TriState unbreakable, OptionalInt customModelData, OptionalInt damagePredicate) { + this(unbreakable, customModelData, damagePredicate, false); + } public static class CustomItemOptionsBuilder implements CustomItemOptions.Builder { private TriState unbreakable = TriState.NOT_SET; private OptionalInt customModelData = OptionalInt.empty(); private OptionalInt damagePredicate = OptionalInt.empty(); + private boolean defaultItem = false; @Override public Builder unbreakable(boolean unbreakable) { @@ -61,9 +67,15 @@ public record GeyserCustomItemOptions(TriState unbreakable, return this; } + @Override + public Builder defaultItem(boolean defaultItem) { + this.defaultItem = defaultItem; + return this; + } + @Override public CustomItemOptions build() { - return new GeyserCustomItemOptions(unbreakable, customModelData, damagePredicate); + return new GeyserCustomItemOptions(this.unbreakable, this.customModelData, this.damagePredicate, this.defaultItem); } } } diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java b/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java index efdc1fdcf..892e22693 100644 --- a/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java @@ -57,7 +57,7 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i public GeyserNonVanillaCustomItemData(NonVanillaCustomItemDataBuilder builder) { super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand, - builder.textureSize, builder.renderOffsets); + builder.displayHandheld, builder.textureSize, builder.renderOffsets); this.identifier = builder.identifier; this.javaId = builder.javaId; @@ -140,11 +140,6 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i return isHat; } - @Override - public boolean isTool() { - return isTool; - } - public static class NonVanillaCustomItemDataBuilder extends GeyserCustomItemData.CustomItemDataBuilder implements NonVanillaCustomItemData.Builder { private String identifier = null; private int javaId = -1; @@ -185,6 +180,11 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i return (NonVanillaCustomItemData.Builder) super.allowOffhand(allowOffhand); } + @Override + public NonVanillaCustomItemData.Builder displayHandheld(boolean displayHandheld) { + return (NonVanillaCustomItemData.Builder) super.displayHandheld(displayHandheld); + } + @Override public NonVanillaCustomItemData.Builder displayName(@NonNull String displayName) { return (NonVanillaCustomItemData.Builder) super.displayName(displayName); @@ -283,12 +283,6 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i return this; } - @Override - public NonVanillaCustomItemData.Builder tool(boolean isTool) { - this.tool = isTool; - return this; - } - @Override public NonVanillaCustomItemData build() { if (identifier == null || javaId == -1) { diff --git a/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java index 217ff844e..e08190aff 100644 --- a/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java +++ b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java @@ -77,6 +77,11 @@ public class MappingsReader_v1 extends MappingsReader { customItemOptions.unbreakable(unbreakable.asBoolean()); } + JsonNode defaultItem = node.get("default"); + if (defaultItem != null && defaultItem.isBoolean()) { + customItemOptions.defaultItem(defaultItem.asBoolean()); + } + return customItemOptions.build(); } @@ -86,13 +91,13 @@ public class MappingsReader_v1 extends MappingsReader { throw new InvalidCustomMappingsFileException("Invalid item mappings entry"); } - String name = node.get("name").asText(); - if (name == null || name.isEmpty()) { + JsonNode name = node.get("name"); + if (name == null || !name.isTextual() || name.asText().isEmpty()) { throw new InvalidCustomMappingsFileException("An item entry has no name"); } CustomItemData.Builder customItemData = CustomItemData.builder() - .name(name) + .name(name.asText()) .customItemOptions(this.readItemCustomItemOptions(node)); //The next entries are optional @@ -108,6 +113,10 @@ public class MappingsReader_v1 extends MappingsReader { customItemData.allowOffhand(node.get("allow_offhand").asBoolean()); } + if (node.has("display_handheld")) { + customItemData.displayHandheld(node.get("display_handheld").asBoolean()); + } + if (node.has("texture_size")) { customItemData.textureSize(node.get("texture_size").asInt()); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java index 345a5b0d9..cd7a0d491 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java @@ -153,7 +153,7 @@ public class CustomItemRegistryPopulator { .build(); NbtMapBuilder builder = createComponentNbt(customItemData, customItemData.identifier(), customItemId, - customItemData.creativeCategory(), customItemData.creativeGroup(), customItemData.isHat(), customItemData.isTool()); + customItemData.creativeCategory(), customItemData.creativeGroup(), customItemData.isHat(), customItemData.displayHandheld()); ComponentItemData componentItemData = new ComponentItemData(customIdentifier, builder.build()); return new NonVanillaItemRegistration(componentItemData, item, customItemMapping); @@ -168,7 +168,7 @@ public class CustomItemRegistryPopulator { NbtMapBuilder itemProperties = NbtMap.builder(); NbtMapBuilder componentBuilder = NbtMap.builder(); - setupBasicItemInfo(mapping.getMaxDamage(), mapping.getStackSize(), mapping.getToolType() != null, customItemData, itemProperties, componentBuilder); + setupBasicItemInfo(mapping.getMaxDamage(), mapping.getStackSize(), mapping.getToolType() != null || customItemData.displayHandheld(), customItemData, itemProperties, componentBuilder); boolean canDestroyInCreative = true; if (mapping.getToolType() != null) { // This is not using the isTool boolean because it is not just a render type here. @@ -217,7 +217,7 @@ public class CustomItemRegistryPopulator { private static NbtMapBuilder createComponentNbt(NonVanillaCustomItemData customItemData, String customItemName, int customItemId, OptionalInt creativeCategory, - String creativeGroup, boolean isHat, boolean isTool) { + String creativeGroup, boolean isHat, boolean displayHandheld) { NbtMapBuilder builder = NbtMap.builder(); builder.putString("name", customItemName) .putInt("id", customItemId); @@ -225,7 +225,7 @@ public class CustomItemRegistryPopulator { NbtMapBuilder itemProperties = NbtMap.builder(); NbtMapBuilder componentBuilder = NbtMap.builder(); - setupBasicItemInfo(customItemData.maxDamage(), customItemData.stackSize(), isTool, customItemData, itemProperties, componentBuilder); + setupBasicItemInfo(customItemData.maxDamage(), customItemData.stackSize(), displayHandheld, customItemData, itemProperties, componentBuilder); boolean canDestroyInCreative = true; if (customItemData.toolType() != null) { // This is not using the isTool boolean because it is not just a render type here. @@ -253,14 +253,14 @@ public class CustomItemRegistryPopulator { return builder; } - private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean isTool, CustomItemData customItemData, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) { + private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean displayHandheld, CustomItemData customItemData, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) { itemProperties.putCompound("minecraft:icon", NbtMap.builder() .putString("texture", customItemData.icon()) .build()); componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", customItemData.displayName()).build()); itemProperties.putBoolean("allow_off_hand", customItemData.allowOffhand()); - itemProperties.putBoolean("hand_equipped", isTool); + itemProperties.putBoolean("hand_equipped", displayHandheld); itemProperties.putInt("max_stack_size", stackSize); // Ignore durability if the item's predicate requires that it be unbreakable if (maxDamage > 0 && customItemData.customItemOptions().unbreakable() != TriState.TRUE) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CustomItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CustomItemTranslator.java index 4abfd18b9..31491fc3d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CustomItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CustomItemTranslator.java @@ -93,6 +93,10 @@ public final class CustomItemTranslator { continue; } + if (options.defaultItem()) { + return null; + } + return mappingTypes.value(); }