From 21ea6ecf7db89a3461e2e048298311799ed079b6 Mon Sep 17 00:00:00 2001 From: OmeWillem Date: Mon, 11 Nov 2024 23:08:26 +0100 Subject: [PATCH 1/4] added replace_block_item as an additional custom item option --- .../item/custom/NonVanillaCustomItemData.java | 8 +- .../CustomItemRegistryPopulator.java | 185 +++++++++--------- 2 files changed, 101 insertions(+), 92 deletions(-) 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 2c283780cc1..821ca2961de 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 @@ -162,11 +162,11 @@ default boolean isTool() { } /** - * Gets the block the item places. + * Gets the block placer settings, if it's null then the component won't be added. * - * @return the block the item places + * @return the block placer settings */ - String block(); + @Nullable CustomBlockPlacer blockPlacer(); static NonVanillaCustomItemData.Builder builder() { return GeyserApi.api().provider(NonVanillaCustomItemData.Builder.class); @@ -208,7 +208,7 @@ interface Builder extends CustomItemData.Builder { Builder chargeable(boolean isChargeable); - Builder block(String block); + Builder blockPlacer(CustomBlockPlacer blockPlacer); /** * @deprecated Use {@link #displayHandheld(boolean)} instead. 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 0a9c93980a7..bd04d6a42fa 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 @@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition; import org.cloudburstmc.protocol.bedrock.data.inventory.ComponentItemData; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomBlockPlacer; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; @@ -49,7 +50,12 @@ import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.NonVanillaItemRegistration; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; public class CustomItemRegistryPopulator { public static void populate(Map items, Multimap customItems, List nonVanillaCustomItems) { @@ -75,14 +81,14 @@ public boolean register(@NonNull String identifier, @NonNull CustomItemData cust public boolean register(@NonNull NonVanillaCustomItemData customItemData) { if (customItemData.identifier().startsWith("minecraft:")) { GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() + - " is attempting to masquerade as a vanilla Minecraft item!"); + " is attempting to masquerade as a vanilla Minecraft item!"); return false; } if (customItemData.javaId() < items.size()) { // Attempting to overwrite an item that already exists in the protocol GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() + - " is attempting to overwrite a vanilla Minecraft item!"); + " is attempting to overwrite a vanilla Minecraft item!"); return false; } @@ -130,8 +136,8 @@ public static NonVanillaItemRegistration registerCustomItem(NonVanillaCustomItem Set repairMaterials = customItemData.repairMaterials(); Item.Builder itemBuilder = Item.builder() - .stackSize(customItemData.stackSize()) - .maxDamage(customItemData.maxDamage()); + .stackSize(customItemData.stackSize()) + .maxDamage(customItemData.maxDamage()); Item item = new Item(customIdentifier, itemBuilder) { @Override public boolean isValidRepairItem(Item other) { @@ -141,18 +147,18 @@ public boolean isValidRepairItem(Item other) { Items.register(item, customItemData.javaId()); ItemMapping customItemMapping = ItemMapping.builder() - .bedrockDefinition(new SimpleItemDefinition(customIdentifier, customItemId, true)) - .bedrockData(0) - .bedrockBlockDefinition(null) - .toolType(customItemData.toolType()) - .toolTier(customItemData.toolTier()) - .translationString(customItemData.translationString()) - .customItemOptions(Collections.emptyList()) - .javaItem(item) - .build(); + .bedrockDefinition(new SimpleItemDefinition(customIdentifier, customItemId, true)) + .bedrockData(0) + .bedrockBlockDefinition(null) + .toolType(customItemData.toolType()) + .toolTier(customItemData.toolTier()) + .translationString(customItemData.translationString()) + .customItemOptions(Collections.emptyList()) + .javaItem(item) + .build(); NbtMapBuilder builder = createComponentNbt(customItemData, customItemData.identifier(), customItemId, - customItemData.isHat(), customItemData.displayHandheld(), protocolVersion); + customItemData.isHat(), customItemData.displayHandheld(), protocolVersion); ComponentItemData componentItemData = new ComponentItemData(customIdentifier, builder.build()); return new NonVanillaItemRegistration(componentItemData, item, customItemMapping); @@ -162,7 +168,7 @@ private static NbtMapBuilder createComponentNbt(CustomItemData customItemData, I String customItemName, int customItemId, int protocolVersion) { NbtMapBuilder builder = NbtMap.builder(); builder.putString("name", customItemName) - .putInt("id", customItemId); + .putInt("id", customItemId); NbtMapBuilder itemProperties = NbtMap.builder(); NbtMapBuilder componentBuilder = NbtMap.builder(); @@ -180,7 +186,7 @@ private static NbtMapBuilder createComponentNbt(CustomItemData customItemData, I } if (mapping.getFirstBlockRuntimeId() != null) { - computeBlockItemProperties(mapping.getBedrockIdentifier(), componentBuilder); + computeBlockItemProperties(new CustomBlockPlacer(mapping.getBedrockIdentifier(), false), componentBuilder); } if (mapping.isEdible()) { @@ -192,18 +198,17 @@ private static NbtMapBuilder createComponentNbt(CustomItemData customItemData, I } switch (mapping.getBedrockIdentifier()) { - case "minecraft:fire_charge", "minecraft:flint_and_steel" -> computeBlockItemProperties("minecraft:fire", componentBuilder); + case "minecraft:fire_charge", "minecraft:flint_and_steel" -> computeBlockItemProperties(new CustomBlockPlacer("minecraft:fire", false), componentBuilder); case "minecraft:bow", "minecraft:crossbow", "minecraft:trident" -> computeChargeableProperties(itemProperties, componentBuilder, mapping.getBedrockIdentifier(), protocolVersion); case "minecraft:honey_bottle", "minecraft:milk_bucket", "minecraft:potion" -> computeConsumableProperties(itemProperties, componentBuilder, 2, true); - case "minecraft:experience_bottle", "minecraft:egg", "minecraft:ender_pearl", "minecraft:ender_eye", "minecraft:lingering_potion", "minecraft:snowball", "minecraft:splash_potion" -> - computeThrowableProperties(componentBuilder); + case "minecraft:experience_bottle", "minecraft:egg", "minecraft:ender_pearl", "minecraft:ender_eye", "minecraft:lingering_potion", "minecraft:snowball", "minecraft:splash_potion" -> computeThrowableProperties(componentBuilder); } // Hardcoded on Java, and should extend to the custom item boolean isHat = (javaItem.equals(Items.SKELETON_SKULL) || javaItem.equals(Items.WITHER_SKELETON_SKULL) - || javaItem.equals(Items.CARVED_PUMPKIN) || javaItem.equals(Items.ZOMBIE_HEAD) - || javaItem.equals(Items.PIGLIN_HEAD) || javaItem.equals(Items.DRAGON_HEAD) - || javaItem.equals(Items.CREEPER_HEAD) || javaItem.equals(Items.PLAYER_HEAD) + || javaItem.equals(Items.CARVED_PUMPKIN) || javaItem.equals(Items.ZOMBIE_HEAD) + || javaItem.equals(Items.PIGLIN_HEAD) || javaItem.equals(Items.DRAGON_HEAD) + || javaItem.equals(Items.CREEPER_HEAD) || javaItem.equals(Items.PLAYER_HEAD) ); computeRenderOffsets(isHat, customItemData, componentBuilder); @@ -217,7 +222,7 @@ private static NbtMapBuilder createComponentNbt(NonVanillaCustomItemData customI int customItemId, boolean isHat, boolean displayHandheld, int protocolVersion) { NbtMapBuilder builder = NbtMap.builder(); builder.putString("name", customItemName) - .putInt("id", customItemId); + .putInt("id", customItemId); NbtMapBuilder itemProperties = NbtMap.builder(); NbtMapBuilder componentBuilder = NbtMap.builder(); @@ -253,9 +258,9 @@ private static NbtMapBuilder createComponentNbt(NonVanillaCustomItemData customI itemProperties.putBoolean("foil", true); } - String block = customItemData.block(); - if (block != null) { - computeBlockItemProperties(block, componentBuilder); + CustomBlockPlacer blockPlacer = customItemData.blockPlacer(); + if (blockPlacer != null) { + computeBlockItemProperties(blockPlacer, componentBuilder); } componentBuilder.putCompound("item_properties", itemProperties.build()); @@ -267,8 +272,8 @@ private static NbtMapBuilder createComponentNbt(NonVanillaCustomItemData customI private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean displayHandheld, CustomItemData customItemData, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder, int protocolVersion) { NbtMap iconMap = NbtMap.builder() .putCompound("textures", NbtMap.builder() - .putString("default", customItemData.icon()) - .build()) + .putString("default", customItemData.icon()) + .build()) .build(); itemProperties.putCompound("minecraft:icon", iconMap); @@ -299,12 +304,12 @@ private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean dis // Ignore durability if the item's predicate requires that it be unbreakable if (maxDamage > 0 && customItemData.customItemOptions().unbreakable() != TriState.TRUE) { componentBuilder.putCompound("minecraft:durability", NbtMap.builder() - .putCompound("damage_chance", NbtMap.builder() - .putInt("max", 1) - .putInt("min", 1) - .build()) - .putInt("max_durability", maxDamage) - .build()); + .putCompound("damage_chance", NbtMap.builder() + .putInt("max", 1) + .putInt("min", 1) + .build()) + .putInt("max_durability", maxDamage) + .build()); itemProperties.putBoolean("use_duration", true); } } @@ -322,33 +327,33 @@ private static boolean computeToolProperties(String toolType, NbtMapBuilder item List speed = new ArrayList<>(List.of( NbtMap.builder() .putCompound("block", NbtMap.builder() - .putString("tags", "1") - .build()) + .putString("tags", "1") + .build()) .putCompound("on_dig", NbtMap.builder() - .putCompound("condition", NbtMap.builder() - .putString("expression", "") - .putInt("version", -1) - .build()) - .putString("event", "tool_durability") - .putString("target", "self") + .putCompound("condition", NbtMap.builder() + .putString("expression", "") + .putInt("version", -1) .build()) + .putString("event", "tool_durability") + .putString("target", "self") + .build()) .putInt("speed", 0) .build() )); - + componentBuilder.putCompound("minecraft:digger", NbtMap.builder() - .putList("destroy_speeds", NbtType.COMPOUND, speed) - .putCompound("on_dig", NbtMap.builder() + .putList("destroy_speeds", NbtType.COMPOUND, speed) + .putCompound("on_dig", NbtMap.builder() .putCompound("condition", NbtMap.builder() - .putString("expression", "") - .putInt("version", -1) - .build()) + .putString("expression", "") + .putInt("version", -1) + .build()) .putString("event", "tool_durability") .putString("target", "self") .build()) - .putBoolean("use_efficiency", true) - .build() + .putBoolean("use_efficiency", true) + .build() ); if (toolType.equals("sword")) { @@ -408,13 +413,17 @@ private static void computeArmorProperties(String armorType, int protectionValue } } - private static void computeBlockItemProperties(String blockItem, NbtMapBuilder componentBuilder) { + private static void computeBlockItemProperties(CustomBlockPlacer blockPlacer, NbtMapBuilder componentBuilder) { // carved pumpkin should be able to be worn and for that we would need to add wearable and armor with protection 0 here // however this would have the side effect of preventing carved pumpkins from working as an attachable on the RP side outside the head slot // it also causes the item to glitch when right clicked to "equip" so this should only be added here later if these issues can be overcome // all block items registered should be given this component to prevent double placement - componentBuilder.putCompound("minecraft:block_placer", NbtMap.builder().putString("block", blockItem).build()); + componentBuilder.putCompound("minecraft:block_placer", + NbtMap.builder() + .putString("block", blockPlacer.block()) + .putBoolean("replace_block_item", blockPlacer.replaceBlockItem()) + .build()); } private static void computeChargeableProperties(NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder, String mapping, int protocolVersion) { @@ -426,9 +435,9 @@ private static void computeChargeableProperties(NbtMapBuilder itemProperties, Nb itemProperties.putInt("enchantable_value", 1); componentBuilder.putCompound("minecraft:use_modifiers", NbtMap.builder() - .putFloat("use_duration", 100F) - .putFloat("movement_modifier", 0.35F) - .build()); + .putFloat("use_duration", 100F) + .putFloat("movement_modifier", 0.35F) + .build()); switch (mapping) { case "minecraft:bow" -> { @@ -436,19 +445,19 @@ private static void computeChargeableProperties(NbtMapBuilder itemProperties, Nb itemProperties.putInt("frame_count", 3); componentBuilder.putCompound("minecraft:shooter", NbtMap.builder() - .putList("ammunition", NbtType.COMPOUND, List.of( - NbtMap.builder() - .putCompound("item", NbtMap.builder() - .putString("name", "minecraft:arrow") - .build()) - .putBoolean("use_offhand", true) - .putBoolean("search_inventory", true) - .build() - )) - .putFloat("max_draw_duration", 0f) - .putBoolean("charge_on_draw", true) - .putBoolean("scale_power_by_draw_duration", true) - .build()); + .putList("ammunition", NbtType.COMPOUND, List.of( + NbtMap.builder() + .putCompound("item", NbtMap.builder() + .putString("name", "minecraft:arrow") + .build()) + .putBoolean("use_offhand", true) + .putBoolean("search_inventory", true) + .build() + )) + .putFloat("max_draw_duration", 0f) + .putBoolean("charge_on_draw", true) + .putBoolean("scale_power_by_draw_duration", true) + .build()); componentBuilder.putInt("minecraft:use_duration", 999); } case "minecraft:trident" -> { @@ -460,19 +469,19 @@ private static void computeChargeableProperties(NbtMapBuilder itemProperties, Nb itemProperties.putInt("frame_count", 10); componentBuilder.putCompound("minecraft:shooter", NbtMap.builder() - .putList("ammunition", NbtType.COMPOUND, List.of( - NbtMap.builder() - .putCompound("item", NbtMap.builder() - .putString("name", "minecraft:arrow") - .build()) - .putBoolean("use_offhand", true) - .putBoolean("search_inventory", true) - .build() - )) - .putFloat("max_draw_duration", 1f) - .putBoolean("charge_on_draw", true) - .putBoolean("scale_power_by_draw_duration", true) - .build()); + .putList("ammunition", NbtType.COMPOUND, List.of( + NbtMap.builder() + .putCompound("item", NbtMap.builder() + .putString("name", "minecraft:arrow") + .build()) + .putBoolean("use_offhand", true) + .putBoolean("search_inventory", true) + .build() + )) + .putFloat("max_draw_duration", 1f) + .putBoolean("charge_on_draw", true) + .putBoolean("scale_power_by_draw_duration", true) + .build()); componentBuilder.putInt("minecraft:use_duration", 999); } } @@ -521,12 +530,12 @@ private static void computeRenderOffsets(boolean isHat, CustomItemData customIte float scale3 = (float) (0.075 / (customItemData.textureSize() / 16f * 2.4f)); componentBuilder.putCompound("minecraft:render_offsets", - NbtMap.builder().putCompound("main_hand", NbtMap.builder() - .putCompound("first_person", xyzToScaleList(scale3, scale3, scale3)) - .putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build()) - .putCompound("off_hand", NbtMap.builder() - .putCompound("first_person", xyzToScaleList(scale1, scale2, scale1)) - .putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build()).build()); + NbtMap.builder().putCompound("main_hand", NbtMap.builder() + .putCompound("first_person", xyzToScaleList(scale3, scale3, scale3)) + .putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build()) + .putCompound("off_hand", NbtMap.builder() + .putCompound("first_person", xyzToScaleList(scale1, scale2, scale1)) + .putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build()).build()); } } From a3fee00e2cf0cd46a711df8dc78a5aebe358d1d6 Mon Sep 17 00:00:00 2001 From: OmeWillem Date: Mon, 11 Nov 2024 23:22:53 +0100 Subject: [PATCH 2/4] might be smart to actually provide the custom block placer --- .../api/item/custom/CustomBlockPlacer.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 api/src/main/java/org/geysermc/geyser/api/item/custom/CustomBlockPlacer.java diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomBlockPlacer.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomBlockPlacer.java new file mode 100644 index 00000000000..1c184bddc30 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomBlockPlacer.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 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.api.item.custom; + +/** + * This class is used to store the block placer settings of custom items. + */ +public record CustomBlockPlacer(String block, boolean replaceBlockItem) {} From 62d9deaa0ddb6206f6e1e16d37f5205a530636b6 Mon Sep 17 00:00:00 2001 From: OmeWillem Date: Mon, 11 Nov 2024 23:30:19 +0100 Subject: [PATCH 3/4] my ide decided not to push this again.. --- .../item/GeyserNonVanillaCustomItemData.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 9c9269df363..1593cba42d8 100644 --- a/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java @@ -29,6 +29,7 @@ import lombok.ToString; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.api.item.custom.CustomBlockPlacer; import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; @@ -55,7 +56,7 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i private final boolean isEdible; private final boolean canAlwaysEat; private final boolean isChargeable; - private final String block; + private final CustomBlockPlacer blockPlacer; public GeyserNonVanillaCustomItemData(Builder builder) { super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand, @@ -79,7 +80,7 @@ public GeyserNonVanillaCustomItemData(Builder builder) { this.isEdible = builder.edible; this.canAlwaysEat = builder.canAlwaysEat; this.isChargeable = builder.chargeable; - this.block = builder.block; + this.blockPlacer = builder.blockPlacer; } @Override @@ -163,8 +164,8 @@ public boolean isChargeable() { } @Override - public String block() { - return block; + public CustomBlockPlacer blockPlacer() { + return blockPlacer; } public static class Builder extends GeyserCustomItemData.Builder implements NonVanillaCustomItemData.Builder { @@ -193,7 +194,7 @@ public static class Builder extends GeyserCustomItemData.Builder implements NonV private boolean edible = false; private boolean canAlwaysEat = false; private boolean chargeable = false; - private String block = null; + private CustomBlockPlacer blockPlacer = null; @Override public Builder name(@NonNull String name) { @@ -348,8 +349,8 @@ public Builder chargeable(boolean isChargeable) { } @Override - public Builder block(String block) { - this.block = block; + public Builder blockPlacer(CustomBlockPlacer blockPlacer) { + this.blockPlacer = blockPlacer; return this; } From 9efb855ca1e5b0b15dd9cb884417a0abdbbb774e Mon Sep 17 00:00:00 2001 From: OmeWillem Date: Tue, 12 Nov 2024 20:31:23 +0100 Subject: [PATCH 4/4] changes requested by reviewer --- .../item/custom/NonVanillaCustomItemData.java | 14 ++++++++ .../item/GeyserNonVanillaCustomItemData.java | 11 ++++++ .../CustomItemRegistryPopulator.java | 35 ++++++++++--------- 3 files changed, 43 insertions(+), 17 deletions(-) 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 821ca2961de..660af3d589a 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 @@ -161,6 +161,14 @@ default boolean isTool() { return displayHandheld(); } + /** + * @deprecated Use {@link #blockPlacer()} instead. + * Gets the block the item places. + * + * @return the block the item places + */ + String block(); + /** * Gets the block placer settings, if it's null then the component won't be added. * @@ -208,6 +216,12 @@ interface Builder extends CustomItemData.Builder { Builder chargeable(boolean isChargeable); + /** + * @deprecated Use {@link #blockPlacer(CustomBlockPlacer)} instead. + */ + @Deprecated + Builder block(String block); + Builder blockPlacer(CustomBlockPlacer blockPlacer); /** 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 1593cba42d8..38af17faac7 100644 --- a/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java @@ -163,6 +163,11 @@ public boolean isChargeable() { return isChargeable; } + @Override + public String block() { + return blockPlacer == null ? null : blockPlacer.block(); + } + @Override public CustomBlockPlacer blockPlacer() { return blockPlacer; @@ -348,6 +353,12 @@ public Builder chargeable(boolean isChargeable) { return this; } + @Override + public Builder block(String block) { + this.blockPlacer = new CustomBlockPlacer(block, false); + return this; + } + @Override public Builder blockPlacer(CustomBlockPlacer blockPlacer) { this.blockPlacer = blockPlacer; 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 bd04d6a42fa..037a257854d 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 @@ -50,12 +50,7 @@ import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.NonVanillaItemRegistration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; public class CustomItemRegistryPopulator { public static void populate(Map items, Multimap customItems, List nonVanillaCustomItems) { @@ -173,7 +168,7 @@ private static NbtMapBuilder createComponentNbt(CustomItemData customItemData, I NbtMapBuilder itemProperties = NbtMap.builder(); NbtMapBuilder componentBuilder = NbtMap.builder(); - setupBasicItemInfo(javaItem.maxDamage(), javaItem.maxStackSize(), mapping.getToolType() != null || customItemData.displayHandheld(), customItemData, itemProperties, componentBuilder, protocolVersion); + setupBasicItemInfo(javaItem.maxDamage(), javaItem.maxStackSize(), mapping.getToolType() != null || customItemData.displayHandheld(), false, customItemData, itemProperties, componentBuilder, protocolVersion); boolean canDestroyInCreative = true; if (mapping.getToolType() != null) { // This is not using the isTool boolean because it is not just a render type here. @@ -201,7 +196,8 @@ private static NbtMapBuilder createComponentNbt(CustomItemData customItemData, I case "minecraft:fire_charge", "minecraft:flint_and_steel" -> computeBlockItemProperties(new CustomBlockPlacer("minecraft:fire", false), componentBuilder); case "minecraft:bow", "minecraft:crossbow", "minecraft:trident" -> computeChargeableProperties(itemProperties, componentBuilder, mapping.getBedrockIdentifier(), protocolVersion); case "minecraft:honey_bottle", "minecraft:milk_bucket", "minecraft:potion" -> computeConsumableProperties(itemProperties, componentBuilder, 2, true); - case "minecraft:experience_bottle", "minecraft:egg", "minecraft:ender_pearl", "minecraft:ender_eye", "minecraft:lingering_potion", "minecraft:snowball", "minecraft:splash_potion" -> computeThrowableProperties(componentBuilder); + case "minecraft:experience_bottle", "minecraft:egg", "minecraft:ender_pearl", "minecraft:ender_eye", "minecraft:lingering_potion", "minecraft:snowball", "minecraft:splash_potion" -> + computeThrowableProperties(componentBuilder); } // Hardcoded on Java, and should extend to the custom item @@ -227,7 +223,10 @@ private static NbtMapBuilder createComponentNbt(NonVanillaCustomItemData customI NbtMapBuilder itemProperties = NbtMap.builder(); NbtMapBuilder componentBuilder = NbtMap.builder(); - setupBasicItemInfo(customItemData.maxDamage(), customItemData.stackSize(), displayHandheld, customItemData, itemProperties, componentBuilder, protocolVersion); + // this can replace minecraft:icon, so we have to add this here. + boolean replaceBlockItem = customItemData.blockPlacer() != null && customItemData.blockPlacer().replaceBlockItem(); + + setupBasicItemInfo(customItemData.maxDamage(), customItemData.stackSize(), displayHandheld, replaceBlockItem, customItemData, itemProperties, componentBuilder, protocolVersion); boolean canDestroyInCreative = true; if (customItemData.toolType() != null) { // This is not using the isTool boolean because it is not just a render type here. @@ -269,13 +268,15 @@ private static NbtMapBuilder createComponentNbt(NonVanillaCustomItemData customI return builder; } - private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean displayHandheld, CustomItemData customItemData, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder, int protocolVersion) { - NbtMap iconMap = NbtMap.builder() - .putCompound("textures", NbtMap.builder() - .putString("default", customItemData.icon()) - .build()) - .build(); - itemProperties.putCompound("minecraft:icon", iconMap); + private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean displayHandheld, boolean replaceBlockItem, CustomItemData customItemData, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder, int protocolVersion) { + if (!replaceBlockItem) { + NbtMap iconMap = NbtMap.builder() + .putCompound("textures", NbtMap.builder() + .putString("default", customItemData.icon()) + .build()) + .build(); + itemProperties.putCompound("minecraft:icon", iconMap); + } if (customItemData.creativeCategory().isPresent()) { itemProperties.putInt("creative_category", customItemData.creativeCategory().getAsInt()); @@ -499,7 +500,7 @@ private static void computeConsumableProperties(NbtMapBuilder itemProperties, Nb private static void computeEntityPlacerProperties(NbtMapBuilder componentBuilder) { // all items registered that place entities should be given this component to prevent double placement - // it is okay that the entity here does not match the actual one since we control what entity actually spawns + // it is okay that the entity here does not match the actual one since we control what entity actually spawns componentBuilder.putCompound("minecraft:entity_placer", NbtMap.builder().putString("entity", "minecraft:minecart").build()); }