diff --git a/BUILTIN.md b/BUILTIN.md index ad57c24..ecc747a 100644 --- a/BUILTIN.md +++ b/BUILTIN.md @@ -78,7 +78,6 @@ Checks if player has required operator level. "key": "" } ``` - Checks and returns value of player's statistic with success value of true if it's higher than 0. Should be used with other predicates. @@ -95,7 +94,7 @@ See https://minecraft.fandom.com/wiki/Statistics#Statistic_types_and_names } ``` -Checks using Vanilla (advancement/loot table) predicates. +Checks using Vanilla (advancement) predicates. See https://minecraft.fandom.com/wiki/Template:Nbt_inherit/conditions/entity @@ -130,13 +129,46 @@ Should be used with others for more specific checks ```json5 { "type": "placeholder", - // Placeholder value with arguments, without `%` + // Placeholder value with arguments, without `%`, for example "player:displayname" "placeholder": "...", // (Optional) Boolean, making it return raw value (if provides string, it won't be formatted). Defaults to false "raw": false } ``` -Checks if player has specified option set by permission mod. -Should be used with others for more specific checks -(see Comparator predicates). \ No newline at end of file +Returns value of provided placeholder, to be used with other comparators. + + +## Has (Entity/Player/World/Game Profile) (0.2.0+1.20.1 and newer) +```json5 +{ + // [X] needs to be replaced with entity, player, world, game_profile + "type": "has_[X]" +} +``` + +Checks if Entity/Player/World/Game Profile is present. + +## Starts With (0.2.0+1.20.1 and newer) +```json5 +{ + "type": "starts_with", + // A string (text in quotes), a number or predicate definition + "input": "", + "argument": "", +} +``` + +Checks if input starts with argument (converted to strings) + +## Ends With (0.2.0+1.20.1 and newer) +```json5 +{ + "type": "ends_with", + // A string (text in quotes), a number or predicate definition + "input": "", + "argument": "", +} +``` + +Checks if input starts with argument (converted to strings) diff --git a/build.gradle b/build.gradle index febb758..02a2100 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,7 @@ dependencies { modCompileOnly("net.fabricmc.fabric-api:fabric-api:${project.fabric_version}") modLocalRuntime("net.fabricmc.fabric-api:fabric-api:${project.fabric_version}") modLocalRuntime("eu.pb4:polymer-reg-sync-manipulator:0.0.1+1.19") - modCompileOnly("eu.pb4:placeholder-api:2.0.0-pre.1+1.19.2") + modCompileOnly("eu.pb4:placeholder-api:2.1.1+1.20") modCompileOnly("me.lucko:fabric-permissions-api:0.2-SNAPSHOT") // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. diff --git a/gradle.properties b/gradle.properties index c137048..d1c8ff3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,15 +3,15 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://fabricmc.net/use - minecraft_version=1.20-rc1 - yarn_mappings=1.20-rc1+build.2 + minecraft_version=1.20.1 + yarn_mappings=1.20.1+build.2 loader_version=0.14.21 #Fabric api - fabric_version=0.83.0+1.20 + fabric_version=0.83.0+1.20.1 # Mod Properties - mod_version = 0.1.2+1.20 + mod_version = 0.2.0+1.20.1 maven_group = eu.pb4 archives_base_name = predicate-api diff --git a/src/main/java/eu/pb4/predicate/api/BuiltinPredicates.java b/src/main/java/eu/pb4/predicate/api/BuiltinPredicates.java index df13786..f6ad1cd 100644 --- a/src/main/java/eu/pb4/predicate/api/BuiltinPredicates.java +++ b/src/main/java/eu/pb4/predicate/api/BuiltinPredicates.java @@ -57,6 +57,14 @@ public static MinecraftPredicate moreOrEqual(Object object, Object object2) { return new NumberPredicate.MoreEqual(object, object2); } + public static MinecraftPredicate startsWith(Object input, Object argument) { + return new StringPredicate.StartsWith(input, argument); + } + + public static MinecraftPredicate endsWith(Object input, Object argument) { + return new StringPredicate.EndsWith(input, argument); + } + public static MinecraftPredicate operatorLevel(int level) { return new OperatorPredicate(level); } @@ -65,6 +73,22 @@ public static MinecraftPredicate statistic(StatType type, T key) { return new StatisticPredicate(type, type.getRegistry().getId(key)); } + public static MinecraftPredicate hasWorld() { + return SimplePredicate.HAS_WORLD; + } + + public static MinecraftPredicate hasPlayer() { + return SimplePredicate.HAS_PLAYER; + } + + public static MinecraftPredicate hasGameProfile() { + return SimplePredicate.HAS_GAME_PROFILE; + } + + public static MinecraftPredicate hasEntity() { + return SimplePredicate.HAS_ENTITY; + } + public static MinecraftPredicate vanillaEntityPredicate(EntityPredicate predicate) { return new EntityPredicatePredicate(predicate); } diff --git a/src/main/java/eu/pb4/predicate/api/MinecraftPredicate.java b/src/main/java/eu/pb4/predicate/api/MinecraftPredicate.java index 6ddbfca..7bcca48 100644 --- a/src/main/java/eu/pb4/predicate/api/MinecraftPredicate.java +++ b/src/main/java/eu/pb4/predicate/api/MinecraftPredicate.java @@ -1,12 +1,23 @@ package eu.pb4.predicate.api; import com.mojang.serialization.MapCodec; -import eu.pb4.predicate.impl.predicates.generic.UnitPredicate; +import eu.pb4.predicate.impl.predicates.generic.ConstantUnitPredicate; +import eu.pb4.predicate.impl.predicates.generic.SimplePredicate; import net.minecraft.util.Identifier; +import java.util.function.Function; + public interface MinecraftPredicate { static MinecraftPredicate unit(Object valueA) { - return new UnitPredicate(valueA); + return new ConstantUnitPredicate(valueA); + } + + static MinecraftPredicate simple(Identifier identifier, Function> resultFunction) { + return new SimplePredicate(identifier, resultFunction); + } + + static MapCodec simpleCodec(Identifier identifier, Function> resultFunction) { + return simple(identifier, resultFunction).codec(); } PredicateResult test(PredicateContext context); diff --git a/src/main/java/eu/pb4/predicate/api/PredicateRegistry.java b/src/main/java/eu/pb4/predicate/api/PredicateRegistry.java index 0d58430..002978b 100644 --- a/src/main/java/eu/pb4/predicate/api/PredicateRegistry.java +++ b/src/main/java/eu/pb4/predicate/api/PredicateRegistry.java @@ -16,7 +16,7 @@ public final class PredicateRegistry { private static final Map> CODECS = new HashMap<>(); private static final Map, Identifier> CODEC_IDS = new HashMap<>(); - public static final Codec CODEC = new MapCodec.MapCodecCodec(new BaseCodec()); + public static final Codec CODEC = new MapCodec.MapCodecCodec<>(new BaseCodec()); private PredicateRegistry() {} diff --git a/src/main/java/eu/pb4/predicate/impl/PredicatesInit.java b/src/main/java/eu/pb4/predicate/impl/PredicatesInit.java index 8fec399..41e4c02 100644 --- a/src/main/java/eu/pb4/predicate/impl/PredicatesInit.java +++ b/src/main/java/eu/pb4/predicate/impl/PredicatesInit.java @@ -1,5 +1,8 @@ package eu.pb4.predicate.impl; +import com.mojang.serialization.MapCodec; +import eu.pb4.predicate.api.MinecraftPredicate; +import eu.pb4.predicate.api.PredicateRegistry; import eu.pb4.predicate.impl.predicates.compat.CompatStatus; import eu.pb4.predicate.impl.predicates.compat.PermissionOptionPredicate; import eu.pb4.predicate.impl.predicates.compat.PermissionPredicate; @@ -8,12 +11,11 @@ import eu.pb4.predicate.impl.predicates.player.OperatorPredicate; import eu.pb4.predicate.impl.predicates.player.StatisticPredicate; import eu.pb4.predicate.impl.predicates.player.EntityPredicatePredicate; +import net.minecraft.util.Identifier; -import static eu.pb4.predicate.api.PredicateRegistry.register; public class PredicatesInit { public static void initialize() { - register(EqualityPredicate.ID, EqualityPredicate.CODEC); register(NegatePredicate.ID, NegatePredicate.CODEC); register(AnyPredicate.ID, AnyPredicate.CODEC); @@ -22,11 +24,18 @@ public static void initialize() { register(NumberPredicate.LessEqual.ID, NumberPredicate.LessEqual.CODEC); register(NumberPredicate.MoreThan.ID, NumberPredicate.MoreThan.CODEC); register(NumberPredicate.MoreEqual.ID, NumberPredicate.MoreEqual.CODEC); + register(StringPredicate.StartsWith.ID, StringPredicate.StartsWith.CODEC); + register(StringPredicate.EndsWith.ID, StringPredicate.EndsWith.CODEC); + register(StringPredicate.Join.ID, StringPredicate.Join.CODEC); register(OperatorPredicate.ID, OperatorPredicate.CODEC); register(StatisticPredicate.ID, StatisticPredicate.CODEC); register(EntityPredicatePredicate.ID, EntityPredicatePredicate.CODEC); + register(SimplePredicate.HAS_ENTITY); + register(SimplePredicate.HAS_PLAYER); + register(SimplePredicate.HAS_WORLD); + register(SimplePredicate.HAS_GAME_PROFILE); if (CompatStatus.PLACEHOLDER_API) { register(PlaceholderPredicate.ID, PlaceholderPredicate.CODEC); @@ -37,4 +46,12 @@ public static void initialize() { register(PermissionOptionPredicate.ID, PermissionOptionPredicate.CODEC); } } + + public static void register(MinecraftPredicate predicate) { + register(predicate.identifier(), predicate.codec()); + } + + public static void register(Identifier identifier, MapCodec codec) { + PredicateRegistry.register(identifier, codec); + } } diff --git a/src/main/java/eu/pb4/predicate/impl/predicates/GenericObject.java b/src/main/java/eu/pb4/predicate/impl/predicates/GenericObject.java index 5477fd1..2b9b80e 100644 --- a/src/main/java/eu/pb4/predicate/impl/predicates/GenericObject.java +++ b/src/main/java/eu/pb4/predicate/impl/predicates/GenericObject.java @@ -10,7 +10,7 @@ public final class GenericObject { public static final Codec CODEC = (Codec) (Object) Codec.either(PredicateRegistry.CODEC, Codec.either(Codec.STRING, Codec.DOUBLE)); public static MinecraftPredicate toPredicate(Object valueA) { - if (valueA instanceof Either either) { + if (valueA instanceof Either either) { if (either.left().isPresent()) { return toPredicate(either.left().get()); } else { @@ -38,4 +38,19 @@ public static double toNumber(Object value, boolean bool) { } return bool ? 1 : 0; } + + public static String toString(Object value) { + try { + if (value instanceof Text text) { + return text.getString(); + } else if (value instanceof String string) { + return string; + } else { + return value.toString(); + } + } catch (Throwable e) { + + } + return ""; + } } diff --git a/src/main/java/eu/pb4/predicate/impl/predicates/generic/UnitPredicate.java b/src/main/java/eu/pb4/predicate/impl/predicates/generic/ConstantUnitPredicate.java similarity index 80% rename from src/main/java/eu/pb4/predicate/impl/predicates/generic/UnitPredicate.java rename to src/main/java/eu/pb4/predicate/impl/predicates/generic/ConstantUnitPredicate.java index 64448d6..212930b 100644 --- a/src/main/java/eu/pb4/predicate/impl/predicates/generic/UnitPredicate.java +++ b/src/main/java/eu/pb4/predicate/impl/predicates/generic/ConstantUnitPredicate.java @@ -7,10 +7,10 @@ import eu.pb4.predicate.api.PredicateResult; import net.minecraft.util.Identifier; -public final class UnitPredicate extends AbstractPredicate { +public final class ConstantUnitPredicate extends AbstractPredicate { private final PredicateResult value; - public UnitPredicate(Object value) { + public ConstantUnitPredicate(Object value) { super(new Identifier("unit"), MapCodec.unit(null)); this.value = PredicateResult.ofNullable(value); diff --git a/src/main/java/eu/pb4/predicate/impl/predicates/generic/SimplePredicate.java b/src/main/java/eu/pb4/predicate/impl/predicates/generic/SimplePredicate.java new file mode 100644 index 0000000..091afbc --- /dev/null +++ b/src/main/java/eu/pb4/predicate/impl/predicates/generic/SimplePredicate.java @@ -0,0 +1,40 @@ +package eu.pb4.predicate.impl.predicates.generic; + +import com.mojang.serialization.MapCodec; +import eu.pb4.predicate.api.AbstractPredicate; +import eu.pb4.predicate.api.MinecraftPredicate; +import eu.pb4.predicate.api.PredicateContext; +import eu.pb4.predicate.api.PredicateResult; +import net.minecraft.util.Identifier; +import org.apache.commons.lang3.mutable.MutableObject; + +import java.util.function.Function; +import java.util.function.Predicate; + +public final class SimplePredicate extends AbstractPredicate { + public static final MinecraftPredicate HAS_PLAYER = new SimplePredicate(new Identifier("has_player"), PredicateContext::hasPlayer); + public static final MinecraftPredicate HAS_ENTITY = new SimplePredicate(new Identifier("has_entity"), PredicateContext::hasEntity); + public static final MinecraftPredicate HAS_WORLD = new SimplePredicate(new Identifier("has_world"), PredicateContext::hasWorld); + public static final MinecraftPredicate HAS_GAME_PROFILE = new SimplePredicate(new Identifier("has_game_profile"), PredicateContext::hasGameProfile); + + private final Function> function; + + public SimplePredicate(Identifier identifier, Predicate function) { + this(identifier, (x) -> PredicateResult.ofBoolean(function.test(x)), new MutableObject<>()); + } + + public SimplePredicate(Identifier identifier, Function> function) { + this(identifier, function, new MutableObject<>()); + } + + private SimplePredicate(Identifier identifier, Function> function, MutableObject self) { + super(identifier, MapCodec.unit(self::getValue)); + this.function = function; + } + + @Override + public PredicateResult test(PredicateContext context) { + return function.apply(context); + } + +} diff --git a/src/main/java/eu/pb4/predicate/impl/predicates/generic/StringPredicate.java b/src/main/java/eu/pb4/predicate/impl/predicates/generic/StringPredicate.java new file mode 100644 index 0000000..fc3b2e6 --- /dev/null +++ b/src/main/java/eu/pb4/predicate/impl/predicates/generic/StringPredicate.java @@ -0,0 +1,96 @@ +package eu.pb4.predicate.impl.predicates.generic; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import eu.pb4.predicate.api.AbstractPredicate; +import eu.pb4.predicate.api.MinecraftPredicate; +import eu.pb4.predicate.api.PredicateContext; +import eu.pb4.predicate.api.PredicateResult; +import eu.pb4.predicate.impl.predicates.GenericObject; +import net.minecraft.util.Identifier; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; + +public abstract class StringPredicate extends AbstractPredicate { + private final Object value; + private final Object argument; + private final MinecraftPredicate valuePredicate; + private final MinecraftPredicate argumentPredicate; + + public StringPredicate(Object value, Object argument, Identifier identifier, MapCodec codec) { + super(identifier, codec); + this.value = value; + this.argument = argument; + + this.valuePredicate = GenericObject.toPredicate(value); + this.argumentPredicate = GenericObject.toPredicate(argument); + } + + public static MapCodec codec(BiFunction creator) { + return RecordCodecBuilder.mapCodec(instance -> instance.group( + GenericObject.CODEC.fieldOf("input").forGetter(StringPredicate::input), + GenericObject.CODEC.fieldOf("argument").forGetter(StringPredicate::argument) + ).apply(instance, creator)); + } + + public final Object input() { + return this.value; + } + + public final Object argument() { + return this.argument; + } + + @Override + public PredicateResult test(PredicateContext context) { + String a = GenericObject.toString(this.valuePredicate.test(context)); + String b = GenericObject.toString(this.argumentPredicate.test(context)); + + return this.testString(a, b); + } + + protected abstract PredicateResult testString(String a, String b); + + public static final class StartsWith extends StringPredicate { + public static final Identifier ID = new Identifier("starts_with"); + public static final MapCodec CODEC = StringPredicate.codec(StartsWith::new); + + public StartsWith(Object value, Object argument) { + super(value, argument, ID, CODEC); + } + + @Override + protected PredicateResult testString(String a, String b) { + return PredicateResult.ofBoolean(a.startsWith(b)); + } + } + + public static final class EndsWith extends StringPredicate { + public static final Identifier ID = new Identifier("ends_with"); + public static final MapCodec CODEC = StringPredicate.codec(EndsWith::new); + + public EndsWith(Object value, Object argument) { + super(value, argument, ID, CODEC); + } + + @Override + protected PredicateResult testString(String a, String b) { + return PredicateResult.ofBoolean(a.endsWith(b)); + } + } + + public static final class Join extends StringPredicate { + public static final Identifier ID = new Identifier("join_string"); + public static final MapCodec CODEC = StringPredicate.codec(Join::new); + + public Join(Object value, Object argument) { + super(value, argument, ID, CODEC); + } + + @Override + protected PredicateResult testString(String a, String b) { + return PredicateResult.ofSuccess(a + b); + } + } +} diff --git a/src/main/java/eu/pb4/predicate/impl/predicates/player/EntityPredicatePredicate.java b/src/main/java/eu/pb4/predicate/impl/predicates/player/EntityPredicatePredicate.java index 781e7f1..dfa1bc7 100644 --- a/src/main/java/eu/pb4/predicate/impl/predicates/player/EntityPredicatePredicate.java +++ b/src/main/java/eu/pb4/predicate/impl/predicates/player/EntityPredicatePredicate.java @@ -9,12 +9,13 @@ import eu.pb4.predicate.api.PredicateResult; import net.minecraft.predicate.entity.EntityPredicate; import net.minecraft.util.Identifier; +import net.minecraft.util.dynamic.Codecs; public class EntityPredicatePredicate extends AbstractPredicate { public static final Identifier ID = new Identifier("entity"); public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - EntityPredicateCodec.INSTANCE.fieldOf("value").forGetter(EntityPredicatePredicate::predicate) + Codecs.JSON_ELEMENT.xmap(EntityPredicate::fromJson, EntityPredicate::toJson).fieldOf("value").forGetter(EntityPredicatePredicate::predicate) ).apply(instance, EntityPredicatePredicate::new)); private final EntityPredicate entityPredicate; @@ -34,25 +35,4 @@ public PredicateResult test(PredicateContext context) { } return PredicateResult.ofFailure(); } - - private static class EntityPredicateCodec implements Codec { - private static final EntityPredicateCodec INSTANCE = new EntityPredicateCodec(); - - @Override - public DataResult> decode(DynamicOps ops, T input) { - return DataResult.success(new Pair<>( - EntityPredicate.fromJson(input instanceof JsonElement jsonElement - ? jsonElement - : ops.convertTo(JsonOps.INSTANCE, input) - ), input)); - } - - @Override - public DataResult encode(EntityPredicate input, DynamicOps ops, T prefix) { - var encoded= ops instanceof JsonOps ? (T) input.toJson() : JsonOps.INSTANCE.convertTo(ops, input.toJson()); - - return ops.getMap(encoded).flatMap(map -> ops.mergeToMap(prefix, map)); - } - } - }