From 5dd71b8a73f29100cde253f78d1ee89bbca1f7c2 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Thu, 28 Aug 2025 19:28:12 -0700 Subject: [PATCH 01/39] basic implementations for Contains and Named properties --- src/main/java/ch/njol/skript/Skript.java | 14 +- .../ch/njol/skript/classes/ClassInfo.java | 33 +++- .../skript/classes/data/BukkitClasses.java | 34 +++- .../njol/skript/classes/data/JavaClasses.java | 14 ++ .../skript/classes/data/SkriptClasses.java | 28 ++- .../ch/njol/skript/lang/ExpressionList.java | 7 +- .../ch/njol/skript/registrations/Classes.java | 14 +- .../lang/properties/PropCondContains.java | 105 ++++++++++ .../skript/lang/properties/PropExprName.java | 32 +++ .../skript/lang/properties/Property.java | 91 +++++++++ .../properties/PropertyBaseExpression.java | 186 ++++++++++++++++++ .../skript/lang/properties/PropertyInfo.java | 4 + .../lang/properties/PropertyRegistry.java | 68 +++++++ .../skript/lang/properties/PropertyUtils.java | 87 ++++++++ 14 files changed, 703 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/PropExprName.java create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/Property.java create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index d44ee843f79..5562c88d4f6 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -107,6 +107,8 @@ import org.skriptlang.skript.lang.converter.Converters; import org.skriptlang.skript.lang.entry.EntryValidator; import org.skriptlang.skript.lang.experiment.ExperimentRegistry; +import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.PropertyRegistry; import org.skriptlang.skript.lang.script.Script; import org.skriptlang.skript.lang.structure.Structure; import org.skriptlang.skript.lang.structure.StructureInfo; @@ -370,6 +372,12 @@ public static ExperimentRegistry experiments() { return experimentRegistry; } + + private static PropertyRegistry propertyRegistry; + public static PropertyRegistry getPropertyRegistry() { + return propertyRegistry; + } + /** * @return The folder containing all Scripts. */ @@ -498,6 +506,9 @@ public void onEnable() { experimentRegistry = new ExperimentRegistry(this); Feature.registerAll(getAddonInstance(), experimentRegistry); + propertyRegistry = new PropertyRegistry(this); + Property.registerDefaultProperties(); + // Load classes which are always safe to use new JavaClasses(); // These may be needed in configuration @@ -523,7 +534,7 @@ public void onEnable() { if (pauseThreshold > -1) { Skript.warning("Minecraft server pausing is enabled!"); Skript.warning("Scripts that interact with the world or entities may not work as intended when the server is paused and may crash your server."); - Skript.warning("Consider setting 'pause-when-empty-seconds' to -1 in server.properties to make sure you don't encounter any issues."); + Skript.warning("Consider setting 'pause-when-empty-seconds' to -1 in server.propertyRegistry to make sure you don't encounter any issues."); } } @@ -577,6 +588,7 @@ public void onEnable() { getAddonInstance().loadClasses("ch.njol.skript", "conditions", "effects", "events", "expressions", "entity", "sections", "structures"); getAddonInstance().loadClasses("org.skriptlang.skript.bukkit", "misc"); + getAddonInstance().loadClasses("org.skriptlang.skript.lang", "properties"); // todo: become proper module once registry api is merged FishingModule.load(); BreedingModule.load(); diff --git a/src/main/java/ch/njol/skript/classes/ClassInfo.java b/src/main/java/ch/njol/skript/classes/ClassInfo.java index 41380de7b41..60188ee3ae4 100644 --- a/src/main/java/ch/njol/skript/classes/ClassInfo.java +++ b/src/main/java/ch/njol/skript/classes/ClassInfo.java @@ -6,16 +6,16 @@ import ch.njol.skript.lang.DefaultExpression; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.skript.localization.Noun; +import ch.njol.skript.registrations.Classes; import ch.njol.util.coll.iterator.ArrayIterator; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.PropertyInfo; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; +import java.util.*; import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -476,4 +476,29 @@ public String toString(final @Nullable Event event, final boolean debug) { return getName().getSingular(); } + + private final Map, PropertyInfo> propertyInfos = new HashMap<>(); + + public ClassInfo property(Property property, @NotNull Handler handler) { + if (propertyInfos.containsKey(property)) { + throw new IllegalStateException("Property " + property.name() + " is already registered for the " + c.getName() + " type."); + } + propertyInfos.put(property, new PropertyInfo<>(property, handler)); + Classes.CLASS_INFOS_BY_PROPERTY.computeIfAbsent(property, k -> new ArrayList<>()).add(this); + return this; + } + + public boolean hasProperty(Property property) { + return propertyInfos.containsKey(property); + } + + public @Nullable PropertyInfo getPropertyInfo(Property property) { + if (!propertyInfos.containsKey(property)) { + return null; + } + //noinspection unchecked + return (PropertyInfo) propertyInfos.get(property); + } + + } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index d860e5dc169..ccfcd187af4 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -60,7 +60,10 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.CachedServerIcon; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.Property.NameHandler; import java.io.StreamCorruptedException; import java.util.*; @@ -580,7 +583,23 @@ public String getDebugMessage(final Inventory i) { public String toVariableNameString(final Inventory i) { return "inventory of " + Classes.toString(i.getHolder(), StringMode.VARIABLE_NAME); } - }).changer(DefaultChangers.inventoryChanger)); + }).changer(DefaultChangers.inventoryChanger) + .property(Property.CONTAINS, new Property.ContainsHandler() { + @Override + public boolean contains(Inventory container, Object element) { + if (element instanceof ItemType type) { + return type.isContainedIn(container); + } else if (element instanceof ItemStack stack) { + return container.containsAtLeast(stack, stack.getAmount()); + } + return false; + } + + @Override + public Class[] elementTypes() { + return new Class[]{ItemType.class, ItemStack.class}; + } + })); Classes.registerClass(new EnumClassInfo<>(InventoryAction.class, "inventoryaction", "inventory actions") .user("inventory ?actions?") @@ -679,7 +698,18 @@ public String getDebugMessage(final Player p) { } }) .changer(DefaultChangers.playerChanger) - .serializeAs(OfflinePlayer.class)); + .serializeAs(OfflinePlayer.class) + .property(Property.NAME, new NameHandler() { + @Override + public String name(Player player) { + return player.getName(); + } + + @Override + public @NotNull Class returnType() { + return String.class; + } + })); Classes.registerClass(new ClassInfo<>(OfflinePlayer.class, "offlineplayer") .user("offline ?players?") diff --git a/src/main/java/ch/njol/skript/classes/data/JavaClasses.java b/src/main/java/ch/njol/skript/classes/data/JavaClasses.java index d2011548fd9..8b88f7d04e4 100644 --- a/src/main/java/ch/njol/skript/classes/data/JavaClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/JavaClasses.java @@ -18,6 +18,8 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; +import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.Property.ContainsHandler; import java.io.StreamCorruptedException; import java.util.UUID; @@ -301,6 +303,18 @@ public String deserialize(String s) { public boolean mustSyncDeserialization() { return false; } + }) + .property(Property.CONTAINS, new ContainsHandler() { + @Override + public boolean contains(String container, String element) { + return StringUtils.contains(container, element, SkriptConfig.caseSensitive.value()); + } + + @Override + public Class[] elementTypes() { + //noinspection unchecked + return new Class[]{String.class}; + } })); // joml type - for display entities diff --git a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java index 22539ac0187..d0fb8a83b32 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -28,7 +28,9 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.properties.Property; import org.skriptlang.skript.lang.script.Script; import org.skriptlang.skript.lang.util.SkriptQueue; import org.skriptlang.skript.util.Executable; @@ -217,7 +219,31 @@ public String toVariableNameString(final ItemType t) { } }) .cloner(ItemType::clone) - .serializer(new YggdrasilSerializer<>())); + .serializer(new YggdrasilSerializer<>()) + .property(Property.NAME, new Property.NameHandler() { + @Override + public String name(ItemType itemType) { + return itemType.name(); + } + + @Override + public Class @Nullable [] acceptChange(Changer.ChangeMode mode) { + if (mode == Changer.ChangeMode.SET || mode == Changer.ChangeMode.RESET) + return new Class[] {String.class}; + return null; + } + + @Override + public void change(ItemType itemType, Object @Nullable [] delta, Changer.ChangeMode mode) { + String name = delta != null ? (String) delta[0] : null; + itemType.setName(name); + } + + @Override + public @NotNull Class returnType() { + return String.class; + } + })); Classes.registerClass(new ClassInfo<>(Time.class, "time") .user("times?") diff --git a/src/main/java/ch/njol/skript/lang/ExpressionList.java b/src/main/java/ch/njol/skript/lang/ExpressionList.java index 7de4cc0d877..34a419dd0d9 100644 --- a/src/main/java/ch/njol/skript/lang/ExpressionList.java +++ b/src/main/java/ch/njol/skript/lang/ExpressionList.java @@ -149,13 +149,14 @@ public boolean check(Event event, Predicate checker) { @SuppressWarnings("unchecked") public @Nullable Expression getConvertedExpression(Class... to) { Expression[] exprs = new Expression[expressions.length]; - Class[] returnTypes = new Class[expressions.length]; + Set> possibleReturnTypeSet = new HashSet<>(); for (int i = 0; i < exprs.length; i++) { if ((exprs[i] = expressions[i].getConvertedExpression(to)) == null) return null; - returnTypes[i] = exprs[i].getReturnType(); + possibleReturnTypeSet.addAll(Arrays.asList(exprs[i].possibleReturnTypes())); } - return new ExpressionList<>(exprs, (Class) Classes.getSuperClassInfo(returnTypes).getC(), returnTypes, and, this); + Class[] possibleReturnTypes = possibleReturnTypeSet.toArray(new Class[0]); + return new ExpressionList<>(exprs, (Class) Classes.getSuperClassInfo(possibleReturnTypes).getC(), possibleReturnTypes, and, this); } @Override diff --git a/src/main/java/ch/njol/skript/registrations/Classes.java b/src/main/java/ch/njol/skript/registrations/Classes.java index 75352ef7bf7..b4bf809b87c 100644 --- a/src/main/java/ch/njol/skript/registrations/Classes.java +++ b/src/main/java/ch/njol/skript/registrations/Classes.java @@ -29,12 +29,11 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Chunk; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; +import org.jetbrains.annotations.*; import org.skriptlang.skript.lang.converter.Converter; import org.skriptlang.skript.lang.converter.ConverterInfo; import org.skriptlang.skript.lang.converter.Converters; +import org.skriptlang.skript.lang.properties.Property; import java.io.*; import java.lang.reflect.Array; @@ -345,6 +344,15 @@ public static List> getAllSuperClassInfos(Class c) { return list; } + + @ApiStatus.Internal + public static final Map, List>> CLASS_INFOS_BY_PROPERTY = new HashMap<>(); + + public static @NotNull List> getClassInfosByProperty(@NotNull Property property) { + return CLASS_INFOS_BY_PROPERTY.getOrDefault(property, Collections.emptyList()); + } + + /** * Gets a class by its code name * diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java b/src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java new file mode 100644 index 00000000000..4995e585792 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java @@ -0,0 +1,105 @@ +package org.skriptlang.skript.lang.properties; + +import ch.njol.skript.Skript; +import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.comparator.Comparators; +import org.skriptlang.skript.lang.comparator.Relation; +import org.skriptlang.skript.lang.properties.Property.ContainsHandler; +import org.skriptlang.skript.lang.properties.PropertyUtils.PropertyMap; + +public class PropCondContains extends Condition { + + static { + Skript.registerCondition(PropCondContains.class, + "property %inventories% (has|have) %itemtypes% [in [(the[ir]|his|her|its)] inventory]", + "property %inventories% (doesn't|does not|do not|don't) have %itemtypes% [in [(the[ir]|his|her|its)] inventory]", + "property %inventories/strings/objects% contain[(1¦s)] %itemtypes/strings/objects%", + "property %inventories/strings/objects% (doesn't|does not|do not|don't) contain %itemtypes/strings/objects%"); + } + + private Expression haystack; + private Expression needles; + private PropertyMap> properties; + + boolean explicitSingle = false; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + + this.haystack = PropertyUtils.asProperty(Property.CONTAINS, expressions[0]); + if (haystack == null) { + Skript.error("The expression " + expressions[0] + " returns types that do not contain anything."); + return false; + } + // determine if the expression truly has a name property + + properties = PropertyUtils.getPossiblePropertyInfos(Property.CONTAINS, haystack); + if (properties.isEmpty()) { + Skript.error("The expression " + haystack + " returns types that do not contain anything."); + return false; // no name property found + } + // determine possible return types +// elementTypes = getElementTypes(properties); + this.needles = expressions[1]; + explicitSingle = matchedPattern == 2 && parseResult.mark != 1 || haystack.isSingle(); + + needles = expressions[1]; + return true; + } + + private Class[][] getElementTypes(PropertyMap> properties) { + return properties.values().stream() + .map((propertyInfo) -> propertyInfo.handler().elementTypes()) + .toArray(Class[][]::new); + } + + @Override + public boolean check(Event event) { + Object[] haystacks = haystack.getAll(event); + boolean haystackAnd = haystack.getAnd(); + Object[] needles = this.needles.getAll(event); + boolean needlesAnd = this.needles.getAnd(); + if (haystacks.length == 0) { + return isNegated(); + } + + // We should compare the contents of the haystacks to the needles + if (explicitSingle) { + // use properties + return SimpleExpression.check(haystacks, (haystack) -> { + // for each haystack, determine property + //noinspection unchecked + var handler = (ContainsHandler) properties.getHandler(haystack.getClass()); + if (handler == null) { + return false; + } + // if found, use it to check against needles + return SimpleExpression.check(needles, (needle) -> + handler.canContain(needle.getClass()) + && handler.contains(haystack, needle), + false, needlesAnd); + }, isNegated(), haystackAnd); + + // compare the haystacks themselves to the needles + } else { + return this.needles.check(event, o1 -> { + for (Object o2 : haystacks) { + if (Comparators.compare(o1, o2) == Relation.EQUAL) + return true; + } + return false; + }, isNegated()); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "x contains y"; + } +} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropExprName.java b/src/main/java/org/skriptlang/skript/lang/properties/PropExprName.java new file mode 100644 index 00000000000..140ce4a7b93 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropExprName.java @@ -0,0 +1,32 @@ +package org.skriptlang.skript.lang.properties; + +import ch.njol.skript.Skript; +import ch.njol.skript.lang.ExpressionType; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.properties.Property.NameHandler; + +public class PropExprName extends PropertyBaseExpression> { + + static { + // Register the expression with Skript + Skript.registerExpression(PropExprName.class, Object.class, ExpressionType.PROPERTY, "[the] property name[s] of %objects%"); + } + + @Override + Property> getProperty() { + return Property.NAME; + } + + @Override + @SuppressWarnings("unchecked") + protected @Nullable Object convert(Event event, NameHandler handler, T source) { + return ((NameHandler) handler).name(source); + } + + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "name property of x"; + } +} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/Property.java b/src/main/java/org/skriptlang/skript/lang/properties/Property.java new file mode 100644 index 00000000000..6aecc8aea9a --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -0,0 +1,91 @@ +package org.skriptlang.skript.lang.properties; + + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.addon.SkriptAddon; + +import java.util.Locale; + +public record Property( + String name, + SkriptAddon provider, + @NotNull Class handler +) { + public Property(@NotNull String name, SkriptAddon provider, @NotNull Class handler) { + this.name = name.toLowerCase(Locale.ENGLISH); + this.provider = provider; + this.handler = handler; + } + + public static Property of( + @NotNull String name, + @NotNull SkriptAddon provider, + @NotNull Class handler) { + return new Property<>(name, provider, handler); + } + + @SuppressWarnings("unchecked") + public static Class toHandlerType(Class rawClass) { + return (Class) rawClass; + } + + public static final Property> NAME = Property.of( + "name", + Skript.instance(), + toHandlerType(NameHandler.class)); + + public static final Property> CONTAINS = Property.of( + "contains", + Skript.instance(), + toHandlerType(ContainsHandler.class)); + +// public static final Property AMOUNT = new Property("amount", Skript.getAddonInstance()); +// @SuppressWarnings("unchecked") +// public static final Property> CONTAINS = new Property<>("contains", Skript.getAddonInstance(), (Class>) ContainsHandler.class); +// public static final Property VALUED = new Property("valued", Skript.getAddonInstance()); + + public static void registerDefaultProperties() { + // Register default propertyRegistry here + // Example: PropertyRegistry.getInstance().register(new Property("example", SkriptAddon.getInstance(), ExampleExpression.class)); + // This method can be called during addon initialization to ensure default propertyRegistry are registered. + PropertyRegistry propertyRegistry = Skript.getPropertyRegistry(); + propertyRegistry.register(NAME); + propertyRegistry.register(CONTAINS); + } + + public interface ExpressionPropertyHandler { + // Handler for the NAME property + ReturnType name(Type named); + default Class @Nullable [] acceptChange(ChangeMode mode) { + return null; + } + default void change(Type named, Object @Nullable [] delta, ChangeMode mode) { + throw new UnsupportedOperationException("Changing the name is not supported for this property."); + } + @NotNull Class returnType(); + } + + /** + * no returning arrays + * @param + * @param + */ + public interface NameHandler extends ExpressionPropertyHandler { } + + public interface ContainsHandler { + boolean contains(Container container, Element element); + Class[] elementTypes(); + default boolean canContain(Class type) { + for (Class elementType : elementTypes()) { + if (elementType.isAssignableFrom(type)) { + return true; + } + } + return false; + } + } + +} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java new file mode 100644 index 00000000000..881ed0a7f6b --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java @@ -0,0 +1,186 @@ +package org.skriptlang.skript.lang.properties; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.expressions.base.PropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.skript.util.Utils; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.properties.Property.ExpressionPropertyHandler; +import org.skriptlang.skript.lang.properties.PropertyUtils.PropertyMap; + +import java.lang.reflect.Array; +import java.util.*; +import java.util.function.Function; + +public abstract class PropertyBaseExpression> extends SimpleExpression { + + abstract Property getProperty(); + + protected static void register(Class> expressionClass, String property) { + Skript.registerExpression(expressionClass, Object.class, ExpressionType.PROPERTY, PropertyExpression.getPatterns(property, "objects")); + } + + private Expression expr; + private PropertyMap properties; + private Class[] returnTypes; + private Class returnType; + private final Property property = getProperty(); + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + this.expr = PropertyUtils.asProperty(property, expressions[0]); + if (expr == null) { + Skript.error("The expression " + expressions[0] + " returns types that do not have a name."); + return false; + } + + // get all possible property infos for the expression's return types + properties = PropertyUtils.getPossiblePropertyInfos(property, expr); + if (properties.isEmpty()) { + Skript.error("The expression " + expr + " returns types that do not have a name."); + return false; // no name property found + } + + // determine possible return types + returnTypes = getPropertyReturnTypes(properties, Handler::returnType); + returnType = Utils.getSuperType(returnTypes); + return true; + } + + private Class @NotNull [] getPropertyReturnTypes(@NotNull PropertyMap properties, Function> getReturnType) { + return properties.values().stream() + .map((propertyInfo) -> getReturnType.apply(propertyInfo.handler())) + .filter(type -> type != Object.class) + .toArray(Class[]::new); + } + + @Override + protected Object @Nullable [] get(Event event) { + return expr.stream(event) + .map(source -> { + var handler = properties.getHandler(source.getClass()); + if (handler == null) { + return null; // no property info found, skip + } + return convert(event, handler, source); + }) + .filter(Objects::nonNull) + .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); + } + + protected void preConvert(Event event) {} + + protected abstract @Nullable Object convert(Event event, Handler handler, T source); + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + Set> allowedChangeTypes = new HashSet<>(); + for (PropertyInfo propertyInfo : properties.values()) { + Class[] types = propertyInfo.handler().acceptChange(mode); + changeDetails.storeTypes(mode, propertyInfo, types); + if (types != null) { + if (mode == ChangeMode.DELETE || mode == ChangeMode.RESET) { + // if we are deleting or resetting, we can accept any type + return new Class[0]; + } else { + allowedChangeTypes.addAll(Arrays.asList(types)); + } + } + } + if (allowedChangeTypes.isEmpty()) { + return null; // no types accepted + } + return allowedChangeTypes.toArray(new Class[0]); + } + + private final ChangeDetails changeDetails = new ChangeDetails(); + + class ChangeDetails extends EnumMap, Class[]>> { + + public ChangeDetails() { + super(ChangeMode.class); + } + + public void storeTypes(ChangeMode mode, PropertyInfo propertyInfo, Class[] types) { + Map, Class[]> map = computeIfAbsent(mode, k -> new HashMap<>()); + map.put(propertyInfo, types); + } + + public Class[] getTypes(ChangeMode mode, PropertyInfo propertyInfo) { + Map, Class[]> map = get(mode); + if (map != null) { + return map.get(propertyInfo); + } + return null; // no types found for this mode and property info + } + + } + + + // TOOD: + // Track which property handlers accept which change modes and which classes + // so the change method is safe + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + for (Object nameHaver : expr.getArray(event)) { + PropertyInfo propertyInfo; + // check if we don't already know the right info for this class + if (properties.containsKey(nameHaver.getClass())) { + propertyInfo = properties.get(nameHaver.getClass()); + } else { + // search for assignable property info + propertyInfo = properties.lookupPropertyInfo(nameHaver.getClass()); + } + if (propertyInfo == null) { + continue; // no property info found, skip + } + + // check against allowed change types + Class[] allowedTypes = changeDetails.getTypes(mode, propertyInfo); + if (allowedTypes == null) + continue; // no types accepted for this mode and property info + + if (allowedTypes.length == 0 && !(mode == ChangeMode.DELETE || mode == ChangeMode.RESET)) { + continue; // not deleting or resetting, and no types accepted + } + + for (Class allowedType : allowedTypes) { + // array type, compare to delta + // single type, compare to delta[0] + if ((allowedType.isArray() && allowedType.isInstance(delta)) + || (delta != null && allowedType.isInstance(delta[0]))) { + // if the nameHaver is allowed, change + @SuppressWarnings("unchecked") + var handler = (Property.NameHandler) propertyInfo.handler(); + handler.change(nameHaver, delta, mode); + } + // if allowed type is singular, take delta[0] + } + + // no matching types, go next + } + } + + @Override + public boolean isSingle() { + return expr.isSingle(); + } + + @Override + public Class getReturnType() { + return returnType; + } + + @Override + public Class[] possibleReturnTypes() { + return returnTypes; + } +} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java new file mode 100644 index 00000000000..0fee5813704 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java @@ -0,0 +1,4 @@ +package org.skriptlang.skript.lang.properties; + +public record PropertyInfo(Property property, Handler handler) { +} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java new file mode 100644 index 00000000000..5f4c4705f83 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java @@ -0,0 +1,68 @@ +package org.skriptlang.skript.lang.properties; + +import ch.njol.skript.Skript; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.util.Registry; + +import java.util.Collection; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; + +public class PropertyRegistry implements Registry { + + private final Map properties; + private final Skript skript; + + public PropertyRegistry(Skript skript) { + this.skript = skript; + this.properties = new java.util.HashMap<>(); + } + + public boolean register(@NotNull Property property) { + String name = property.name(); + if (properties.containsKey(name)) { + Skript.error("Property '" + name + "' is already registered by " + properties.get(name).provider().name() + "."); + return false; // Property already registered + } + properties.put(name, property); + Skript.debug("Registered property '" + name + "' provided by " + property.provider().name() + "."); + return true; + } + + public boolean unregister(@NotNull Property property) { + String name = property.name(); + return unregister(name); + } + + public boolean unregister(String name) { + name = name.toLowerCase(Locale.ENGLISH); + if (!properties.containsKey(name)) { + Skript.error("Property '" + name + "' is not registered and cannot be unregistered."); + return false; // Property not registered + } + properties.remove(name); + Skript.debug("Unregistered property '" + name + "'."); + return true; + } + + @Override + public @Unmodifiable Collection elements() { + return Collections.unmodifiableCollection(properties.values()); + } + + public Property get(String name) { + return properties.get(name); + } + + public boolean isRegistered(@NotNull Property property) { + return isRegistered(property.name()); + } + + public boolean isRegistered(@NotNull String name) { + return properties.containsKey(name.toLowerCase(Locale.ENGLISH)); + } + + +} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java new file mode 100644 index 00000000000..083c98c803c --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java @@ -0,0 +1,87 @@ +package org.skriptlang.skript.lang.properties; + +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.registrations.Classes; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.List; + +public class PropertyUtils { + + + + + public static class PropertyMap extends HashMap, PropertyInfo> { + public @Nullable Handler getHandler(Class inputClass) { + PropertyInfo propertyInfo; + // check if we don't already know the right info for this class + if (containsKey(inputClass)) { + propertyInfo = get(inputClass); + } else { + // search for assignable property info + propertyInfo = lookupPropertyInfo(inputClass); + } + if (propertyInfo == null) { + // no property info found, return null + return null; + } + // get the name using the property handler + return propertyInfo.handler(); + } + + public PropertyInfo lookupPropertyInfo(Class actualClass) { + Class closestClass = null; + for (Class candidateClass : keySet()) { + // need to make sure we get the closest match + if (candidateClass.isAssignableFrom(actualClass)) { + if (closestClass == null || closestClass.isAssignableFrom(candidateClass)) { + closestClass = candidateClass; + } + } + } + + var propertyInfo = get(closestClass); + // add to properties so we don't have to search again + put(actualClass, propertyInfo); + return propertyInfo; + } + + } + + public static Expression asProperty(Property property, Expression expr) { + if (expr == null) { + return null; // no expression to convert + } + + // get all types with a name property + List> namedClassInfos = Classes.getClassInfosByProperty(property); + Class[] namedClasses = namedClassInfos.stream().map(ClassInfo::getC).toArray(Class[]::new); + + //noinspection unchecked,rawtypes + return expr.getConvertedExpression((Class[]) namedClasses); + } + + + public static PropertyMap getPossiblePropertyInfos( + Property property, + Expression expr + ) { + PropertyMap propertyInfos = new PropertyMap<>(); + // for each return type, check if it has a name property + for (Class returnType : expr.possibleReturnTypes()) { + ClassInfo classInfo = Classes.getSuperClassInfo(returnType); + // get property + var propertyInfo = classInfo.getPropertyInfo(property); + if (propertyInfo == null) { + continue; // no name property + } + propertyInfos.put(classInfo.getC(), propertyInfo); + } + return propertyInfos; + } + + + +} From cbc9107c77d19122da725b6e94854e701cb291ce Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Thu, 28 Aug 2025 20:32:23 -0700 Subject: [PATCH 02/39] fix rogue find+replace --- src/main/java/ch/njol/skript/Skript.java | 2 +- .../java/org/skriptlang/skript/lang/properties/Property.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 5562c88d4f6..ca06b0290f6 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -534,7 +534,7 @@ public void onEnable() { if (pauseThreshold > -1) { Skript.warning("Minecraft server pausing is enabled!"); Skript.warning("Scripts that interact with the world or entities may not work as intended when the server is paused and may crash your server."); - Skript.warning("Consider setting 'pause-when-empty-seconds' to -1 in server.propertyRegistry to make sure you don't encounter any issues."); + Skript.warning("Consider setting 'pause-when-empty-seconds' to -1 in server.properties to make sure you don't encounter any issues."); } } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/Property.java b/src/main/java/org/skriptlang/skript/lang/properties/Property.java index 6aecc8aea9a..fe2534481da 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/Property.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -58,7 +58,6 @@ public static void registerDefaultProperties() { public interface ExpressionPropertyHandler { // Handler for the NAME property - ReturnType name(Type named); default Class @Nullable [] acceptChange(ChangeMode mode) { return null; } @@ -73,7 +72,9 @@ default void change(Type named, Object @Nullable [] delta, ChangeMode mode) { * @param * @param */ - public interface NameHandler extends ExpressionPropertyHandler { } + public interface NameHandler extends ExpressionPropertyHandler { + Name name(Named named); + } public interface ContainsHandler { boolean contains(Container container, Element element); From 7c90f01abbd159488adfc3aa2d63364eb4180d45 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Thu, 28 Aug 2025 20:52:44 -0700 Subject: [PATCH 03/39] cleanup and move lookupPropertyInfo to override of get(Class) --- .../properties/PropertyBaseExpression.java | 19 +++++-------------- .../skript/lang/properties/PropertyUtils.java | 15 +++++++-------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java index 881ed0a7f6b..67970eab493 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java @@ -75,8 +75,6 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); } - protected void preConvert(Event event) {} - protected abstract @Nullable Object convert(Event event, Handler handler, T source); @Override @@ -130,15 +128,8 @@ public Class[] getTypes(ChangeMode mode, PropertyInfo propertyInfo) @Override public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { - for (Object nameHaver : expr.getArray(event)) { - PropertyInfo propertyInfo; - // check if we don't already know the right info for this class - if (properties.containsKey(nameHaver.getClass())) { - propertyInfo = properties.get(nameHaver.getClass()); - } else { - // search for assignable property info - propertyInfo = properties.lookupPropertyInfo(nameHaver.getClass()); - } + for (Object propertyHaver : expr.getArray(event)) { + PropertyInfo propertyInfo = properties.get(propertyHaver.getClass()); if (propertyInfo == null) { continue; // no property info found, skip } @@ -157,10 +148,10 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { // single type, compare to delta[0] if ((allowedType.isArray() && allowedType.isInstance(delta)) || (delta != null && allowedType.isInstance(delta[0]))) { - // if the nameHaver is allowed, change + // if the propertyHaver is allowed, change @SuppressWarnings("unchecked") - var handler = (Property.NameHandler) propertyInfo.handler(); - handler.change(nameHaver, delta, mode); + var handler = (ExpressionPropertyHandler) propertyInfo.handler(); + handler.change(propertyHaver, delta, mode); } // if allowed type is singular, take delta[0] } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java index 083c98c803c..ad14df0ad96 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java @@ -17,12 +17,7 @@ public static class PropertyMap extends HashMap, PropertyInfo< public @Nullable Handler getHandler(Class inputClass) { PropertyInfo propertyInfo; // check if we don't already know the right info for this class - if (containsKey(inputClass)) { - propertyInfo = get(inputClass); - } else { - // search for assignable property info - propertyInfo = lookupPropertyInfo(inputClass); - } + propertyInfo = get(inputClass); if (propertyInfo == null) { // no property info found, return null return null; @@ -31,7 +26,11 @@ public static class PropertyMap extends HashMap, PropertyInfo< return propertyInfo.handler(); } - public PropertyInfo lookupPropertyInfo(Class actualClass) { + public PropertyInfo get(Class actualClass) { + if (super.containsKey(actualClass)) { + return super.get(actualClass); + } + Class closestClass = null; for (Class candidateClass : keySet()) { // need to make sure we get the closest match @@ -42,7 +41,7 @@ public PropertyInfo lookupPropertyInfo(Class actualClass) { } } - var propertyInfo = get(closestClass); + var propertyInfo = super.get(closestClass); // add to properties so we don't have to search again put(actualClass, propertyInfo); return propertyInfo; From 51604665a1f63dcc13104143df65ae4a4a53d5f8 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Fri, 29 Aug 2025 17:07:57 -0400 Subject: [PATCH 04/39] Remove necessary usage of toHandlerType --- .../skript/lang/properties/Property.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/skriptlang/skript/lang/properties/Property.java b/src/main/java/org/skriptlang/skript/lang/properties/Property.java index fe2534481da..62c1aea78d8 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/Property.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -20,27 +20,23 @@ public Property(@NotNull String name, SkriptAddon provider, @NotNull Class Property of( + public static Property of( @NotNull String name, @NotNull SkriptAddon provider, - @NotNull Class handler) { - return new Property<>(name, provider, handler); - } - - @SuppressWarnings("unchecked") - public static Class toHandlerType(Class rawClass) { - return (Class) rawClass; + @NotNull Class handler) { + //noinspection unchecked + return (Property) new Property<>(name, provider, handler); } public static final Property> NAME = Property.of( "name", Skript.instance(), - toHandlerType(NameHandler.class)); + NameHandler.class); public static final Property> CONTAINS = Property.of( "contains", Skript.instance(), - toHandlerType(ContainsHandler.class)); + ContainsHandler.class); // public static final Property AMOUNT = new Property("amount", Skript.getAddonInstance()); // @SuppressWarnings("unchecked") From 85122a31fb1fd0457bab7f5069362beb9acd66b5 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Fri, 29 Aug 2025 15:43:19 -0700 Subject: [PATCH 05/39] add base handler class --- src/main/java/ch/njol/skript/classes/ClassInfo.java | 4 ++-- .../skriptlang/skript/lang/properties/Property.java | 11 +++++++---- .../skript/lang/properties/PropertyInfo.java | 2 +- .../skript/lang/properties/PropertyUtils.java | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/ch/njol/skript/classes/ClassInfo.java b/src/main/java/ch/njol/skript/classes/ClassInfo.java index 60188ee3ae4..249813f9709 100644 --- a/src/main/java/ch/njol/skript/classes/ClassInfo.java +++ b/src/main/java/ch/njol/skript/classes/ClassInfo.java @@ -479,7 +479,7 @@ public String toString(final @Nullable Event event, final boolean debug) { private final Map, PropertyInfo> propertyInfos = new HashMap<>(); - public ClassInfo property(Property property, @NotNull Handler handler) { + public > ClassInfo property(Property property, @NotNull Handler handler) { if (propertyInfos.containsKey(property)) { throw new IllegalStateException("Property " + property.name() + " is already registered for the " + c.getName() + " type."); } @@ -492,7 +492,7 @@ public boolean hasProperty(Property property) { return propertyInfos.containsKey(property); } - public @Nullable PropertyInfo getPropertyInfo(Property property) { + public > @Nullable PropertyInfo getPropertyInfo(Property property) { if (!propertyInfos.containsKey(property)) { return null; } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/Property.java b/src/main/java/org/skriptlang/skript/lang/properties/Property.java index 62c1aea78d8..52c3cd6cae9 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/Property.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -9,7 +9,7 @@ import java.util.Locale; -public record Property( +public record Property>( String name, SkriptAddon provider, @NotNull Class handler @@ -20,7 +20,7 @@ public Property(@NotNull String name, SkriptAddon provider, @NotNull Class Property of( + public static , Handler extends HandlerClass> Property of( @NotNull String name, @NotNull SkriptAddon provider, @NotNull Class handler) { @@ -52,7 +52,10 @@ public static void registerDefaultProperties() { propertyRegistry.register(CONTAINS); } - public interface ExpressionPropertyHandler { + @SuppressWarnings("unused") + public interface PropertyHandler {} + + public interface ExpressionPropertyHandler extends PropertyHandler { // Handler for the NAME property default Class @Nullable [] acceptChange(ChangeMode mode) { return null; @@ -72,7 +75,7 @@ public interface NameHandler extends ExpressionPropertyHandler { + public interface ContainsHandler extends PropertyHandler { boolean contains(Container container, Element element); Class[] elementTypes(); default boolean canContain(Class type) { diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java index 0fee5813704..acfd7e25524 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java @@ -1,4 +1,4 @@ package org.skriptlang.skript.lang.properties; -public record PropertyInfo(Property property, Handler handler) { +public record PropertyInfo>(Property property, Handler handler) { } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java index ad14df0ad96..24feb24cf2a 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java @@ -13,7 +13,7 @@ public class PropertyUtils { - public static class PropertyMap extends HashMap, PropertyInfo> { + public static class PropertyMap> extends HashMap, PropertyInfo> { public @Nullable Handler getHandler(Class inputClass) { PropertyInfo propertyInfo; // check if we don't already know the right info for this class @@ -63,7 +63,7 @@ public static Expression asProperty(Property property, Expression expr) } - public static PropertyMap getPossiblePropertyInfos( + public static > PropertyMap getPossiblePropertyInfos( Property property, Expression expr ) { From 1afb9770468bafcff60554ad0a96bb2d147f05e6 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 30 Aug 2025 17:50:57 -0700 Subject: [PATCH 06/39] Improve property resolution, add back-compat for AnyNamed, AnyContains, start parse-time checks for Contains --- .../java/ch/njol/skript/classes/AnyInfo.java | 1 + .../skript/classes/data/SkriptClasses.java | 49 +++++++++++++++++-- .../skript/lang/util/common/AnyAmount.java | 1 + .../skript/lang/util/common/AnyContains.java | 1 + .../skript/lang/util/common/AnyNamed.java | 1 + .../skript/lang/util/common/AnyProvider.java | 1 + .../skript/lang/util/common/AnyValued.java | 2 +- .../lang/properties/PropCondContains.java | 35 ++++++++++--- .../properties/PropertyBaseExpression.java | 3 +- .../skript/lang/properties/PropertyUtils.java | 37 +++++++++++--- 10 files changed, 112 insertions(+), 19 deletions(-) diff --git a/src/main/java/ch/njol/skript/classes/AnyInfo.java b/src/main/java/ch/njol/skript/classes/AnyInfo.java index 283e0c5189b..50d73389931 100644 --- a/src/main/java/ch/njol/skript/classes/AnyInfo.java +++ b/src/main/java/ch/njol/skript/classes/AnyInfo.java @@ -13,6 +13,7 @@ * * @see AnyProvider */ +@Deprecated(since="INSERT VERSION") public class AnyInfo extends ClassInfo { /** diff --git a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java index d0fb8a83b32..767d08bd0ef 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -7,6 +7,7 @@ import ch.njol.skript.aliases.ItemType; import ch.njol.skript.bukkitutil.ItemUtils; import ch.njol.skript.classes.*; +import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.config.Config; import ch.njol.skript.config.Node; import ch.njol.skript.expressions.base.EventValueExpression; @@ -227,14 +228,14 @@ public String name(ItemType itemType) { } @Override - public Class @Nullable [] acceptChange(Changer.ChangeMode mode) { - if (mode == Changer.ChangeMode.SET || mode == Changer.ChangeMode.RESET) + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.RESET) return new Class[] {String.class}; return null; } @Override - public void change(ItemType itemType, Object @Nullable [] delta, Changer.ChangeMode mode) { + public void change(ItemType itemType, Object @Nullable [] delta, ChangeMode mode) { String name = delta != null ? (String) delta[0] : null; itemType.setName(name); } @@ -958,14 +959,43 @@ public String toVariableNameString(DynamicFunctionReference function) { } })); + //noinspection deprecation Classes.registerClass(new AnyInfo<>(AnyNamed.class, "named") .name("Any Named Thing") .description("Something that has a name (e.g. an item).") .usage("") .examples("{thing}'s name") .since("2.10") + .property(Property.NAME, new Property.NameHandler() { + + @Override + public @NotNull Class returnType() { + return String.class; + } + + @Override + public String name(AnyNamed anyNamed) { + return anyNamed.name(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET) + return new Class[] {String.class}; + return null; + } + + @Override + public void change(AnyNamed named, Object @Nullable [] delta, ChangeMode mode) { + if (mode == ChangeMode.SET && named.supportsNameChange()) { + assert delta != null; + named.setName((String) delta[0]); + } + } + }) ); + //noinspection deprecation Classes.registerClass(new AnyInfo<>(AnyAmount.class, "numbered") .name("Any Numbered/Sized Thing") .description("Something that has an amount or size.") @@ -974,6 +1004,7 @@ public String toVariableNameString(DynamicFunctionReference function) { .since("2.10") ); + //noinspection deprecation Classes.registerClass(new AnyInfo<>(AnyValued.class, "valued") .name("Any Valued Thing") .description("Something that has a value.") @@ -982,6 +1013,7 @@ public String toVariableNameString(DynamicFunctionReference function) { .since("2.10") ); + //noinspection deprecation Classes.registerClass(new AnyInfo<>(AnyContains.class, "containing") .user("any container") .name("Anything with Contents") @@ -989,6 +1021,17 @@ public String toVariableNameString(DynamicFunctionReference function) { .usage("") .examples("{a} contains {b}") .since("2.10") + .property(Property.CONTAINS, new Property.ContainsHandler() { + @Override + public boolean contains(AnyContains anyContains, Object object) { + return anyContains.checkSafely(object); + } + + @Override + public Class[] elementTypes() { + return new Class[]{Object.class}; + } + }) ); } diff --git a/src/main/java/ch/njol/skript/lang/util/common/AnyAmount.java b/src/main/java/ch/njol/skript/lang/util/common/AnyAmount.java index 9232f45826e..2c6501556c7 100644 --- a/src/main/java/ch/njol/skript/lang/util/common/AnyAmount.java +++ b/src/main/java/ch/njol/skript/lang/util/common/AnyAmount.java @@ -10,6 +10,7 @@ * @see AnyProvider */ @FunctionalInterface +@Deprecated(since="INSERT VERSION") public interface AnyAmount extends AnyProvider { /** diff --git a/src/main/java/ch/njol/skript/lang/util/common/AnyContains.java b/src/main/java/ch/njol/skript/lang/util/common/AnyContains.java index 2e9473a240d..a83b08119c7 100644 --- a/src/main/java/ch/njol/skript/lang/util/common/AnyContains.java +++ b/src/main/java/ch/njol/skript/lang/util/common/AnyContains.java @@ -19,6 +19,7 @@ * @see AnyProvider */ @FunctionalInterface +@Deprecated(since="INSERT VERSION") public interface AnyContains extends AnyProvider { /** diff --git a/src/main/java/ch/njol/skript/lang/util/common/AnyNamed.java b/src/main/java/ch/njol/skript/lang/util/common/AnyNamed.java index 4da763d229d..43a057ad111 100644 --- a/src/main/java/ch/njol/skript/lang/util/common/AnyNamed.java +++ b/src/main/java/ch/njol/skript/lang/util/common/AnyNamed.java @@ -10,6 +10,7 @@ * @see AnyProvider */ @FunctionalInterface +@Deprecated(since="INSERT VERSION") public interface AnyNamed extends AnyProvider { /** diff --git a/src/main/java/ch/njol/skript/lang/util/common/AnyProvider.java b/src/main/java/ch/njol/skript/lang/util/common/AnyProvider.java index 110a96ce810..a12bd4d12f5 100644 --- a/src/main/java/ch/njol/skript/lang/util/common/AnyProvider.java +++ b/src/main/java/ch/njol/skript/lang/util/common/AnyProvider.java @@ -23,6 +23,7 @@ * may conflict between things that provide two values (e.g. something declaring * both a name and a size) */ +@Deprecated(since="INSERT VERSION") public interface AnyProvider { } diff --git a/src/main/java/ch/njol/skript/lang/util/common/AnyValued.java b/src/main/java/ch/njol/skript/lang/util/common/AnyValued.java index 93c36715c19..a2d1c4a8ca3 100644 --- a/src/main/java/ch/njol/skript/lang/util/common/AnyValued.java +++ b/src/main/java/ch/njol/skript/lang/util/common/AnyValued.java @@ -4,7 +4,6 @@ import ch.njol.skript.lang.ParseContext; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.StringMode; -import com.sun.jdi.request.StepRequest; import org.jetbrains.annotations.UnknownNullability; import org.skriptlang.skript.lang.converter.Converters; @@ -15,6 +14,7 @@ * * @see AnyProvider */ +@Deprecated(since="INSERT VERSION") public interface AnyValued extends AnyProvider { /** diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java b/src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java index 4995e585792..d44d35cbdf8 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java @@ -5,6 +5,8 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.skript.registrations.Classes; +import ch.njol.skript.util.LiteralUtils; import ch.njol.util.Kleenean; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -31,7 +33,6 @@ public class PropCondContains extends Condition { @Override public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - this.haystack = PropertyUtils.asProperty(Property.CONTAINS, expressions[0]); if (haystack == null) { Skript.error("The expression " + expressions[0] + " returns types that do not contain anything."); @@ -44,13 +45,35 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is Skript.error("The expression " + haystack + " returns types that do not contain anything."); return false; // no name property found } - // determine possible return types -// elementTypes = getElementTypes(properties); - this.needles = expressions[1]; + + this.needles = LiteralUtils.defendExpression(expressions[1]); explicitSingle = matchedPattern == 2 && parseResult.mark != 1 || haystack.isSingle(); - needles = expressions[1]; - return true; + if (explicitSingle) { + // determine possible needle types + Class[][] elementTypes = getElementTypes(properties); + var needleReturnTypes = needles.possibleReturnTypes(); + // if no needle types are compatible with the element types, error + if (!determineTypeCompatibility(needleReturnTypes, elementTypes)) { + Skript.error("'" + haystack + "' cannot contain " + Classes.toString(needleReturnTypes, false)); + return false; + } + } + + return LiteralUtils.canInitSafely(haystack, needles); + } + + private static boolean determineTypeCompatibility(Class[] needleReturnTypes, Class[][] elementTypes) { + for (Class needleType : needleReturnTypes) { + for (Class[] haystackType : elementTypes) { + for (Class allowedNeedleType : haystackType) { + if (allowedNeedleType.isAssignableFrom(needleType)) { + return true; + } + } + } + } + return false; } private Class[][] getElementTypes(PropertyMap> properties) { diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java index 67970eab493..45a2ad6bb46 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java @@ -7,6 +7,7 @@ import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.skript.util.LiteralUtils; import ch.njol.skript.util.Utils; import ch.njol.util.Kleenean; import org.bukkit.event.Event; @@ -51,7 +52,7 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is // determine possible return types returnTypes = getPropertyReturnTypes(properties, Handler::returnType); returnType = Utils.getSuperType(returnTypes); - return true; + return LiteralUtils.canInitSafely(expr); } private Class @NotNull [] getPropertyReturnTypes(@NotNull PropertyMap properties, Function> getReturnType) { diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java index 24feb24cf2a..7530318d4fd 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java @@ -3,6 +3,7 @@ import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.lang.Expression; import ch.njol.skript.registrations.Classes; +import ch.njol.skript.util.LiteralUtils; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -55,11 +56,11 @@ public static Expression asProperty(Property property, Expression expr) } // get all types with a name property - List> namedClassInfos = Classes.getClassInfosByProperty(property); - Class[] namedClasses = namedClassInfos.stream().map(ClassInfo::getC).toArray(Class[]::new); + List> classInfos = Classes.getClassInfosByProperty(property); + Class[] classes = classInfos.stream().map(ClassInfo::getC).toArray(Class[]::new); //noinspection unchecked,rawtypes - return expr.getConvertedExpression((Class[]) namedClasses); + return LiteralUtils.defendExpression(expr).getConvertedExpression((Class[]) classes); } @@ -68,15 +69,35 @@ public static > PropertyMap Expression expr ) { PropertyMap propertyInfos = new PropertyMap<>(); - // for each return type, check if it has a name property + + // get all types with a name property + List> classInfos = Classes.getClassInfosByProperty(property); + + // for each return type, match to a classinfo w/ name property for (Class returnType : expr.possibleReturnTypes()) { - ClassInfo classInfo = Classes.getSuperClassInfo(returnType); - // get property - var propertyInfo = classInfo.getPropertyInfo(property); - if (propertyInfo == null) { + ClassInfo closestInfo = null; + for (ClassInfo propertiedClassInfo : classInfos) { + if (propertiedClassInfo.getC() == returnType) { + // exact match, use it + closestInfo = propertiedClassInfo; + break; + } + if (propertiedClassInfo.getC().isAssignableFrom(returnType)) { + // closest match so far + if (closestInfo == null || closestInfo.getC().isAssignableFrom(propertiedClassInfo.getC())) { + closestInfo = propertiedClassInfo; + } + } + } + if (closestInfo == null) { continue; // no name property } + + // get property + var propertyInfo = closestInfo.getPropertyInfo(property); + ClassInfo classInfo = Classes.getSuperClassInfo(returnType); propertyInfos.put(classInfo.getC(), propertyInfo); + propertyInfos.put(closestInfo.getC(), propertyInfo); } return propertyInfos; } From 50a5314de724ec3f1c85b8d4b8eba435ca41a4c2 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 30 Aug 2025 18:10:37 -0700 Subject: [PATCH 07/39] cleaning and reorganization --- src/main/java/ch/njol/skript/Skript.java | 8 +--- .../ch/njol/skript/classes/ClassInfo.java | 12 ++--- .../skript/classes/data/BukkitClasses.java | 7 +-- .../njol/skript/classes/data/JavaClasses.java | 2 +- .../skript/classes/data/SkriptClasses.java | 8 ++-- .../conditions}/PropCondContains.java | 6 ++- .../expressions}/PropExprName.java | 8 ++-- .../skript/lang/properties/Property.java | 44 ++---------------- .../properties/PropertyBaseExpression.java | 24 +++++----- .../lang/properties/PropertyHandler.java | 46 +++++++++++++++++++ .../skript/lang/properties/PropertyInfo.java | 4 -- .../lang/properties/PropertyRegistry.java | 15 +++--- .../skript/lang/properties/PropertyUtils.java | 8 ++-- 13 files changed, 100 insertions(+), 92 deletions(-) rename src/main/java/org/skriptlang/skript/{lang/properties => common/conditions}/PropCondContains.java (94%) rename src/main/java/org/skriptlang/skript/{lang/properties => common/expressions}/PropExprName.java (71%) create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java delete mode 100644 src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index ca06b0290f6..09b8a644562 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -372,12 +372,6 @@ public static ExperimentRegistry experiments() { return experimentRegistry; } - - private static PropertyRegistry propertyRegistry; - public static PropertyRegistry getPropertyRegistry() { - return propertyRegistry; - } - /** * @return The folder containing all Scripts. */ @@ -506,7 +500,7 @@ public void onEnable() { experimentRegistry = new ExperimentRegistry(this); Feature.registerAll(getAddonInstance(), experimentRegistry); - propertyRegistry = new PropertyRegistry(this); + getAddonInstance().storeRegistry(PropertyRegistry.class, new PropertyRegistry(this)); Property.registerDefaultProperties(); // Load classes which are always safe to use diff --git a/src/main/java/ch/njol/skript/classes/ClassInfo.java b/src/main/java/ch/njol/skript/classes/ClassInfo.java index 249813f9709..e109307f689 100644 --- a/src/main/java/ch/njol/skript/classes/ClassInfo.java +++ b/src/main/java/ch/njol/skript/classes/ClassInfo.java @@ -13,7 +13,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.properties.Property; -import org.skriptlang.skript.lang.properties.PropertyInfo; +import org.skriptlang.skript.lang.properties.PropertyHandler; import java.util.*; import java.util.function.Supplier; @@ -477,13 +477,13 @@ public String toString(final @Nullable Event event, final boolean debug) { } - private final Map, PropertyInfo> propertyInfos = new HashMap<>(); + private final Map, Property.PropertyInfo> propertyInfos = new HashMap<>(); - public > ClassInfo property(Property property, @NotNull Handler handler) { + public > ClassInfo property(Property property, @NotNull Handler handler) { if (propertyInfos.containsKey(property)) { throw new IllegalStateException("Property " + property.name() + " is already registered for the " + c.getName() + " type."); } - propertyInfos.put(property, new PropertyInfo<>(property, handler)); + propertyInfos.put(property, new Property.PropertyInfo<>(property, handler)); Classes.CLASS_INFOS_BY_PROPERTY.computeIfAbsent(property, k -> new ArrayList<>()).add(this); return this; } @@ -492,12 +492,12 @@ public boolean hasProperty(Property property) { return propertyInfos.containsKey(property); } - public > @Nullable PropertyInfo getPropertyInfo(Property property) { + public > @Nullable Property.PropertyInfo getPropertyInfo(Property property) { if (!propertyInfos.containsKey(property)) { return null; } //noinspection unchecked - return (PropertyInfo) propertyInfos.get(property); + return (Property.PropertyInfo) propertyInfos.get(property); } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index ccfcd187af4..42377d9cd3d 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -63,7 +63,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.properties.Property; -import org.skriptlang.skript.lang.properties.Property.NameHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.ContainsHandler; import java.io.StreamCorruptedException; import java.util.*; @@ -584,7 +585,7 @@ public String toVariableNameString(final Inventory i) { return "inventory of " + Classes.toString(i.getHolder(), StringMode.VARIABLE_NAME); } }).changer(DefaultChangers.inventoryChanger) - .property(Property.CONTAINS, new Property.ContainsHandler() { + .property(Property.CONTAINS, new ContainsHandler() { @Override public boolean contains(Inventory container, Object element) { if (element instanceof ItemType type) { @@ -699,7 +700,7 @@ public String getDebugMessage(final Player p) { }) .changer(DefaultChangers.playerChanger) .serializeAs(OfflinePlayer.class) - .property(Property.NAME, new NameHandler() { + .property(Property.NAME, new PropertyHandler.NameHandler() { @Override public String name(Player player) { return player.getName(); diff --git a/src/main/java/ch/njol/skript/classes/data/JavaClasses.java b/src/main/java/ch/njol/skript/classes/data/JavaClasses.java index 8b88f7d04e4..8b9a552e297 100644 --- a/src/main/java/ch/njol/skript/classes/data/JavaClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/JavaClasses.java @@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.skriptlang.skript.lang.properties.Property; -import org.skriptlang.skript.lang.properties.Property.ContainsHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.ContainsHandler; import java.io.StreamCorruptedException; import java.util.UUID; diff --git a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java index 767d08bd0ef..662d493b13a 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -31,7 +31,9 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.properties.PropertyHandler; import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.PropertyHandler.ContainsHandler; import org.skriptlang.skript.lang.script.Script; import org.skriptlang.skript.lang.util.SkriptQueue; import org.skriptlang.skript.util.Executable; @@ -221,7 +223,7 @@ public String toVariableNameString(final ItemType t) { }) .cloner(ItemType::clone) .serializer(new YggdrasilSerializer<>()) - .property(Property.NAME, new Property.NameHandler() { + .property(Property.NAME, new PropertyHandler.NameHandler() { @Override public String name(ItemType itemType) { return itemType.name(); @@ -966,7 +968,7 @@ public String toVariableNameString(DynamicFunctionReference function) { .usage("") .examples("{thing}'s name") .since("2.10") - .property(Property.NAME, new Property.NameHandler() { + .property(Property.NAME, new PropertyHandler.NameHandler() { @Override public @NotNull Class returnType() { @@ -1021,7 +1023,7 @@ public void change(AnyNamed named, Object @Nullable [] delta, ChangeMode mode) { .usage("") .examples("{a} contains {b}") .since("2.10") - .property(Property.CONTAINS, new Property.ContainsHandler() { + .property(Property.CONTAINS, new ContainsHandler() { @Override public boolean contains(AnyContains anyContains, Object object) { return anyContains.checkSafely(object); diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java b/src/main/java/org/skriptlang/skript/common/conditions/PropCondContains.java similarity index 94% rename from src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java rename to src/main/java/org/skriptlang/skript/common/conditions/PropCondContains.java index d44d35cbdf8..97c8aa51a11 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropCondContains.java +++ b/src/main/java/org/skriptlang/skript/common/conditions/PropCondContains.java @@ -1,4 +1,4 @@ -package org.skriptlang.skript.lang.properties; +package org.skriptlang.skript.common.conditions; import ch.njol.skript.Skript; import ch.njol.skript.lang.Condition; @@ -12,7 +12,9 @@ import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.comparator.Comparators; import org.skriptlang.skript.lang.comparator.Relation; -import org.skriptlang.skript.lang.properties.Property.ContainsHandler; +import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.PropertyHandler.ContainsHandler; +import org.skriptlang.skript.lang.properties.PropertyUtils; import org.skriptlang.skript.lang.properties.PropertyUtils.PropertyMap; public class PropCondContains extends Condition { diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropExprName.java b/src/main/java/org/skriptlang/skript/common/expressions/PropExprName.java similarity index 71% rename from src/main/java/org/skriptlang/skript/lang/properties/PropExprName.java rename to src/main/java/org/skriptlang/skript/common/expressions/PropExprName.java index 140ce4a7b93..1a1ca4c6e3d 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropExprName.java +++ b/src/main/java/org/skriptlang/skript/common/expressions/PropExprName.java @@ -1,10 +1,12 @@ -package org.skriptlang.skript.lang.properties; +package org.skriptlang.skript.common.expressions; import ch.njol.skript.Skript; import ch.njol.skript.lang.ExpressionType; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.lang.properties.Property.NameHandler; +import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.PropertyBaseExpression; +import org.skriptlang.skript.lang.properties.PropertyHandler.NameHandler; public class PropExprName extends PropertyBaseExpression> { @@ -14,7 +16,7 @@ public class PropExprName extends PropertyBaseExpression> { } @Override - Property> getProperty() { + public Property> getProperty() { return Property.NAME; } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/Property.java b/src/main/java/org/skriptlang/skript/lang/properties/Property.java index 52c3cd6cae9..c336842ee8c 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/Property.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -2,14 +2,14 @@ import ch.njol.skript.Skript; -import ch.njol.skript.classes.Changer.ChangeMode; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.lang.properties.PropertyHandler.ContainsHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.NameHandler; import java.util.Locale; -public record Property>( +public record Property>( String name, SkriptAddon provider, @NotNull Class handler @@ -47,45 +47,11 @@ public static void registerDefaultProperties() { // Register default propertyRegistry here // Example: PropertyRegistry.getInstance().register(new Property("example", SkriptAddon.getInstance(), ExampleExpression.class)); // This method can be called during addon initialization to ensure default propertyRegistry are registered. - PropertyRegistry propertyRegistry = Skript.getPropertyRegistry(); + PropertyRegistry propertyRegistry = Skript.getAddonInstance().registry(PropertyRegistry.class); propertyRegistry.register(NAME); propertyRegistry.register(CONTAINS); } - @SuppressWarnings("unused") - public interface PropertyHandler {} - - public interface ExpressionPropertyHandler extends PropertyHandler { - // Handler for the NAME property - default Class @Nullable [] acceptChange(ChangeMode mode) { - return null; - } - default void change(Type named, Object @Nullable [] delta, ChangeMode mode) { - throw new UnsupportedOperationException("Changing the name is not supported for this property."); - } - @NotNull Class returnType(); - } - - /** - * no returning arrays - * @param - * @param - */ - public interface NameHandler extends ExpressionPropertyHandler { - Name name(Named named); + public record PropertyInfo>(Property property, Handler handler) { } - - public interface ContainsHandler extends PropertyHandler { - boolean contains(Container container, Element element); - Class[] elementTypes(); - default boolean canContain(Class type) { - for (Class elementType : elementTypes()) { - if (elementType.isAssignableFrom(type)) { - return true; - } - } - return false; - } - } - } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java index 45a2ad6bb46..8ce83330f17 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java @@ -13,16 +13,13 @@ import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.lang.properties.Property.ExpressionPropertyHandler; import org.skriptlang.skript.lang.properties.PropertyUtils.PropertyMap; import java.lang.reflect.Array; import java.util.*; import java.util.function.Function; -public abstract class PropertyBaseExpression> extends SimpleExpression { - - abstract Property getProperty(); +public abstract class PropertyBaseExpression> extends SimpleExpression { protected static void register(Class> expressionClass, String property) { Skript.registerExpression(expressionClass, Object.class, ExpressionType.PROPERTY, PropertyExpression.getPatterns(property, "objects")); @@ -34,6 +31,9 @@ protected static void register(Class> expres private Class returnType; private final Property property = getProperty(); + + public abstract Property getProperty(); + @Override public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { this.expr = PropertyUtils.asProperty(property, expressions[0]); @@ -81,7 +81,7 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is @Override public Class @Nullable [] acceptChange(ChangeMode mode) { Set> allowedChangeTypes = new HashSet<>(); - for (PropertyInfo propertyInfo : properties.values()) { + for (Property.PropertyInfo propertyInfo : properties.values()) { Class[] types = propertyInfo.handler().acceptChange(mode); changeDetails.storeTypes(mode, propertyInfo, types); if (types != null) { @@ -101,19 +101,19 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is private final ChangeDetails changeDetails = new ChangeDetails(); - class ChangeDetails extends EnumMap, Class[]>> { + class ChangeDetails extends EnumMap, Class[]>> { public ChangeDetails() { super(ChangeMode.class); } - public void storeTypes(ChangeMode mode, PropertyInfo propertyInfo, Class[] types) { - Map, Class[]> map = computeIfAbsent(mode, k -> new HashMap<>()); + public void storeTypes(ChangeMode mode, Property.PropertyInfo propertyInfo, Class[] types) { + Map, Class[]> map = computeIfAbsent(mode, k -> new HashMap<>()); map.put(propertyInfo, types); } - public Class[] getTypes(ChangeMode mode, PropertyInfo propertyInfo) { - Map, Class[]> map = get(mode); + public Class[] getTypes(ChangeMode mode, Property.PropertyInfo propertyInfo) { + Map, Class[]> map = get(mode); if (map != null) { return map.get(propertyInfo); } @@ -130,7 +130,7 @@ public Class[] getTypes(ChangeMode mode, PropertyInfo propertyInfo) @Override public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { for (Object propertyHaver : expr.getArray(event)) { - PropertyInfo propertyInfo = properties.get(propertyHaver.getClass()); + Property.PropertyInfo propertyInfo = properties.get(propertyHaver.getClass()); if (propertyInfo == null) { continue; // no property info found, skip } @@ -151,7 +151,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { || (delta != null && allowedType.isInstance(delta[0]))) { // if the propertyHaver is allowed, change @SuppressWarnings("unchecked") - var handler = (ExpressionPropertyHandler) propertyInfo.handler(); + var handler = (PropertyHandler.ExpressionPropertyHandler) propertyInfo.handler(); handler.change(propertyHaver, delta, mode); } // if allowed type is singular, take delta[0] diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java new file mode 100644 index 00000000000..9f34ce8ef88 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java @@ -0,0 +1,46 @@ +package org.skriptlang.skript.lang.properties; + +import ch.njol.skript.classes.Changer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("unused") +public interface PropertyHandler { + interface ContainsHandler extends PropertyHandler { + boolean contains(Container container, Element element); + + Class[] elementTypes(); + + default boolean canContain(Class type) { + for (Class elementType : elementTypes()) { + if (elementType.isAssignableFrom(type)) { + return true; + } + } + return false; + } + } + + interface ExpressionPropertyHandler extends PropertyHandler { + // Handler for the NAME property + default Class @Nullable [] acceptChange(Changer.ChangeMode mode) { + return null; + } + + default void change(Type named, Object @Nullable [] delta, Changer.ChangeMode mode) { + throw new UnsupportedOperationException("Changing the name is not supported for this property."); + } + + @NotNull Class returnType(); + } + + /** + * no returning arrays + * + * @param + * @param + */ + interface NameHandler extends ExpressionPropertyHandler { + Name name(Named named); + } +} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java deleted file mode 100644 index acfd7e25524..00000000000 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyInfo.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.skriptlang.skript.lang.properties; - -public record PropertyInfo>(Property property, Handler handler) { -} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java index 5f4c4705f83..88aaa15cfa5 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java @@ -10,9 +10,9 @@ import java.util.Locale; import java.util.Map; -public class PropertyRegistry implements Registry { +public class PropertyRegistry implements Registry> { - private final Map properties; + private final Map> properties; private final Skript skript; public PropertyRegistry(Skript skript) { @@ -20,7 +20,7 @@ public PropertyRegistry(Skript skript) { this.properties = new java.util.HashMap<>(); } - public boolean register(@NotNull Property property) { + public boolean register(@NotNull Property property) { String name = property.name(); if (properties.containsKey(name)) { Skript.error("Property '" + name + "' is already registered by " + properties.get(name).provider().name() + "."); @@ -31,7 +31,7 @@ public boolean register(@NotNull Property property) { return true; } - public boolean unregister(@NotNull Property property) { + public boolean unregister(@NotNull Property property) { String name = property.name(); return unregister(name); } @@ -48,15 +48,15 @@ public boolean unregister(String name) { } @Override - public @Unmodifiable Collection elements() { + public @Unmodifiable Collection> elements() { return Collections.unmodifiableCollection(properties.values()); } - public Property get(String name) { + public Property get(String name) { return properties.get(name); } - public boolean isRegistered(@NotNull Property property) { + public boolean isRegistered(@NotNull Property property) { return isRegistered(property.name()); } @@ -64,5 +64,4 @@ public boolean isRegistered(@NotNull String name) { return properties.containsKey(name.toLowerCase(Locale.ENGLISH)); } - } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java index 7530318d4fd..69996097e53 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyUtils.java @@ -14,9 +14,9 @@ public class PropertyUtils { - public static class PropertyMap> extends HashMap, PropertyInfo> { + public static class PropertyMap> extends HashMap, Property.PropertyInfo> { public @Nullable Handler getHandler(Class inputClass) { - PropertyInfo propertyInfo; + Property.PropertyInfo propertyInfo; // check if we don't already know the right info for this class propertyInfo = get(inputClass); if (propertyInfo == null) { @@ -27,7 +27,7 @@ public static class PropertyMap> ext return propertyInfo.handler(); } - public PropertyInfo get(Class actualClass) { + public Property.PropertyInfo get(Class actualClass) { if (super.containsKey(actualClass)) { return super.get(actualClass); } @@ -64,7 +64,7 @@ public static Expression asProperty(Property property, Expression expr) } - public static > PropertyMap getPossiblePropertyInfos( + public static > PropertyMap getPossiblePropertyInfos( Property property, Expression expr ) { From 7d15cea6172b85e9173bf4ae36592f24cef460a2 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 30 Aug 2025 18:19:33 -0700 Subject: [PATCH 08/39] implement common module --- src/main/java/ch/njol/skript/Skript.java | 45 ++++--------------- .../skript/common/CommonModule.java | 18 ++++++++ 2 files changed, 27 insertions(+), 36 deletions(-) create mode 100644 src/main/java/org/skriptlang/skript/common/CommonModule.java diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 09b8a644562..75d8082e1f2 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -3,53 +3,29 @@ import ch.njol.skript.aliases.Aliases; import ch.njol.skript.bukkitutil.BurgerHelper; import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.classes.data.BukkitClasses; -import ch.njol.skript.classes.data.BukkitEventValues; -import ch.njol.skript.classes.data.DefaultComparators; -import ch.njol.skript.classes.data.DefaultConverters; -import ch.njol.skript.classes.data.DefaultFunctions; -import ch.njol.skript.classes.data.DefaultOperations; -import ch.njol.skript.classes.data.JavaClasses; -import ch.njol.skript.classes.data.SkriptClasses; +import ch.njol.skript.classes.data.*; import ch.njol.skript.command.Commands; import ch.njol.skript.doc.Documentation; import ch.njol.skript.events.EvtSkript; import ch.njol.skript.hooks.Hook; import ch.njol.skript.lang.*; +import ch.njol.skript.lang.Effect; import ch.njol.skript.lang.Condition.ConditionType; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.localization.Language; import ch.njol.skript.localization.Message; import ch.njol.skript.localization.PluralizingArgsMessage; -import ch.njol.skript.log.BukkitLoggerFilter; -import ch.njol.skript.log.CountingLogHandler; -import ch.njol.skript.log.ErrorDescLogHandler; -import ch.njol.skript.log.ErrorQuality; -import ch.njol.skript.log.LogEntry; -import ch.njol.skript.log.LogHandler; -import ch.njol.skript.log.SkriptLogger; -import ch.njol.skript.log.TestingLogHandler; -import ch.njol.skript.log.Verbosity; +import ch.njol.skript.log.*; import ch.njol.skript.registrations.Classes; import ch.njol.skript.registrations.EventValues; import ch.njol.skript.registrations.Feature; -import ch.njol.skript.test.runner.EffObjectives; -import ch.njol.skript.test.runner.SkriptAsyncJUnitTest; -import ch.njol.skript.test.runner.SkriptJUnitTest; -import ch.njol.skript.test.runner.SkriptTestEvent; -import ch.njol.skript.test.runner.TestMode; -import ch.njol.skript.test.runner.TestTracker; +import ch.njol.skript.test.runner.*; import ch.njol.skript.timings.SkriptTimings; import ch.njol.skript.update.ReleaseManifest; import ch.njol.skript.update.ReleaseStatus; import ch.njol.skript.update.UpdateManifest; import ch.njol.skript.util.Date; -import ch.njol.skript.util.EmptyStacktraceException; -import ch.njol.skript.util.ExceptionUtils; -import ch.njol.skript.util.FileUtils; -import ch.njol.skript.util.Task; -import ch.njol.skript.util.Utils; -import ch.njol.skript.util.Version; +import ch.njol.skript.util.*; import ch.njol.skript.util.chat.BungeeConverter; import ch.njol.skript.util.chat.ChatMessages; import ch.njol.skript.variables.Variables; @@ -62,11 +38,7 @@ import com.google.gson.GsonBuilder; import io.papermc.lib.PaperLib; import org.bstats.bukkit.Metrics; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; +import org.bukkit.*; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.entity.Player; @@ -101,6 +73,7 @@ import org.skriptlang.skript.bukkit.registration.BukkitRegistryKeys; import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos; import org.skriptlang.skript.bukkit.tags.TagModule; +import org.skriptlang.skript.common.CommonModule; import org.skriptlang.skript.lang.comparator.Comparator; import org.skriptlang.skript.lang.comparator.Comparators; import org.skriptlang.skript.lang.converter.Converter; @@ -500,7 +473,7 @@ public void onEnable() { experimentRegistry = new ExperimentRegistry(this); Feature.registerAll(getAddonInstance(), experimentRegistry); - getAddonInstance().storeRegistry(PropertyRegistry.class, new PropertyRegistry(this)); + skript.storeRegistry(PropertyRegistry.class, new PropertyRegistry(this)); Property.registerDefaultProperties(); // Load classes which are always safe to use @@ -582,7 +555,6 @@ public void onEnable() { getAddonInstance().loadClasses("ch.njol.skript", "conditions", "effects", "events", "expressions", "entity", "sections", "structures"); getAddonInstance().loadClasses("org.skriptlang.skript.bukkit", "misc"); - getAddonInstance().loadClasses("org.skriptlang.skript.lang", "properties"); // todo: become proper module once registry api is merged FishingModule.load(); BreedingModule.load(); @@ -592,6 +564,7 @@ public void onEnable() { FurnaceModule.load(); LootTableModule.load(); skript.loadModules(new DamageSourceModule()); + skript.loadModules(new CommonModule()); } catch (final Exception e) { exception(e, "Could not load required .class files: " + e.getLocalizedMessage()); setEnabled(false); diff --git a/src/main/java/org/skriptlang/skript/common/CommonModule.java b/src/main/java/org/skriptlang/skript/common/CommonModule.java new file mode 100644 index 00000000000..680862dd55c --- /dev/null +++ b/src/main/java/org/skriptlang/skript/common/CommonModule.java @@ -0,0 +1,18 @@ +package org.skriptlang.skript.common; + +import ch.njol.skript.Skript; +import org.skriptlang.skript.addon.AddonModule; +import org.skriptlang.skript.addon.SkriptAddon; + +import java.io.IOException; + +public class CommonModule implements AddonModule { + @Override + public void load(SkriptAddon addon) { + try { + Skript.getAddonInstance().loadClasses("org.skriptlang.skript.common", "expressions", "conditions"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} From bb9bd18acbb0a6da54a261b50da53446ea755bb0 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sun, 31 Aug 2025 16:14:49 -0700 Subject: [PATCH 09/39] Cleanup, fully implement ExprName, many docs, some other improvements - allows stateful property handlers - removes use of AnyNamed - removes NameHandler in favor of just using ExpressionPropertyHandler - Avoid exposing silly cast to users when possible in PropertyBaseExpression - fix issue with handling DELETE and RESET changers in PropertyBaseExpression - add support for default toString impl in PropertyBaseExpression --- .../java/ch/njol/skript/aliases/ItemType.java | 11 +- .../ch/njol/skript/classes/ClassInfo.java | 9 +- .../skript/classes/data/BukkitClasses.java | 410 ++---------------- .../skript/classes/data/DefaultChangers.java | 287 +----------- .../classes/data/DefaultConverters.java | 69 --- .../skript/classes/data/SkriptClasses.java | 155 +++---- .../java/ch/njol/skript/config/Config.java | 9 +- src/main/java/ch/njol/skript/config/Node.java | 8 +- .../ch/njol/skript/expressions/ExprName.java | 230 +--------- .../function/DynamicFunctionReference.java | 4 +- .../java/ch/njol/skript/util/slot/Slot.java | 44 +- .../bukkit/base/types/BlockClassInfo.java | 224 ++++++++++ .../bukkit/base/types/EntityClassInfo.java | 195 +++++++++ .../bukkit/base/types/InventoryClassInfo.java | 287 ++++++++++++ .../bukkit/base/types/NameableClassInfo.java | 29 ++ .../base/types/OfflinePlayerClassInfo.java | 121 ++++++ .../bukkit/base/types/PlayerClassInfo.java | 164 +++++++ .../expressions/PropExprCustomName.java | 50 +++ .../common/expressions/PropExprName.java | 62 ++- .../skript/common/types/ScriptClassInfo.java | 105 +++++ .../skript/lang/properties/Property.java | 100 ++++- .../properties/PropertyBaseExpression.java | 108 +++-- .../lang/properties/PropertyHandler.java | 178 +++++++- .../skript/lang/properties/PropertyUtils.java | 68 ++- .../skriptlang/skript/lang/script/Script.java | 19 +- 25 files changed, 1764 insertions(+), 1182 deletions(-) create mode 100644 src/main/java/org/skriptlang/skript/bukkit/base/types/BlockClassInfo.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/base/types/EntityClassInfo.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/base/types/InventoryClassInfo.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/base/types/NameableClassInfo.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/base/types/OfflinePlayerClassInfo.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/base/types/PlayerClassInfo.java create mode 100644 src/main/java/org/skriptlang/skript/common/expressions/PropExprCustomName.java create mode 100644 src/main/java/org/skriptlang/skript/common/types/ScriptClassInfo.java diff --git a/src/main/java/ch/njol/skript/aliases/ItemType.java b/src/main/java/ch/njol/skript/aliases/ItemType.java index 4c083d940d8..a2a3705c349 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemType.java +++ b/src/main/java/ch/njol/skript/aliases/ItemType.java @@ -6,7 +6,6 @@ import ch.njol.skript.bukkitutil.ItemUtils; import ch.njol.skript.lang.Unit; import ch.njol.skript.lang.util.common.AnyAmount; -import ch.njol.skript.lang.util.common.AnyNamed; import ch.njol.skript.localization.Adjective; import ch.njol.skript.localization.GeneralWords; import ch.njol.skript.localization.Language; @@ -49,8 +48,7 @@ import java.util.stream.Collectors; @ContainerType(ItemStack.class) -public class ItemType implements Unit, Iterable, Container, YggdrasilExtendedSerializable, - AnyNamed, AnyAmount { +public class ItemType implements Unit, Iterable, Container, YggdrasilExtendedSerializable, AnyAmount { private static final boolean IS_RUNNING_1_21 = Skript.isRunningMinecraft(1, 21); @@ -1611,18 +1609,11 @@ public ItemType getBaseType() { return copy; } - @Override public @Nullable String name() { ItemMeta meta = this.getItemMeta(); return meta.hasDisplayName() ? meta.getDisplayName() : null; } - @Override - public boolean supportsNameChange() { - return true; - } - - @Override public void setName(String name) { ItemMeta meta = this.getItemMeta(); meta.setDisplayName(name); diff --git a/src/main/java/ch/njol/skript/classes/ClassInfo.java b/src/main/java/ch/njol/skript/classes/ClassInfo.java index e109307f689..afb23bef477 100644 --- a/src/main/java/ch/njol/skript/classes/ClassInfo.java +++ b/src/main/java/ch/njol/skript/classes/ClassInfo.java @@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.Property.PropertyInfo; import org.skriptlang.skript.lang.properties.PropertyHandler; import java.util.*; @@ -477,13 +478,13 @@ public String toString(final @Nullable Event event, final boolean debug) { } - private final Map, Property.PropertyInfo> propertyInfos = new HashMap<>(); + private final Map, PropertyInfo> propertyInfos = new HashMap<>(); public > ClassInfo property(Property property, @NotNull Handler handler) { if (propertyInfos.containsKey(property)) { throw new IllegalStateException("Property " + property.name() + " is already registered for the " + c.getName() + " type."); } - propertyInfos.put(property, new Property.PropertyInfo<>(property, handler)); + propertyInfos.put(property, new PropertyInfo<>(property, handler)); Classes.CLASS_INFOS_BY_PROPERTY.computeIfAbsent(property, k -> new ArrayList<>()).add(this); return this; } @@ -492,12 +493,12 @@ public boolean hasProperty(Property property) { return propertyInfos.containsKey(property); } - public > @Nullable Property.PropertyInfo getPropertyInfo(Property property) { + public > @Nullable PropertyInfo getPropertyInfo(Property property) { if (!propertyInfos.containsKey(property)) { return null; } //noinspection unchecked - return (Property.PropertyInfo) propertyInfos.get(property); + return (PropertyInfo) propertyInfos.get(property); } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index 42377d9cd3d..996393f95d5 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -1,7 +1,6 @@ package ch.njol.skript.classes.data; import ch.njol.skript.Skript; -import ch.njol.skript.SkriptConfig; import ch.njol.skript.aliases.Aliases; import ch.njol.skript.aliases.ItemType; import ch.njol.skript.bukkitutil.BukkitUtils; @@ -12,23 +11,22 @@ import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.entity.ChickenData.ChickenVariantDummy; import ch.njol.skript.entity.CowData.CowVariantDummy; -import ch.njol.skript.entity.EntityData; import ch.njol.skript.entity.PigData.PigVariantDummy; import ch.njol.skript.entity.WolfData.WolfVariantDummy; import ch.njol.skript.expressions.ExprDamageCause; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.lang.util.SimpleLiteral; -import ch.njol.skript.localization.Language; import ch.njol.skript.registrations.Classes; -import ch.njol.skript.util.*; +import ch.njol.skript.util.BlockUtils; +import ch.njol.skript.util.PaperUtils; +import ch.njol.skript.util.PotionEffectUtils; import ch.njol.yggdrasil.Fields; import io.papermc.paper.world.MoonPhase; import org.bukkit.*; import org.bukkit.World.Environment; import org.bukkit.attribute.Attribute; import org.bukkit.block.Biome; -import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.DoubleChest; import org.bukkit.block.banner.PatternType; @@ -53,21 +51,20 @@ import org.bukkit.event.player.PlayerQuitEvent.QuitReason; import org.bukkit.event.player.PlayerResourcePackStatusEvent.Status; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; -import org.bukkit.inventory.BlockInventoryHolder; import org.bukkit.inventory.*; import org.bukkit.metadata.Metadatable; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.CachedServerIcon; import org.bukkit.util.Vector; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.base.types.*; +import org.skriptlang.skript.bukkit.base.types.EntityClassInfo.EntityChanger; import org.skriptlang.skript.lang.properties.Property; -import org.skriptlang.skript.lang.properties.PropertyHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.ContainsHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; import java.io.StreamCorruptedException; -import java.util.*; +import java.util.Arrays; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -79,45 +76,7 @@ public class BukkitClasses { public BukkitClasses() {} static { - Classes.registerClass(new ClassInfo<>(Entity.class, "entity") - .user("entit(y|ies)") - .name("Entity") - .description("An entity is something in a world that's not a block, " + - "e.g. a player, a skeleton, or a zombie, but also " + - "projectiles like arrows, fireballs or thrown potions, " + - "or special entities like dropped items, falling blocks or paintings.") - .usage("player, op, wolf, tamed ocelot, powered creeper, zombie, unsaddled pig, fireball, arrow, dropped item, item frame, etc.") - .examples("entity is a zombie or creeper", - "player is an op", - "projectile is an arrow", - "shoot a fireball from the player") - .since("1.0") - .defaultExpression(new EventValueExpression<>(Entity.class)) - .parser(new Parser() { - @Override - public @Nullable Entity parse(final String s, final ParseContext context) { - if (Utils.isValidUUID(s)) - return Bukkit.getEntity(UUID.fromString(s)); - - return null; - } - - @Override - public boolean canParse(final ParseContext context) { - return context == ParseContext.COMMAND || context == ParseContext.PARSE; - } - - @Override - public String toVariableNameString(final Entity e) { - return "entity:" + e.getUniqueId().toString().toLowerCase(Locale.ENGLISH); - } - - @Override - public String toString(final Entity e, final int flags) { - return EntityData.toString(e, flags); - } - }) - .changer(DefaultChangers.entityChanger)); + Classes.registerClass(new EntityClassInfo()); Classes.registerClass(new ClassInfo<>(LivingEntity.class, "livingentity") .user("living ?entit(y|ies)") @@ -129,7 +88,7 @@ public String toString(final Entity e, final int flags) { "shoot a zombie from the creeper") .since("1.0") .defaultExpression(new EventValueExpression<>(LivingEntity.class)) - .changer(DefaultChangers.entityChanger)); + .changer(new EntityChanger())); Classes.registerClass(new ClassInfo<>(Projectile.class, "projectile") .user("projectiles?") @@ -142,98 +101,7 @@ public String toString(final Entity e, final int flags) { .defaultExpression(new EventValueExpression<>(Projectile.class)) .changer(DefaultChangers.nonLivingEntityChanger)); - Classes.registerClass(new ClassInfo<>(Block.class, "block") - .user("blocks?") - .name("Block") - .description("A block in a world. It has a location and a type, " + - "and can also have a direction (mostly a facing), an inventory, or other special properties.") - .usage("") - .examples("") - .since("1.0") - .defaultExpression(new EventValueExpression<>(Block.class)) - .parser(new Parser() { - @Override - @Nullable - public Block parse(final String s, final ParseContext context) { - return null; - } - - @Override - public boolean canParse(final ParseContext context) { - return false; - } - - @Override - public String toString(final Block b, final int flags) { - return BlockUtils.blockToString(b, flags); - } - - @Override - public String toVariableNameString(final Block b) { - return b.getWorld().getName() + ":" + b.getX() + "," + b.getY() + "," + b.getZ(); - } - - @Override - public String getDebugMessage(final Block b) { - return toString(b, 0) + " block (" + b.getWorld().getName() + ":" + b.getX() + "," + b.getY() + "," + b.getZ() + ")"; - } - }) - .changer(DefaultChangers.blockChanger) - .serializer(new Serializer() { - @Override - public Fields serialize(final Block b) { - final Fields f = new Fields(); - f.putObject("world", b.getWorld()); - f.putPrimitive("x", b.getX()); - f.putPrimitive("y", b.getY()); - f.putPrimitive("z", b.getZ()); - return f; - } - - @Override - public void deserialize(final Block o, final Fields f) { - assert false; - } - - @Override - protected Block deserialize(final Fields fields) throws StreamCorruptedException { - final World w = fields.getObject("world", World.class); - final int x = fields.getPrimitive("x", int.class), y = fields.getPrimitive("y", int.class), z = fields.getPrimitive("z", int.class); - if (w == null) - throw new StreamCorruptedException(); - return w.getBlockAt(x, y, z); - } - - @Override - public boolean mustSyncDeserialization() { - return true; - } - - @Override - public boolean canBeInstantiated() { - return false; - } - - // return b.getWorld().getName() + ":" + b.getX() + "," + b.getY() + "," + b.getZ(); - @Override - @Nullable - public Block deserialize(final String s) { - final String[] split = s.split("[:,]"); - if (split.length != 4) - return null; - final World w = Bukkit.getWorld(split[0]); - if (w == null) - return null; - try { - final int[] l = new int[3]; - for (int i = 0; i < 3; i++) - l[i] = Integer.parseInt(split[i + 1]); - return w.getBlockAt(l[0], l[1], l[2]); - } catch (final NumberFormatException e) { - return null; - } - } - })); + Classes.registerClass(new BlockClassInfo()); Classes.registerClass(new ClassInfo<>(BlockData.class, "blockdata") .user("block ?datas?") @@ -544,63 +412,11 @@ public World deserialize(final String s) { public boolean mustSyncDeserialization() { return true; } - })); - - Classes.registerClass(new ClassInfo<>(Inventory.class, "inventory") - .user("inventor(y|ies)") - .name("Inventory") - .description("An inventory of a player or block. " + - "Inventories have many effects and conditions regarding the items contained.", - "An inventory has a fixed amount of slots which represent a specific place in the inventory, " + - "e.g. the helmet slot for players " + - "(Please note that slot support is still very limited but will be improved eventually).") - .usage("") - .examples("") - .since("1.0") - .defaultExpression(new EventValueExpression<>(Inventory.class)) - .parser(new Parser() { - @Override - @Nullable - public Inventory parse(final String s, final ParseContext context) { - return null; - } - - @Override - public boolean canParse(final ParseContext context) { - return false; - } - - @Override - public String toString(final Inventory i, final int flags) { - return "inventory of " + Classes.toString(i.getHolder()); - } - - @Override - public String getDebugMessage(final Inventory i) { - return "inventory of " + Classes.getDebugMessage(i.getHolder()); - } - - @Override - public String toVariableNameString(final Inventory i) { - return "inventory of " + Classes.toString(i.getHolder(), StringMode.VARIABLE_NAME); - } - }).changer(DefaultChangers.inventoryChanger) - .property(Property.CONTAINS, new ContainsHandler() { - @Override - public boolean contains(Inventory container, Object element) { - if (element instanceof ItemType type) { - return type.isContainedIn(container); - } else if (element instanceof ItemStack stack) { - return container.containsAtLeast(stack, stack.getAmount()); - } - return false; - } + }) + .property(Property.NAME, ExpressionPropertyHandler.of(World::getName, String.class)) + ); - @Override - public Class[] elementTypes() { - return new Class[]{ItemType.class, ItemStack.class}; - } - })); + Classes.registerClass(new InventoryClassInfo()); Classes.registerClass(new EnumClassInfo<>(InventoryAction.class, "inventoryaction", "inventory actions") .user("inventory ?actions?") @@ -624,189 +440,9 @@ public Class[] elementTypes() { .examples("") .since("2.2-dev32")); - Classes.registerClass(new ClassInfo<>(Player.class, "player") - .user("players?") - .name("Player") - .description( - "A player. Depending on whether a player is online or offline several actions can be performed with them, " + - "though you won't get any errors when using effects that only work if the player is online (e.g. changing their inventory) on an offline player.", - "You have two possibilities to use players as command arguments: and . " + - "The first requires that the player is online and also accepts only part of the name, " + - "while the latter doesn't require that the player is online, but the player's name has to be entered exactly." - ).usage( - "Parsing an offline player as a player (online) will return nothing (none), for that case you would need to parse as " + - "offlineplayer which only returns nothing (none) if player doesn't exist in Minecraft databases (name not taken) otherwise it will return the player regardless of their online status." - ).examples( - "set {_p} to \"Notch\" parsed as a player # returns unless Notch is actually online or starts with Notch like Notchan", - "set {_p} to \"N\" parsed as a player # returns Notch if Notch is online because their name starts with 'N' (case insensitive) however, it would return nothing if no player whose name starts with 'N' is online." - ).since("1.0") - .defaultExpression(new EventValueExpression<>(Player.class)) - .after("string", "world") - .parser(new Parser() { - @Override - @Nullable - public Player parse(String string, ParseContext context) { - if (context == ParseContext.COMMAND || context == ParseContext.PARSE) { - if (string.isEmpty()) - return null; - - if (Utils.isValidUUID(string)) - return Bukkit.getPlayer(UUID.fromString(string)); - - String name = string.toLowerCase(Locale.ENGLISH); - int nameLength = name.length(); // caching - List players = new ArrayList<>(); - for (Player player : Bukkit.getOnlinePlayers()) { - if (player.getName().toLowerCase(Locale.ENGLISH).startsWith(name)) { - if (player.getName().length() == nameLength) // a little better in performance than String#equals() - return player; - players.add(player); - } - } - if (players.size() == 1) - return players.get(0); - if (players.size() == 0) - Skript.error(String.format(Language.get("commands.no player starts with"), string)); - else - Skript.error(String.format(Language.get("commands.multiple players start with"), string)); - return null; - } - assert false; - return null; - } - - @Override - public boolean canParse(final ParseContext context) { - return context == ParseContext.COMMAND || context == ParseContext.PARSE; - } - - @Override - public String toString(final Player p, final int flags) { - return "" + p.getName(); - } - - @Override - public String toVariableNameString(final Player p) { - if (SkriptConfig.usePlayerUUIDsInVariableNames.value()) - return "" + p.getUniqueId(); - else - return "" + p.getName(); - } - - @Override - public String getDebugMessage(final Player p) { - return p.getName() + " " + Classes.getDebugMessage(p.getLocation()); - } - }) - .changer(DefaultChangers.playerChanger) - .serializeAs(OfflinePlayer.class) - .property(Property.NAME, new PropertyHandler.NameHandler() { - @Override - public String name(Player player) { - return player.getName(); - } - - @Override - public @NotNull Class returnType() { - return String.class; - } - })); - - Classes.registerClass(new ClassInfo<>(OfflinePlayer.class, "offlineplayer") - .user("offline ?players?") - .name("Offline Player") - .description( - "A player that is possibly offline. See player for more information. " + - "Please note that while all effects and conditions that require a player can be used with an " + - "offline player as well, they will not work if the player is not actually online." - ).usage( - "Parsing an offline player as a player (online) will return nothing (none), for that case you would need to parse as " + - "offlineplayer which only returns nothing (none) if player doesn't exist in Minecraft databases (name not taken) otherwise it will return the player regardless of their online status." - ).examples("set {_p} to \"Notch\" parsed as an offlineplayer # returns Notch even if they're offline") - .since("2.0 beta 8") - .defaultExpression(new EventValueExpression<>(OfflinePlayer.class)) - .after("string", "world") - .parser(new Parser() { - @Override - public @Nullable OfflinePlayer parse(final String s, final ParseContext context) { - if (Utils.isValidUUID(s)) - return Bukkit.getOfflinePlayer(UUID.fromString(s)); - else if (SkriptConfig.playerNameRegexPattern.value().matcher(s).matches()) - return Bukkit.getOfflinePlayer(s); - return null; - } - - @Override - public boolean canParse(ParseContext context) { - return context == ParseContext.COMMAND || context == ParseContext.PARSE; - } - - @Override - public String toString(OfflinePlayer p, int flags) { - return p.getName() == null ? p.getUniqueId().toString() : p.getName(); - } - - @Override - public String toVariableNameString(OfflinePlayer p) { - if (SkriptConfig.usePlayerUUIDsInVariableNames.value() || p.getName() == null) - return "" + p.getUniqueId(); - else - return "" + p.getName(); - } - - @Override - public String getDebugMessage(OfflinePlayer p) { - if (p.isOnline()) - return Classes.getDebugMessage(p.getPlayer()); - return toString(p, 0); - } - }).serializer(new Serializer() { - @Override - public Fields serialize(final OfflinePlayer p) { - final Fields f = new Fields(); - f.putObject("uuid", p.getUniqueId()); - return f; - } - - @Override - public void deserialize(final OfflinePlayer o, final Fields f) { - assert false; - } - - @Override - public boolean canBeInstantiated() { - return false; - } + Classes.registerClass(new PlayerClassInfo()); - @SuppressWarnings("deprecation") - @Override - protected OfflinePlayer deserialize(final Fields fields) throws StreamCorruptedException { - if (fields.contains("uuid")) { - final UUID uuid = fields.getObject("uuid", UUID.class); - if (uuid == null) - throw new StreamCorruptedException(); - return Bukkit.getOfflinePlayer(uuid); - } else { - final String name = fields.getObject("name", String.class); - if (name == null) - throw new StreamCorruptedException(); - return Bukkit.getOfflinePlayer(name); - } - } - - // return p.getName(); - @SuppressWarnings("deprecation") - @Override - @Nullable - public OfflinePlayer deserialize(final String s) { - return Bukkit.getOfflinePlayer(s); - } - - @Override - public boolean mustSyncDeserialization() { - return true; - } - })); + Classes.registerClass(new OfflinePlayerClassInfo()); Classes.registerClass(new ClassInfo<>(CommandSender.class, "commandsender") .user("((commands?)? ?)?(sender|executor)s?") @@ -827,7 +463,7 @@ public boolean mustSyncDeserialization() { "\t\t\tsend \"Yay!\" to sender and arg-1") .since("1.0") .defaultExpression(new EventValueExpression<>(CommandSender.class)) - .parser(new Parser() { + .parser(new Parser<>() { @Override @Nullable public CommandSender parse(final String s, final ParseContext context) { @@ -841,14 +477,15 @@ public boolean canParse(final ParseContext context) { @Override public String toString(final CommandSender s, final int flags) { - return "" + s.getName(); + return s.getName(); } @Override public String toVariableNameString(final CommandSender s) { - return "" + s.getName(); + return s.getName(); } - })); + }) + .property(Property.NAME, ExpressionPropertyHandler.of(CommandSender::getName, String.class))); Classes.registerClass(new ClassInfo<>(InventoryHolder.class, "inventoryholder") .name(ClassInfo.NO_DOC) @@ -1327,9 +964,10 @@ public String toVariableNameString(FireworkEffect effect) { .documentationId("CatType")); + //noinspection rawtypes PatternedParser gameRuleParser = new PatternedParser<>() { - private String[] patterns = Arrays.stream(GameRule.values()).map(GameRule::getName).toArray(String[]::new); + private final String[] patterns = Arrays.stream(GameRule.values()).map(GameRule::getName).toArray(String[]::new); @Override public @Nullable GameRule parse(String string, ParseContext context) { @@ -1351,6 +989,7 @@ public String[] getPatterns() { return patterns; } }; + Classes.registerClass(new ClassInfo<>(GameRule.class, "gamerule") .user("gamerules?") .name("Gamerule") @@ -1360,6 +999,7 @@ public String[] getPatterns() { .requiredPlugins("Minecraft 1.13 or newer") .supplier(GameRule.values()) .parser(gameRuleParser) + .property(Property.NAME, ExpressionPropertyHandler.of(GameRule::getName, String.class)) ); Classes.registerClass(new ClassInfo<>(EnchantmentOffer.class, "enchantmentoffer") diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultChangers.java b/src/main/java/ch/njol/skript/classes/data/DefaultChangers.java index 8a8ce77dc55..3064b309f7f 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultChangers.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultChangers.java @@ -1,26 +1,17 @@ package ch.njol.skript.classes.data; -import org.bukkit.Material; +import ch.njol.skript.classes.Changer; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.block.Block; -import org.bukkit.block.BlockState; -import org.bukkit.block.data.BlockData; import org.bukkit.entity.Entity; import org.bukkit.entity.Item; -import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; -import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.Nullable; - -import ch.njol.skript.Skript; -import ch.njol.skript.aliases.ItemType; -import ch.njol.skript.bukkitutil.PlayerUtils; -import ch.njol.skript.classes.Changer; -import ch.njol.skript.util.Experience; -import ch.njol.util.coll.CollectionUtils; +import org.skriptlang.skript.bukkit.base.types.BlockClassInfo; +import org.skriptlang.skript.bukkit.base.types.EntityClassInfo; +import org.skriptlang.skript.bukkit.base.types.InventoryClassInfo; /** * @author Peter Güttinger @@ -28,79 +19,12 @@ public class DefaultChangers { public DefaultChangers() {} - - public final static Changer entityChanger = new Changer() { - @Override - @Nullable - public Class[] acceptChange(final ChangeMode mode) { - switch (mode) { - case ADD: - return CollectionUtils.array(ItemType[].class, Inventory.class, Experience[].class); - case DELETE: - return CollectionUtils.array(); - case REMOVE: - return CollectionUtils.array(PotionEffectType[].class, ItemType[].class, Inventory.class); - case REMOVE_ALL: - return CollectionUtils.array(PotionEffectType[].class, ItemType[].class); - case SET: - case RESET: // REMIND reset entity? (unshear, remove held item, reset weapon/armour, ...) - return null; - } - assert false; - return null; - } - - @Override - public void change(final Entity[] entities, final @Nullable Object[] delta, final ChangeMode mode) { - if (delta == null) { - for (final Entity e : entities) { - if (!(e instanceof Player)) - e.remove(); - } - return; - } - boolean hasItem = false; - for (final Entity e : entities) { - for (final Object d : delta) { - if (d instanceof PotionEffectType) { - assert mode == ChangeMode.REMOVE || mode == ChangeMode.REMOVE_ALL; - if (!(e instanceof LivingEntity)) - continue; - ((LivingEntity) e).removePotionEffect((PotionEffectType) d); - } else { - if (e instanceof Player) { - final Player p = (Player) e; - if (d instanceof Experience) { - p.giveExp(((Experience) d).getXP()); - } else if (d instanceof Inventory) { - PlayerInventory inventory = p.getInventory(); - for (ItemStack itemStack : (Inventory) d) { - if (itemStack == null) - continue; - if (mode == ChangeMode.ADD) { - inventory.addItem(itemStack); - } else { - inventory.remove(itemStack); - } - } - } else if (d instanceof ItemType) { - hasItem = true; - final PlayerInventory invi = p.getInventory(); - if (mode == ChangeMode.ADD) - ((ItemType) d).addTo(invi); - else if (mode == ChangeMode.REMOVE) - ((ItemType) d).removeFrom(invi); - else - ((ItemType) d).removeAll(invi); - } - } - } - } - if (e instanceof Player && hasItem) - PlayerUtils.updateInventory((Player) e); - } - } - }; + + /** + * @deprecated Use {@link EntityClassInfo.EntityChanger} directly. + */ + @Deprecated(since = "INSERT VERSION") + public final static Changer entityChanger = new EntityClassInfo.EntityChanger(); public final static Changer playerChanger = new Changer() { @Override @@ -157,182 +81,17 @@ public void change(final Item[] what, final @Nullable Object[] delta, final Chan } } }; - - public final static Changer inventoryChanger = new Changer() { - - private Material[] cachedMaterials = Material.values(); - - @Override - @Nullable - public Class[] acceptChange(final ChangeMode mode) { - if (mode == ChangeMode.RESET) - return null; - if (mode == ChangeMode.REMOVE_ALL) - return CollectionUtils.array(ItemType[].class); - if (mode == ChangeMode.SET) - return CollectionUtils.array(ItemType[].class, Inventory.class); - return CollectionUtils.array(ItemType[].class, Inventory[].class); - } - - @Override - public void change(final Inventory[] invis, final @Nullable Object[] delta, final ChangeMode mode) { - for (final Inventory invi : invis) { - assert invi != null; - switch (mode) { - case DELETE: - invi.clear(); - break; - case SET: - invi.clear(); - //$FALL-THROUGH$ - case ADD: - assert delta != null; - - if(delta instanceof ItemStack[]) { // Old behavior - legacy code (is it used? no idea) - ItemStack[] items = (ItemStack[]) delta; - if(items.length > 36) { - return; - } - for (final Object d : delta) { - if (d instanceof Inventory) { - for (final ItemStack i : (Inventory) d) { - if (i != null) - invi.addItem(i); - } - } else { - ((ItemType) d).addTo(invi); - } - } - } else { - for (final Object d : delta) { - if (d instanceof ItemStack) { - new ItemType((ItemStack) d).addTo(invi); // Can't imagine why would be ItemStack, but just in case... - } else if (d instanceof ItemType) { - ((ItemType) d).addTo(invi); - } else if (d instanceof Block) { - new ItemType((Block) d).addTo(invi); - } else { - Skript.error("Can't " + d.toString() + " to an inventory!"); - } - } - } - - break; - case REMOVE: - case REMOVE_ALL: - assert delta != null; - if (delta.length == cachedMaterials.length) { - // Potential fast path: remove all items -> clear inventory - boolean equal = true; - for (int i = 0; i < delta.length; i++) { - if (!(delta[i] instanceof ItemType)) { - equal = false; - break; // Not an item, take slow path - } - if (((ItemType) delta[i]).getMaterial() != cachedMaterials[i]) { - equal = false; - break; - } - } - if (equal) { // Take fast path, break out before slow one - invi.clear(); - break; - } - } - - // Slow path - for (final Object d : delta) { - if (d instanceof Inventory) { - assert mode == ChangeMode.REMOVE; - for (ItemStack itemStack : (Inventory) d) { - if (itemStack != null) - invi.removeItem(itemStack); - } - } else { - if (mode == ChangeMode.REMOVE) - ((ItemType) d).removeFrom(invi); - else - ((ItemType) d).removeAll(invi); - } - } - break; - case RESET: - assert false; - } - InventoryHolder holder = invi.getHolder(); - if (holder instanceof Player) { - ((Player) holder).updateInventory(); - } - } - } - }; - - public final static Changer blockChanger = new Changer() { - @Override - @Nullable - public Class[] acceptChange(final ChangeMode mode) { - if (mode == ChangeMode.RESET) - return null; // REMIND regenerate? - if (mode == ChangeMode.SET) - return CollectionUtils.array(ItemType.class, BlockData.class); - return CollectionUtils.array(ItemType[].class, Inventory[].class); - } - - @Override - public void change(final Block[] blocks, final @Nullable Object[] delta, final ChangeMode mode) { - for (Block block : blocks) { - assert block != null; - switch (mode) { - case SET: - assert delta != null; - Object object = delta[0]; - if (object instanceof ItemType) { - ((ItemType) object).getBlock().setBlock(block, true); - } else if (object instanceof BlockData) { - block.setBlockData(((BlockData) object)); - } - break; - case DELETE: - block.setType(Material.AIR, true); - break; - case ADD: - case REMOVE: - case REMOVE_ALL: - assert delta != null; - BlockState state = block.getState(); - if (!(state instanceof InventoryHolder)) - break; - Inventory invi = ((InventoryHolder) state).getInventory(); - if (mode == ChangeMode.ADD) { - for (Object obj : delta) { - if (obj instanceof Inventory) { - for (ItemStack i : (Inventory) obj) { - if (i != null) - invi.addItem(i); - } - } else { - ((ItemType) obj).addTo(invi); - } - } - } else { - for (Object obj : delta) { - if (obj instanceof Inventory) { - invi.removeItem(((Inventory) obj).getContents()); - } else { - if (mode == ChangeMode.REMOVE) - ((ItemType) obj).removeFrom(invi); - else - ((ItemType) obj).removeAll(invi); - } - } - } - state.update(); - break; - case RESET: - assert false; - } - } - } - }; + + /** + * @deprecated Use {@link InventoryClassInfo.InventoryChanger} directly. + */ + @Deprecated(since = "INSERT VERSION") + public final static Changer inventoryChanger = new InventoryClassInfo.InventoryChanger(); + + /** + * @deprecated Use {@link BlockClassInfo.BlockChanger} directly. + */ + @Deprecated(since = "INSERT VERSION") + public final static Changer blockChanger = new BlockClassInfo.BlockChanger(); } diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java index f6af95098bb..a1e851963c8 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java @@ -10,7 +10,6 @@ import ch.njol.skript.entity.EntityType; import ch.njol.skript.entity.XpOrbData; import ch.njol.skript.lang.util.common.AnyAmount; -import ch.njol.skript.lang.util.common.AnyNamed; import ch.njol.skript.util.*; import ch.njol.skript.util.slot.Slot; import org.bukkit.*; @@ -29,12 +28,8 @@ import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; -import org.bukkit.plugin.Plugin; -import org.bukkit.scoreboard.Objective; -import org.bukkit.scoreboard.Team; import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnknownNullability; import org.skriptlang.skript.lang.converter.Converter; import org.skriptlang.skript.lang.converter.Converters; import org.skriptlang.skript.lang.script.Script; @@ -159,70 +154,6 @@ public DefaultConverters() {} return null; }, Converter.NO_CHAINING); - // Anything with a name -> AnyNamed - Converters.registerConverter(OfflinePlayer.class, AnyNamed.class, player -> player::getName, Converter.NO_RIGHT_CHAINING); - if (Skript.classExists("org.bukkit.generator.WorldInfo")) - Converters.registerConverter(World.class, AnyNamed.class, world -> world::getName, Converter.NO_RIGHT_CHAINING); - else //noinspection RedundantCast getName method is on World itself in older versions - Converters.registerConverter(World.class, AnyNamed.class, world -> () -> ((World) world).getName(), Converter.NO_RIGHT_CHAINING); - Converters.registerConverter(GameRule.class, AnyNamed.class, rule -> rule::getName, Converter.NO_RIGHT_CHAINING); - Converters.registerConverter(Server.class, AnyNamed.class, server -> server::getName, Converter.NO_RIGHT_CHAINING); - Converters.registerConverter(Plugin.class, AnyNamed.class, plugin -> plugin::getName, Converter.NO_RIGHT_CHAINING); - Converters.registerConverter(WorldType.class, AnyNamed.class, type -> type::getName, Converter.NO_RIGHT_CHAINING); - Converters.registerConverter(Team.class, AnyNamed.class, team -> team::getName, Converter.NO_RIGHT_CHAINING); - Converters.registerConverter(Objective.class, AnyNamed.class, objective -> objective::getName, Converter.NO_RIGHT_CHAINING); - Converters.registerConverter(Nameable.class, AnyNamed.class, // - nameable -> new AnyNamed() { - @Override - public @UnknownNullability String name() { - //noinspection deprecation - return nameable.getCustomName(); - } - - @Override - public boolean supportsNameChange() { - return true; - } - - @Override - public void setName(String name) { - //noinspection deprecation - nameable.setCustomName(name); - } - }, - // - Converter.NO_RIGHT_CHAINING); - Converters.registerConverter(Block.class, AnyNamed.class, // - block -> new AnyNamed() { - @Override - public @UnknownNullability String name() { - BlockState state = block.getState(); - if (state instanceof Nameable nameable) - //noinspection deprecation - return nameable.getCustomName(); - return null; - } - - @Override - public boolean supportsNameChange() { - return true; - } - - @Override - public void setName(String name) { - BlockState state = block.getState(); - if (state instanceof Nameable nameable) { - //noinspection deprecation - nameable.setCustomName(name); - state.update(true, false); - } - } - }, - // - Converter.NO_RIGHT_CHAINING); - Converters.registerConverter(CommandSender.class, AnyNamed.class, thing -> thing::getName, Converter.NO_RIGHT_CHAINING); - // Command senders should be done last because there might be a better alternative above - // Anything with an amount -> AnyAmount Converters.registerConverter(ItemStack.class, AnyAmount.class, // item -> new AnyAmount() { diff --git a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java index 662d493b13a..838e70a872e 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -1,6 +1,5 @@ package ch.njol.skript.classes.data; -import ch.njol.skript.ScriptLoader; import ch.njol.skript.Skript; import ch.njol.skript.aliases.Aliases; import ch.njol.skript.aliases.ItemData; @@ -26,22 +25,23 @@ import ch.njol.skript.util.visual.VisualEffect; import ch.njol.skript.util.visual.VisualEffects; import ch.njol.yggdrasil.Fields; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.lang.properties.PropertyHandler; +import org.skriptlang.skript.common.types.ScriptClassInfo; import org.skriptlang.skript.lang.properties.Property; import org.skriptlang.skript.lang.properties.PropertyHandler.ContainsHandler; -import org.skriptlang.skript.lang.script.Script; +import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; import org.skriptlang.skript.lang.util.SkriptQueue; import org.skriptlang.skript.util.Executable; import java.io.File; import java.io.NotSerializableException; import java.io.StreamCorruptedException; -import java.nio.file.Path; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -105,7 +105,7 @@ public boolean canBeInstantiated() { } @Override - public void deserialize(final ClassInfo o, final Fields f) throws StreamCorruptedException { + public void deserialize(final ClassInfo o, final Fields f) { assert false; } @@ -120,13 +120,6 @@ protected ClassInfo deserialize(final Fields fields) throws StreamCorruptedExcep return ci; } -// return c.getCodeName(); - @Override - @Nullable - public ClassInfo deserialize(final String s) { - return Classes.getClassInfoNoError(s); - } - @Override public boolean mustSyncDeserialization() { return false; @@ -181,51 +174,51 @@ public String toVariableNameString(final WeatherType o) { .supplier(() -> Arrays.stream(Material.values()) .map(ItemType::new) .iterator()) - .parser(new Parser() { + .parser(new Parser<>() { @Override @Nullable - public ItemType parse(final String s, final ParseContext context) { + public ItemType parse(String s, ParseContext context) { return Aliases.parseItemType(s); } @Override - public String toString(final ItemType t, final int flags) { + public String toString(ItemType t, int flags) { return t.toString(flags); } @Override - public String getDebugMessage(final ItemType t) { + public String getDebugMessage(ItemType t) { return t.getDebugMessage(); } @Override - public String toVariableNameString(final ItemType t) { - final StringBuilder b = new StringBuilder("itemtype:"); - b.append(t.getInternalAmount()); - b.append("," + t.isAll()); + public String toVariableNameString(ItemType itemType) { + final StringBuilder result = new StringBuilder("itemtype:"); + result.append(itemType.getInternalAmount()); + result.append(",").append(itemType.isAll()); // TODO this is missing information - for (final ItemData d : t.getTypes()) { - b.append("," + d.getType()); + for (ItemData itemData : itemType.getTypes()) { + result.append(",").append(itemData.getType()); } - final EnchantmentType[] enchs = t.getEnchantmentTypes(); - if (enchs != null) { - b.append("|"); - for (final EnchantmentType ench : enchs) { - Enchantment e = ench.getType(); - if (e == null) + EnchantmentType[] enchantmentTypes = itemType.getEnchantmentTypes(); + if (enchantmentTypes != null) { + result.append("|"); + for (EnchantmentType enchantmentType : enchantmentTypes) { + Enchantment enchantment = enchantmentType.getType(); + if (enchantment == null) continue; - b.append("#" + e.getKey().toString()); - b.append(":" + ench.getLevel()); + result.append("#").append(enchantment.getKey()); + result.append(":").append(enchantmentType.getLevel()); } } - return "" + b.toString(); + return result.toString(); } }) .cloner(ItemType::clone) .serializer(new YggdrasilSerializer<>()) - .property(Property.NAME, new PropertyHandler.NameHandler() { + .property(Property.NAME, new ExpressionPropertyHandler() { @Override - public String name(ItemType itemType) { + public String convert(ItemType itemType) { return itemType.name(); } @@ -564,7 +557,44 @@ public String toVariableNameString(Slot o) { return "slot:" + o.toString(); } }) - .serializeAs(ItemStack.class)); + .serializeAs(ItemStack.class) + .property(Property.NAME, new ExpressionPropertyHandler() { + @Override + public String convert(Slot slot) { + ItemStack stack = slot.getItem(); + if (stack != null && stack.hasItemMeta()) { + ItemMeta meta = stack.getItemMeta(); + return meta.hasDisplayName() ? meta.getDisplayName() : null; + } + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET) + return new Class[] {String.class}; + return null; + } + + @Override + public void change(Slot named, Object @Nullable [] delta, ChangeMode mode) { + assert mode == ChangeMode.SET; + assert delta != null; + String name = (String) delta[0]; + ItemStack stack = named.getItem(); + if (stack != null && !ItemUtils.isAir(stack.getType())) { + ItemMeta meta = stack.hasItemMeta() ? stack.getItemMeta() : Bukkit.getItemFactory().getItemMeta(stack.getType()); + meta.setDisplayName(name); + stack.setItemMeta(meta); + named.setItem(stack); + } + } + + @Override + public @NotNull Class returnType() { + return String.class; + } + })); Classes.registerClass(new ClassInfo<>(Color.class, "color") .user("colou?rs?") @@ -840,7 +870,8 @@ public String toString(Config config, int flags) { public String toVariableNameString(Config config) { return this.toString(config, 0); } - })); + }) + .property(Property.NAME, ExpressionPropertyHandler.of(Config::name, String.class))); Classes.registerClass(new ClassInfo<>(Node.class, "node") .user("nodes?") @@ -867,53 +898,11 @@ public String toVariableNameString(Node node) { return this.toString(node, 0); } - })); + }) + .property(Property.NAME, ExpressionPropertyHandler.of(Node::getKey, String.class))); - Classes.registerClass(new ClassInfo<>(Script.class, "script") - .user("scripts?") - .name("Script") - .description("A script loaded by Skript.", - "Disabled scripts will report as being empty since their content has not been loaded.") - .usage("") - .examples("the current script") - .since("2.10") - .parser(new Parser