mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Add ability to set molang tags for custom items (#4041)
* Start on custom molang tags with custom items * geyser_custom instead of geyser item tag * Address reviews, add custom namespace ("geyser_custom") to tags * use isBlank() instead of isEmpty() * More efficient item tag setting Co-authored-by: Konicai <71294714+konicai@users.noreply.github.com> * tags instead of temp * Merge in master, adapt to changes in the MappingsReader, delete unused ToolBreakSpeedsUtils class * oops * clean diff * Change namespace from `geyser_custom` to just `geyser` * Don't force a namespace at all; just like blocks don't * Tags for items are now, as blocks, NonNull. Additionally, calling the .tags() builder multiple times will not add both sets of tags, but replace the existing tag set * Remove @NotNull usage in favor of @NonNull * Allow setting null for tags, but ensure that .tags() is always non-null * Fix nullable annotation on tags method in the builder interface
This commit is contained in:
parent
aa899af908
commit
f40ca2004e
9 changed files with 85 additions and 191 deletions
|
@ -185,7 +185,7 @@ public interface CustomBlockComponents {
|
|||
|
||||
Builder placeAir(boolean placeAir);
|
||||
|
||||
Builder tags(Set<String> tags);
|
||||
Builder tags(@Nullable Set<String> tags);
|
||||
|
||||
CustomBlockComponents build();
|
||||
}
|
||||
|
|
|
@ -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<String> 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<String> tags);
|
||||
|
||||
CustomItemData build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -239,6 +239,9 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
|||
@Override
|
||||
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
||||
|
||||
@Override
|
||||
Builder tags(@Nullable Set<String> tags);
|
||||
|
||||
NonVanillaCustomItemData build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> 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<String> 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<String> 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<String> 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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> tags) {
|
||||
return (NonVanillaCustomItemData.Builder) super.tags(tags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder identifier(@NonNull String identifier) {
|
||||
this.identifier = identifier;
|
||||
|
|
|
@ -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<NbtMap> 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<NbtMap> speeds = new ArrayList<>();
|
||||
speeds.add(createTagBreakSpeed(speed, "wood", "pumpkin", "plant"));
|
||||
|
||||
return createDigger(speeds);
|
||||
}
|
||||
|
||||
public static NbtMap getPickaxeDigger(int speed, String toolTier) {
|
||||
List<NbtMap> 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<NbtMap> speeds = new ArrayList<>();
|
||||
speeds.add(createTagBreakSpeed(speed, "dirt", "sand", "gravel", "grass", "snow"));
|
||||
|
||||
return createDigger(speeds);
|
||||
}
|
||||
|
||||
public static NbtMap getSwordDigger(int speed) {
|
||||
List<NbtMap> 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<NbtMap> 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<NbtMap> 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);
|
||||
}
|
||||
}
|
|
@ -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<String> tags() {
|
||||
public @NonNull Set<String> 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<String> tags = new HashSet<>();
|
||||
protected Set<String> 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<String> tags) {
|
||||
this.tags.addAll(tags);
|
||||
public Builder tags(@Nullable Set<String> tags) {
|
||||
this.tags = Objects.requireNonNullElseGet(tags, Set::of);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -198,6 +198,12 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||
customItemData.renderOffsets(fromJsonNode(tmpNode));
|
||||
}
|
||||
|
||||
if (node.get("tags") instanceof ArrayNode tags) {
|
||||
Set<String> tagsSet = new ObjectOpenHashSet<>();
|
||||
tags.forEach(tag -> tagsSet.add(tag.asText()));
|
||||
customItemData.tags(tagsSet);
|
||||
}
|
||||
|
||||
return customItemData.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String> 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<String> tagList = (List<String>) 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) {
|
||||
|
|
Loading…
Reference in a new issue