diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/CustomBlockComponents.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/CustomBlockComponents.java index 036723092..63788df8e 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/CustomBlockComponents.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/CustomBlockComponents.java @@ -185,7 +185,7 @@ public interface CustomBlockComponents { Builder placeAir(boolean placeAir); - Builder tags(Set tags); + Builder tags(@Nullable Set tags); CustomBlockComponents build(); } 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 d256b9ac0..404679e60 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 @@ -29,6 +29,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; +import java.util.Set; + /** * This is used to store data for a custom item. */ @@ -89,6 +91,14 @@ public interface CustomItemData { */ @Nullable CustomRenderOffsets renderOffsets(); + /** + * Gets the item's set of tags that can be used in Molang. + * Equivalent to "tag:some_tag" + * + * @return the item's tags, if they exist + */ + @NonNull Set tags(); + static CustomItemData.Builder builder() { return GeyserApi.api().provider(CustomItemData.Builder.class); } @@ -113,6 +123,8 @@ public interface CustomItemData { Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets); + Builder tags(@Nullable Set tags); + CustomItemData 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 0a09f6958..616a5bba6 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 @@ -239,6 +239,9 @@ public interface NonVanillaCustomItemData extends CustomItemData { @Override Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets); + @Override + Builder tags(@Nullable Set tags); + NonVanillaCustomItemData build(); } } 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 3535eaf4d..c86c370bb 100644 --- a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java @@ -28,10 +28,14 @@ package org.geysermc.geyser.item; import lombok.EqualsAndHashCode; import lombok.ToString; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; -import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; @EqualsAndHashCode @ToString @@ -44,6 +48,7 @@ public class GeyserCustomItemData implements CustomItemData { private final boolean displayHandheld; private final int textureSize; private final CustomRenderOffsets renderOffsets; + private final Set tags; public GeyserCustomItemData(String name, CustomItemOptions customItemOptions, @@ -52,7 +57,8 @@ public class GeyserCustomItemData implements CustomItemData { boolean allowOffhand, boolean displayHandheld, int textureSize, - CustomRenderOffsets renderOffsets) { + CustomRenderOffsets renderOffsets, + Set tags) { this.name = name; this.customItemOptions = customItemOptions; this.displayName = displayName; @@ -61,10 +67,11 @@ public class GeyserCustomItemData implements CustomItemData { this.displayHandheld = displayHandheld; this.textureSize = textureSize; this.renderOffsets = renderOffsets; + this.tags = tags; } @Override - public @NotNull String name() { + public @NonNull String name() { return name; } @@ -74,12 +81,12 @@ public class GeyserCustomItemData implements CustomItemData { } @Override - public @NotNull String displayName() { + public @NonNull String displayName() { return displayName; } @Override - public @NotNull String icon() { + public @NonNull String icon() { return icon; } @@ -103,6 +110,11 @@ public class GeyserCustomItemData implements CustomItemData { return renderOffsets; } + @Override + public @NonNull Set tags() { + return tags; + } + public static class CustomItemDataBuilder implements Builder { protected String name = null; protected CustomItemOptions customItemOptions = null; @@ -113,6 +125,7 @@ public class GeyserCustomItemData implements CustomItemData { protected boolean displayHandheld = false; protected int textureSize = 16; protected CustomRenderOffsets renderOffsets = null; + protected Set tags = new HashSet<>(); @Override public Builder name(@NonNull String name) { @@ -162,6 +175,12 @@ public class GeyserCustomItemData implements CustomItemData { return this; } + @Override + public Builder tags(@Nullable Set tags) { + this.tags = Objects.requireNonNullElseGet(tags, Set::of); + return this; + } + @Override public CustomItemData build() { if (this.name == null || this.customItemOptions == null) { @@ -174,7 +193,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.displayHandheld, this.textureSize, this.renderOffsets); + return new GeyserCustomItemData(this.name, this.customItemOptions, this.displayName, this.icon, this.allowOffhand, this.displayHandheld, this.textureSize, this.renderOffsets, this.tags); } } } 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 d6731e3b8..47b5aed33 100644 --- a/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java @@ -61,7 +61,7 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i public GeyserNonVanillaCustomItemData(NonVanillaCustomItemDataBuilder builder) { super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand, - builder.displayHandheld, builder.textureSize, builder.renderOffsets); + builder.displayHandheld, builder.textureSize, builder.renderOffsets, builder.tags); this.identifier = builder.identifier; this.javaId = builder.javaId; @@ -237,6 +237,11 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i return (NonVanillaCustomItemData.Builder) super.renderOffsets(renderOffsets); } + @Override + public NonVanillaCustomItemData.Builder tags(@Nullable Set tags) { + return (NonVanillaCustomItemData.Builder) super.tags(tags); + } + @Override public NonVanillaCustomItemData.Builder identifier(@NonNull String identifier) { this.identifier = identifier; diff --git a/core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java b/core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java deleted file mode 100644 index efefee946..000000000 --- a/core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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.item.components; - -import org.cloudburstmc.nbt.NbtMap; -import org.cloudburstmc.nbt.NbtType; - -import java.util.ArrayList; -import java.util.List; - -public class ToolBreakSpeedsUtils { - public static int toolTierToSpeed(String toolTier) { - ToolTier tier = ToolTier.getByName(toolTier); - if (tier != null) { - return tier.getSpeed(); - } - - return 0; - } - - private static NbtMap createTagBreakSpeed(int speed, String... tags) { - StringBuilder builder = new StringBuilder("query.any_tag('"); - builder.append(tags[0]); - for (int i = 1; i < tags.length; i++) { - builder.append("', '").append(tags[i]); - } - builder.append("')"); - - return NbtMap.builder() - .putCompound("block", NbtMap.builder() - .putString("tags", builder.toString()) - .build()) - .putCompound("on_dig", NbtMap.builder() - .putCompound("condition", NbtMap.builder() - .putString("expression", "") - .putInt("version", -1) - .build()) - .putString("event", "tool_durability") - .putString("target", "self") - .build()) - .putInt("speed", speed) - .build(); - } - - private static NbtMap createBreakSpeed(int speed, String block) { - return NbtMap.builder() - .putCompound("block", NbtMap.builder() - .putString("name", block).build()) - .putCompound("on_dig", NbtMap.builder() - .putCompound("condition", NbtMap.builder() - .putString("expression", "") - .putInt("version", -1) - .build()) - .putString("event", "tool_durability") - .putString("target", "self") - .build()) - .putInt("speed", speed) - .build(); - } - - private static NbtMap createDigger(List speeds) { - return NbtMap.builder() - .putList("destroy_speeds", NbtType.COMPOUND, speeds) - .putCompound("on_dig", NbtMap.builder() - .putCompound("condition", NbtMap.builder() - .putString("expression", "") - .putInt("version", -1) - .build()) - .putString("event", "tool_durability") - .putString("target", "self") - .build()) - .putBoolean("use_efficiency", true) - .build(); - } - - public static NbtMap getAxeDigger(int speed) { - List speeds = new ArrayList<>(); - speeds.add(createTagBreakSpeed(speed, "wood", "pumpkin", "plant")); - - return createDigger(speeds); - } - - public static NbtMap getPickaxeDigger(int speed, String toolTier) { - List speeds = new ArrayList<>(); - if (toolTier.equals(ToolTier.DIAMOND.toString()) || toolTier.equals(ToolTier.NETHERITE.toString())) { - speeds.add(createTagBreakSpeed(speed, "iron_pick_diggable", "diamond_pick_diggable")); - } else { - speeds.add(createTagBreakSpeed(speed, "iron_pick_diggable")); - } - speeds.add(createTagBreakSpeed(speed, "stone", "metal", "rail", "mob_spawner")); - - return createDigger(speeds); - } - - public static NbtMap getShovelDigger(int speed) { - List speeds = new ArrayList<>(); - speeds.add(createTagBreakSpeed(speed, "dirt", "sand", "gravel", "grass", "snow")); - - return createDigger(speeds); - } - - public static NbtMap getSwordDigger(int speed) { - List speeds = new ArrayList<>(); - speeds.add(createBreakSpeed(speed, "minecraft:web")); - speeds.add(createBreakSpeed(speed, "minecraft:bamboo")); - - return createDigger(speeds); - } - - public static NbtMap getHoeDigger(int speed) { - List speeds = new ArrayList<>(); - speeds.add(createBreakSpeed(speed, "minecraft:leaves")); - speeds.add(createBreakSpeed(speed, "minecraft:leaves2")); - speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves")); - speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves_flowered")); - - speeds.add(createBreakSpeed(speed, "minecraft:sculk")); - speeds.add(createBreakSpeed(speed, "minecraft:sculk_catalyst")); - speeds.add(createBreakSpeed(speed, "minecraft:sculk_sensor")); - speeds.add(createBreakSpeed(speed, "minecraft:sculk_shrieker")); - speeds.add(createBreakSpeed(speed, "minecraft:sculk_vein")); - - speeds.add(createBreakSpeed(speed, "minecraft:nether_wart_block")); - speeds.add(createBreakSpeed(speed, "minecraft:warped_wart_block")); - - speeds.add(createBreakSpeed(speed, "minecraft:hay_block")); - speeds.add(createBreakSpeed(speed, "minecraft:moss_block")); - speeds.add(createBreakSpeed(speed, "minecraft:shroomlight")); - speeds.add(createBreakSpeed(speed, "minecraft:sponge")); - speeds.add(createBreakSpeed(speed, "minecraft:target")); - - return createDigger(speeds); - } - - public static NbtMap getShearsDigger(int speed) { - List speeds = new ArrayList<>(); - speeds.add(createBreakSpeed(speed, "minecraft:web")); - - speeds.add(createBreakSpeed(speed, "minecraft:leaves")); - speeds.add(createBreakSpeed(speed, "minecraft:leaves2")); - speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves")); - speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves_flowered")); - - speeds.add(createBreakSpeed(speed, "minecraft:wool")); - - speeds.add(createBreakSpeed(speed, "minecraft:glow_lichen")); - speeds.add(createBreakSpeed(speed, "minecraft:vine")); - - return createDigger(speeds); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/level/block/GeyserCustomBlockComponents.java b/core/src/main/java/org/geysermc/geyser/level/block/GeyserCustomBlockComponents.java index e43e168ee..e401567e2 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/GeyserCustomBlockComponents.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/GeyserCustomBlockComponents.java @@ -31,18 +31,19 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Value; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.block.custom.component.BoxComponent; import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents; import org.geysermc.geyser.api.block.custom.component.GeometryComponent; import org.geysermc.geyser.api.block.custom.component.MaterialInstance; import org.geysermc.geyser.api.block.custom.component.PlacementConditions; import org.geysermc.geyser.api.block.custom.component.TransformationComponent; -import org.jetbrains.annotations.NotNull; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Objects; @Value public class GeyserCustomBlockComponents implements CustomBlockComponents { @@ -152,7 +153,7 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents { } @Override - public @NotNull Set tags() { + public @NonNull Set tags() { return tags; } @@ -170,7 +171,7 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents { protected TransformationComponent transformation; protected boolean unitCube = false; protected boolean placeAir = false; - protected final Set tags = new HashSet<>(); + protected Set tags = new HashSet<>(); private void validateBox(BoxComponent box) { if (box == null) { @@ -217,7 +218,7 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents { } @Override - public Builder materialInstance(@NotNull String name, @NotNull MaterialInstance materialInstance) { + public Builder materialInstance(@NonNull String name, @NonNull MaterialInstance materialInstance) { this.materialInstances.put(name, materialInstance); return this; } @@ -292,8 +293,8 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents { } @Override - public Builder tags(Set tags) { - this.tags.addAll(tags); + public Builder tags(@Nullable Set tags) { + this.tags = Objects.requireNonNullElseGet(tags, Set::of); return this; } diff --git a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java index e63bf6bf9..fb4a6acbe 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java +++ b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java @@ -198,6 +198,12 @@ public class MappingsReader_v1 extends MappingsReader { customItemData.renderOffsets(fromJsonNode(tmpNode)); } + if (node.get("tags") instanceof ArrayNode tags) { + Set tagsSet = new ObjectOpenHashSet<>(); + tags.forEach(tag -> tagsSet.add(tag.asText())); + customItemData.tags(tagsSet); + } + return customItemData.build(); } 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 3f3f5a4ba..638e41a5e 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 @@ -270,6 +270,17 @@ public class CustomItemRegistryPopulator { .build()); componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", customItemData.displayName()).build()); + // Add a Geyser tag to the item, allowing Molang queries + addItemTag(componentBuilder, "geyser:is_custom"); + + // Add other defined tags to the item + Set tags = customItemData.tags(); + for (String tag : tags) { + if (tag != null && !tag.isBlank()) { + addItemTag(componentBuilder, tag); + } + } + itemProperties.putBoolean("allow_off_hand", customItemData.allowOffhand()); itemProperties.putBoolean("hand_equipped", displayHandheld); itemProperties.putInt("max_stack_size", stackSize); @@ -313,7 +324,7 @@ public class CustomItemRegistryPopulator { .build() )); - componentBuilder.putCompound("minecraft:digger", + componentBuilder.putCompound("minecraft:digger", NbtMap.builder() .putList("destroy_speeds", NbtType.COMPOUND, speed) .putCompound("on_dig", NbtMap.builder() @@ -506,8 +517,19 @@ public class CustomItemRegistryPopulator { return List.of(xyz.x(), xyz.y(), xyz.z()); } - private static void setItemTag(NbtMapBuilder builder, String tag) { - builder.putList("item_tags", NbtType.STRING, List.of("minecraft:is_" + tag)); + @SuppressWarnings("unchecked") + private static void addItemTag(NbtMapBuilder builder, String tag) { + List tagList = (List) builder.get("item_tags"); + if (tagList == null) { + builder.putList("item_tags", NbtType.STRING, tag); + } else { + // NbtList is immutable + if (!tagList.contains(tag)) { + tagList = new ArrayList<>(tagList); + tagList.add(tag); + builder.putList("item_tags", NbtType.STRING, tagList); + } + } } private static NbtMap xyzToScaleList(float x, float y, float z) {