From 7af9518d6668e38c88119c7bf29d402e3195f1f7 Mon Sep 17 00:00:00 2001 From: Eclipse Date: Fri, 10 Jan 2025 16:45:15 +0000 Subject: [PATCH] Implement predicate strategies --- .../item/custom/v2/CustomItemDefinition.java | 14 +++++-- .../v2/predicate/PredicateStrategy.java | 37 +++++++++++++++++++ .../custom/GeyserCustomItemDefinition.java | 26 +++++++++---- .../translator/item/CustomItemTranslator.java | 5 +++ 4 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 api/src/main/java/org/geysermc/geyser/api/item/custom/v2/predicate/PredicateStrategy.java diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemDefinition.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemDefinition.java index a34f25e827..170d827638 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemDefinition.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemDefinition.java @@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.item.custom.v2.predicate.CustomItemPredicate; +import org.geysermc.geyser.api.item.custom.v2.predicate.PredicateStrategy; import org.geysermc.geyser.api.util.Identifier; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -84,10 +85,15 @@ public interface CustomItemDefinition { } /** - * The predicates that have to match for this item to be used. These predicates are similar to the Java item model predicates. + * The predicates that have to match for this item definition to be used. These predicates are similar to the Java item model predicates. */ @NonNull List predicates(); + /** + * The predicate strategy to be used. Determines if one of, or all of the predicates have to pass for this item definition to be used. Defaults to {@link PredicateStrategy#AND}. + */ + PredicateStrategy predicateStrategy(); + /** * The priority of this definition. For all definitions for a single Java item model, definitions with a higher priority will be matched first. Defaults to 0. */ @@ -125,11 +131,13 @@ interface Builder { Builder displayName(String displayName); - Builder predicate(@NonNull CustomItemPredicate predicate); + Builder priority(int priority); Builder bedrockOptions(CustomItemBedrockOptions.@NonNull Builder options); - Builder priority(int priority); + Builder predicate(@NonNull CustomItemPredicate predicate); + + Builder predicateStrategy(@NonNull PredicateStrategy strategy); // TODO do we want a component(Type, Value) method instead? Builder components(@NonNull DataComponents components); diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/predicate/PredicateStrategy.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/predicate/PredicateStrategy.java new file mode 100644 index 0000000000..6168112bb5 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/predicate/PredicateStrategy.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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.v2.predicate; + +public enum PredicateStrategy { + /** + * Require all predicates to pass for the item to be used. + */ + AND, + /** + * Require only one of the predicates to pass for the item to be used. + */ + OR +} diff --git a/core/src/main/java/org/geysermc/geyser/item/custom/GeyserCustomItemDefinition.java b/core/src/main/java/org/geysermc/geyser/item/custom/GeyserCustomItemDefinition.java index b6c2128b23..08671658c5 100644 --- a/core/src/main/java/org/geysermc/geyser/item/custom/GeyserCustomItemDefinition.java +++ b/core/src/main/java/org/geysermc/geyser/item/custom/GeyserCustomItemDefinition.java @@ -29,6 +29,7 @@ import org.geysermc.geyser.api.item.custom.v2.CustomItemBedrockOptions; import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition; import org.geysermc.geyser.api.item.custom.v2.predicate.CustomItemPredicate; +import org.geysermc.geyser.api.item.custom.v2.predicate.PredicateStrategy; import org.geysermc.geyser.api.util.Identifier; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -37,15 +38,18 @@ import java.util.List; public record GeyserCustomItemDefinition(@NonNull Identifier bedrockIdentifier, String displayName, @NonNull Identifier model, @NonNull List predicates, + PredicateStrategy predicateStrategy, int priority, @NonNull CustomItemBedrockOptions bedrockOptions, @NonNull DataComponents components) implements CustomItemDefinition { public static class Builder implements CustomItemDefinition.Builder { private final Identifier bedrockIdentifier; private final Identifier model; private final List predicates = new ArrayList<>(); - private int priority = 0; + private String displayName; + private int priority = 0; private CustomItemBedrockOptions bedrockOptions = CustomItemBedrockOptions.builder().build(); + private PredicateStrategy predicateStrategy = PredicateStrategy.AND; private DataComponents components = new DataComponents(new HashMap<>()); public Builder(Identifier bedrockIdentifier, Identifier model) { @@ -60,12 +64,6 @@ public CustomItemDefinition.Builder displayName(String displayName) { return this; } - @Override - public CustomItemDefinition.Builder predicate(@NonNull CustomItemPredicate predicate) { - predicates.add(predicate); - return this; - } - @Override public CustomItemDefinition.Builder priority(int priority) { this.priority = priority; @@ -78,6 +76,18 @@ public CustomItemDefinition.Builder bedrockOptions(CustomItemBedrockOptions.@Non return this; } + @Override + public CustomItemDefinition.Builder predicate(@NonNull CustomItemPredicate predicate) { + predicates.add(predicate); + return this; + } + + @Override + public CustomItemDefinition.Builder predicateStrategy(@NonNull PredicateStrategy strategy) { + predicateStrategy = strategy; + return this; + } + @Override public CustomItemDefinition.Builder components(@NonNull DataComponents components) { this.components = components; @@ -86,7 +96,7 @@ public CustomItemDefinition.Builder components(@NonNull DataComponents component @Override public CustomItemDefinition build() { - return new GeyserCustomItemDefinition(bedrockIdentifier, displayName, model, List.copyOf(predicates), priority, bedrockOptions, components); + return new GeyserCustomItemDefinition(bedrockIdentifier, displayName, model, List.copyOf(predicates), predicateStrategy, priority, bedrockOptions, components); } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/item/CustomItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/item/CustomItemTranslator.java index 7306bf5895..9382beed4c 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/item/CustomItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/item/CustomItemTranslator.java @@ -31,6 +31,7 @@ import net.kyori.adventure.key.Key; import org.cloudburstmc.protocol.bedrock.data.TrimMaterial; import org.geysermc.geyser.api.item.custom.v2.predicate.CustomItemPredicate; +import org.geysermc.geyser.api.item.custom.v2.predicate.PredicateStrategy; import org.geysermc.geyser.item.custom.v2.predicate.ConditionPredicate; import org.geysermc.geyser.item.custom.v2.predicate.RangeDispatchPredicate; import org.geysermc.geyser.api.item.custom.v2.predicate.match.ChargeType; @@ -82,12 +83,16 @@ public static ItemDefinition getCustomItem(GeyserSession session, int stackSize, // Cache predicate values so they're not recalculated every time when there are multiple item definitions using the same predicates Object2BooleanMap calculatedPredicates = new Object2BooleanOpenHashMap<>(); for (GeyserCustomMappingData customMapping : customItems) { + boolean needsOnlyOneMatch = customMapping.definition().predicateStrategy() == PredicateStrategy.OR; boolean allMatch = true; + for (CustomItemPredicate predicate : customMapping.definition().predicates()) { boolean value = calculatedPredicates.computeIfAbsent(predicate, x -> predicateMatches(session, predicate, stackSize, components)); if (!value) { allMatch = false; break; + } else if (needsOnlyOneMatch) { + break; } } if (allMatch) {