From c339816a7e31a9a5dbd5817fbde15cb6f6b2d13f Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Mon, 20 Jul 2026 13:36:13 -0400 Subject: [PATCH 01/11] Add alternative Patterns constructor (#8755) --- .../java/ch/njol/skript/util/Patterns.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/ch/njol/skript/util/Patterns.java b/src/main/java/ch/njol/skript/util/Patterns.java index 5f2b9061a29..486e911d77c 100644 --- a/src/main/java/ch/njol/skript/util/Patterns.java +++ b/src/main/java/ch/njol/skript/util/Patterns.java @@ -40,6 +40,24 @@ public Patterns(Object[][] info) { } } + /** + * Creates a new {@link Patterns} with the provided paired inputs. + * @param info Pattern-Value pairs, for example: + * + * new Patterns("pattern1", value1, "pattern2", value2); + * + */ + public Patterns(Object... info) { + patterns = new String[info.length / 2]; + types = new Object[info.length / 2]; + for (int i = 0; i < info.length; i += 2) { + int pattern = i / 2; + patterns[pattern] = (String) info[i]; + types[pattern] = info[i + 1]; + matchedPatterns.computeIfAbsent(info[i + 1], list -> new ArrayList<>()).add(pattern); + } + } + /** * Returns an array of the registered patterns. * @return An {@link java.lang.reflect.Array} of {@link String}s. From 40b51f651253d21261f817b0308d7f2853532f1e Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Thu, 30 Jul 2026 10:28:01 -0400 Subject: [PATCH 02/11] Remove 1.21.4 environment (#8784) --- .github/workflows/java-21-builds.yml | 4 ++-- .github/workflows/junit-21-builds.yml | 4 ++-- build.gradle | 2 +- .../environments/java21/paper-1.21.4.json | 17 ----------------- 4 files changed, 5 insertions(+), 22 deletions(-) delete mode 100644 src/test/skript/environments/java21/paper-1.21.4.json diff --git a/.github/workflows/java-21-builds.yml b/.github/workflows/java-21-builds.yml index 631b25d70c1..dfd4ffc9456 100644 --- a/.github/workflows/java-21-builds.yml +++ b/.github/workflows/java-21-builds.yml @@ -13,9 +13,9 @@ jobs: if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" uses: ./.github/workflows/parallelize-tests.yml with: - environments: 1.21.4,1.21.5,1.21.8,1.21.10,1.21.11 + environments: 1.21.5,1.21.8,1.21.10,1.21.11 java_version: 21 - parallel_jobs: 3 + parallel_jobs: 2 build: name: ${{ matrix.display }} diff --git a/.github/workflows/junit-21-builds.yml b/.github/workflows/junit-21-builds.yml index 71dcde9b041..4050da5d768 100644 --- a/.github/workflows/junit-21-builds.yml +++ b/.github/workflows/junit-21-builds.yml @@ -13,9 +13,9 @@ jobs: if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" uses: ./.github/workflows/parallelize-tests.yml with: - environments: 1.21.4,1.21.5,1.21.8,1.21.10,1.21.11 + environments: 1.21.5,1.21.8,1.21.10,1.21.11 java_version: 21 - parallel_jobs: 3 + parallel_jobs: 2 build: name: ${{ matrix.display }} diff --git a/build.gradle b/build.gradle index d447088cefe..586d1fbda18 100644 --- a/build.gradle +++ b/build.gradle @@ -295,7 +295,7 @@ tasks.register('JUnit') { // custom test task // usage: gradle customTest -PtestEnvs= -PtestEnvJavaVersion= -Ptimeout= -Pjunit=[true|false] // defaults: testEnvJavaVersion=latestJava, timeout=0, junit=false -// example: gradle customTest -PtestEnvs="java21/paper-1.21.4,java21/paper-1.21.8" -PtestEnvJavaVersion=21 -Ptimeout=600000 -Pjunit=true +// example: gradle customTest -PtestEnvs="java21/paper-1.21.5,java21/paper-1.21.8" -PtestEnvJavaVersion=21 -Ptimeout=600000 -Pjunit=true // get environments String propEnvs = project.hasProperty('testEnvs') ? project.property('testEnvs') as String : project.testEnv diff --git a/src/test/skript/environments/java21/paper-1.21.4.json b/src/test/skript/environments/java21/paper-1.21.4.json deleted file mode 100644 index 5957458a7a9..00000000000 --- a/src/test/skript/environments/java21/paper-1.21.4.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "paper-1.21.4", - "resources": [ - {"source": "server.properties.generic", "target": "server.properties"} - ], - "paperDownloads": [ - { - "version": "1.21.4", - "target": "paperclip.jar" - } - ], - "skriptTarget": "plugins/Skript.jar", - "commandLine": [ - "-Dcom.mojang.eula.agree=true", - "-jar", "paperclip.jar", "--nogui" - ] -} From 1000a66d4acd7f9ad7b691eb8c5b6e790f5c3901 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Fri, 31 Jul 2026 17:17:53 -0400 Subject: [PATCH 03/11] RegistryClassInfo Housekeeping (#8759) --- .../njol/skript/bukkitutil/BukkitUtils.java | 28 ++-- .../ch/njol/skript/classes/ClassInfo.java | 1 - .../skript/classes/data/BukkitClasses.java | 31 +---- .../classes/registry/RegistryClassInfo.java | 105 ++++++++++++-- .../java/ch/njol/skript/entity/CatData.java | 13 +- .../ch/njol/skript/entity/ChickenData.java | 57 +++----- .../java/ch/njol/skript/entity/CowData.java | 128 +++--------------- .../java/ch/njol/skript/entity/FrogData.java | 20 ++- .../java/ch/njol/skript/entity/PigData.java | 53 ++------ .../java/ch/njol/skript/entity/WolfData.java | 55 ++------ .../java/ch/njol/skript/util/PaperUtils.java | 33 +++-- .../damagesource/DamageSourceModule.java | 2 +- .../types/EnchantmentClassInfo.java | 3 +- .../entity/entitydata/ZombieNautilusData.java | 7 +- .../skript/bukkit/potion/PotionModule.java | 14 +- src/main/resources/plugin.yml | 2 +- 16 files changed, 225 insertions(+), 327 deletions(-) diff --git a/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java b/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java index 0b336b01b3a..cf73abf6bdd 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java @@ -7,6 +7,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.Keyed; import org.bukkit.Registry; import org.bukkit.inventory.EquipmentSlot; @@ -89,8 +90,25 @@ public static EquipmentSlot getEquipmentSlotFromIndex(int slotIndex) { String codeName, String languageNode ) { + // validate class if (!Skript.classExists(classPath)) return null; + Class registryClass; + try { + //noinspection unchecked + registryClass = (Class) Class.forName(classPath); + } catch (ClassNotFoundException e) { + Skript.debug("Could not retrieve the class with the path: '" + classPath + "'."); + throw new RuntimeException(e); + } + + // first, try better RegistryKey + if (PaperUtils.registryExists(registryName)) { + RegistryKey registryKey = PaperUtils.getBukkitRegistryKey(registryName); + return new RegistryClassInfo<>(registryClass, registryKey, codeName, languageNode); + } + + // otherwise, standard Registry Registry registry = null; if (BukkitUtils.registryExists(registryName)) { try { @@ -99,18 +117,8 @@ public static EquipmentSlot getEquipmentSlotFromIndex(int slotIndex) { } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } - } else if (PaperUtils.registryExists(registryName)) { - registry = PaperUtils.getBukkitRegistry(registryName); } if (registry != null) { - Class registryClass; - try { - //noinspection unchecked - registryClass = (Class) Class.forName(classPath); - } catch (ClassNotFoundException e) { - Skript.debug("Could not retrieve the class with the path: '" + classPath + "'."); - throw new RuntimeException(e); - } return new RegistryClassInfo<>(registryClass, registry, codeName, languageNode); } Skript.debug("There were no registries found for '" + registryName + "'."); diff --git a/src/main/java/ch/njol/skript/classes/ClassInfo.java b/src/main/java/ch/njol/skript/classes/ClassInfo.java index 2852a33ac26..ee47a37fea8 100644 --- a/src/main/java/ch/njol/skript/classes/ClassInfo.java +++ b/src/main/java/ch/njol/skript/classes/ClassInfo.java @@ -133,7 +133,6 @@ public ClassInfo user(final String... userInputPatterns) throws PatternSyntax * @see SimpleLiteral */ public ClassInfo defaultExpression(final DefaultExpression defaultExpression) { - assert this.defaultExpression == null; if (!defaultExpression.isDefault()) throw new IllegalArgumentException("defaultExpression.isDefault() must return true for the default expression of a class"); this.defaultExpression = defaultExpression; 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 3ad45cd73b2..7089b80c64a 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -11,6 +11,7 @@ import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.BlockUtils; import ch.njol.yggdrasil.Fields; +import io.papermc.paper.registry.RegistryKey; import io.papermc.paper.world.MoonPhase; import net.kyori.adventure.text.Component; import org.bukkit.*; @@ -326,7 +327,7 @@ public String toVariableNameString(InventoryHolder holder) { .since("2.0") .changer(DefaultChangers.itemChanger)); - Classes.registerClass(new RegistryClassInfo<>(Biome.class, Registry.BIOME, "biome", "biomes") + Classes.registerClass(new RegistryClassInfo<>(Biome.class, RegistryKey.BIOME, "biome", "biomes") .user("biomes?") .name("Biome") .description("All possible biomes Minecraft uses to generate a world.", @@ -600,7 +601,7 @@ public String[] getPatterns() { ExpressionPropertyHandler.of(GameRule::getName, String.class) )); - Classes.registerClass(new RegistryClassInfo<>(Attribute.class, Registry.ATTRIBUTE, "attributetype", "attribute types") + Classes.registerClass(new RegistryClassInfo<>(Attribute.class, RegistryKey.ATTRIBUTE, "attributetype", "attribute types") .user("attribute ?types?") .name("Attribute Type") .description("Represents the type of an attribute. Note that this type does not contain any numerical values." + @@ -659,14 +660,14 @@ public String[] getPatterns() { .description("Represents a change reason of an experience cooldown change event.") .since("2.10")); - Classes.registerClass(new RegistryClassInfo<>(Villager.Type.class, Registry.VILLAGER_TYPE, "villagertype", "villager types") + Classes.registerClass(new RegistryClassInfo<>(Villager.Type.class, RegistryKey.VILLAGER_TYPE, "villagertype", "villager types") .user("villager ?types?") .name("Villager Type") .description("Represents the different types of villagers. These are usually the biomes a villager can be from.") .after("biome") .since("2.10")); - Classes.registerClass(new RegistryClassInfo<>(Villager.Profession.class, Registry.VILLAGER_PROFESSION, "villagerprofession", "villager professions") + Classes.registerClass(new RegistryClassInfo<>(Villager.Profession.class, RegistryKey.VILLAGER_PROFESSION, "villagerprofession", "villager professions") .user("villager ?professions?") .name("Villager Profession") .description("Represents the different professions of villagers.") @@ -705,27 +706,7 @@ public String toVariableNameString(EntitySnapshot snapshot) { .description("Represents a banner pattern.") .since("2.10")); - ClassInfo patternTypeInfo; - Registry patternRegistry = Bukkit.getRegistry(PatternType.class); - if (patternRegistry != null) { - patternTypeInfo = new RegistryClassInfo<>(PatternType.class, patternRegistry, "bannerpatterntype", "banner pattern types"); - } else { - try { - Class patternClass = Class.forName("org.bukkit.block.banner.PatternType"); - if (patternClass.isEnum()) { - //noinspection unchecked,rawtypes - Class enumClass = (Class) patternClass; - //noinspection rawtypes,unchecked - patternTypeInfo = new EnumClassInfo<>(enumClass, "bannerpatterntype", "banner pattern types"); - } else { - throw new IllegalStateException("PatternType is neither an enum nor a valid registry."); - } - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - Classes.registerClass(patternTypeInfo + Classes.registerClass(new RegistryClassInfo<>(PatternType.class, RegistryKey.BANNER_PATTERN, "bannerpatterntype", "banner pattern types") .user("banner ?pattern ?types?") .name("Banner Pattern Type") .description("Represents the various banner patterns that can be applied to a banner.") diff --git a/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java b/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java index ee197f03600..c93bfb5856d 100644 --- a/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java +++ b/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java @@ -3,10 +3,16 @@ import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.DefaultExpression; +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.Keyed; import org.bukkit.Registry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Iterator; import java.util.function.Consumer; +import java.util.function.Supplier; /** * This class can be used for easily creating ClassInfos for {@link Registry}s. @@ -16,12 +22,87 @@ */ public class RegistryClassInfo extends ClassInfo { + private final @Nullable RegistryKey registryKey; + + /** + * @param registryClass The registry class + * @param registryKey The registry key + * @param codeName The name used in patterns + * @param languageNode The language node of the type + */ + public RegistryClassInfo(Class registryClass, RegistryKey registryKey, String codeName, String languageNode) { + this(registryClass, registryKey, RegistryAccess.registryAccess().getRegistry(registryKey), codeName, + languageNode, null, null); + } + + /** + * @param registryClass The registry class + * @param registryKey The registry key + * @param codeName The name used in patterns + * @param languageNode The language node of the type + * @param parseCallback A consumer to run on a successful parse. + */ + public RegistryClassInfo(Class registryClass, RegistryKey registryKey, String codeName, String languageNode, + Consumer parseCallback) { + this(registryClass, registryKey, RegistryAccess.registryAccess().getRegistry(registryKey), codeName, + languageNode, null, parseCallback); + } + + private RegistryClassInfo(Class registryClass, @Nullable RegistryKey registryKey, Registry registry, + String codeName, String languageNode, @Nullable DefaultExpression defaultExpression, + @Nullable Consumer parseCallback) { + super(registryClass, codeName); + + if (defaultExpression == null) { + defaultExpression = new EventValueExpression<>(registryClass); + } + if (parseCallback == null) { + parseCallback = ignored -> { }; + } + + this.registryKey = registryKey; + RegistryParser registryParser = new RegistryParser<>(registry, languageNode, parseCallback); + usage(registryParser.getCombinedPatterns()) + .supplier(registry::iterator) + .serializer(new RegistrySerializer<>(registry)) + .defaultExpression(defaultExpression) + .parser(registryParser); + } + + public @Nullable RegistryKey registryKey() { + return registryKey; + } + + @Override + public @NotNull RegistryParser getParser() { + //noinspection ConstantConditions, unchecked + return (RegistryParser) super.getParser(); + } + + @Override + public @NotNull RegistrySerializer getSerializer() { + //noinspection ConstantConditions, unchecked + return (RegistrySerializer) super.getSerializer(); + } + + @Override + public @NotNull Supplier> getSupplier() { + //noinspection ConstantConditions + return super.getSupplier(); + } + + /* + * Legacy Constructors + */ + /** * @param registryClass The registry class * @param registry The registry * @param codeName The name used in patterns * @param languageNode The language node of the type + * @deprecated Use {@link #RegistryClassInfo(Class, RegistryKey, String, String)}. */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode) { this(registryClass, registry, codeName, languageNode, new EventValueExpression<>(registryClass)); } @@ -32,7 +113,9 @@ public RegistryClassInfo(Class registryClass, Registry registry, String co * @param codeName The name used in patterns * @param languageNode The language node of the type * @param parseCallback A consumer to run on a successful parse. + * @deprecated Use {@link #RegistryClassInfo(Class, RegistryKey, String, String, Consumer)}. */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, Consumer parseCallback) { this(registryClass, registry, codeName, languageNode, new EventValueExpression<>(registryClass), parseCallback); } @@ -43,8 +126,11 @@ public RegistryClassInfo(Class registryClass, Registry registry, String co * @param codeName The name used in patterns * @param languageNode The language node of the type * @param defaultExpression The default expression of the type + * @deprecated Use {@link #RegistryClassInfo(Class, RegistryKey, String, String)} with {@link #defaultExpression(DefaultExpression)}. */ - public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, DefaultExpression defaultExpression) { + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, + DefaultExpression defaultExpression) { this(registryClass, registry, codeName, languageNode, defaultExpression, ignored -> {}); } @@ -55,15 +141,13 @@ public RegistryClassInfo(Class registryClass, Registry registry, String co * @param languageNode The language node of the type * @param defaultExpression The default expression of the type * @param parseCallback A consumer to run on a successful parse. + * @deprecated Use {@link #RegistryClassInfo(Class, RegistryKey, String, String, Consumer)} + * with {@link #defaultExpression(DefaultExpression)}. */ - public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, DefaultExpression defaultExpression, Consumer parseCallback) { - super(registryClass, codeName); - RegistryParser registryParser = new RegistryParser<>(registry, languageNode, parseCallback); - usage(registryParser.getCombinedPatterns()) - .supplier(registry::iterator) - .serializer(new RegistrySerializer<>(registry)) - .defaultExpression(defaultExpression) - .parser(registryParser); + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, + DefaultExpression defaultExpression, Consumer parseCallback) { + this(registryClass, null, registry, codeName, languageNode, defaultExpression, parseCallback); } /** @@ -89,7 +173,8 @@ public RegistryClassInfo(Class registryClass, Registry registry, String co * @deprecated {@code registerComparator} is no longer necessary. */ @Deprecated(since = "2.16", forRemoval = true) - public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, DefaultExpression defaultExpression, boolean registerComparator) { + public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, + DefaultExpression defaultExpression, boolean registerComparator) { this(registryClass, registry, codeName, languageNode, defaultExpression); } diff --git a/src/main/java/ch/njol/skript/entity/CatData.java b/src/main/java/ch/njol/skript/entity/CatData.java index a49ad39205f..e387bce9d0c 100644 --- a/src/main/java/ch/njol/skript/entity/CatData.java +++ b/src/main/java/ch/njol/skript/entity/CatData.java @@ -1,15 +1,12 @@ package ch.njol.skript.entity; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.classes.EnumClassInfo; import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; -import org.bukkit.Registry; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Cat; import org.bukkit.entity.Cat.Type; import org.jetbrains.annotations.NotNull; @@ -22,13 +19,7 @@ public class CatData extends EntityData { private static final Type[] TYPES; static { - ClassInfo catTypeClassInfo; - if (BukkitUtils.registryExists("CAT_VARIANT")) { - catTypeClassInfo = new RegistryClassInfo<>(Cat.Type.class, Registry.CAT_VARIANT, "cattype", "cat types"); - } else { - //noinspection unchecked, rawtypes - it is an enum on other versions - catTypeClassInfo = new EnumClassInfo<>((Class) Cat.Type.class, "cattype", "cat types"); - } + var catTypeClassInfo = new RegistryClassInfo<>(Cat.Type.class, RegistryKey.CAT_VARIANT, "cattype", "cat types"); Classes.registerClass(catTypeClassInfo .user("cat ?(type|race)s?") .name("Cat Type") diff --git a/src/main/java/ch/njol/skript/entity/ChickenData.java b/src/main/java/ch/njol/skript/entity/ChickenData.java index c4e414e76ef..46176e0697f 100644 --- a/src/main/java/ch/njol/skript/entity/ChickenData.java +++ b/src/main/java/ch/njol/skript/entity/ChickenData.java @@ -1,13 +1,12 @@ package ch.njol.skript.entity; -import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Chicken; import org.bukkit.entity.Chicken.Variant; import org.jetbrains.annotations.NotNull; @@ -17,21 +16,11 @@ public class ChickenData extends EntityData { - private static final boolean VARIANTS_ENABLED; - private static final Object[] VARIANTS; + private static final Variant[] VARIANTS; static { - ClassInfo chickenVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Chicken$Variant", - "CHICKEN_VARIANT", - "chickenvariant", - "chicken variants" - ); - if (chickenVariantClassInfo == null) { - // Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Chicken.Variant' (1.21.4-) - chickenVariantClassInfo = new ClassInfo<>(ChickenVariantDummy.class, "chickenvariant"); - } - Classes.registerClass(chickenVariantClassInfo + var chickenVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.CHICKEN_VARIANT, "chickenvariant", "chicken variants"); + Classes.registerClass(chickenVariantInfo .user("chicken ?variants?") .name("Chicken Variant") .description("Represents the variant of a chicken entity.", @@ -40,38 +29,31 @@ public class ChickenData extends EntityData { .requiredPlugins("Minecraft 1.21.5+") .documentationId("ChickenVariant") ); + VARIANTS = Iterators.toArray(chickenVariantInfo.getSupplier().get(), Variant.class); register(ChickenData.class, "chicken", Chicken.class, "chicken"); - if (Skript.classExists("org.bukkit.entity.Chicken$Variant")) { - VARIANTS_ENABLED = true; - VARIANTS = Iterators.toArray(Classes.getExactClassInfo(Chicken.Variant.class).getSupplier().get(), Chicken.Variant.class); - } else { - VARIANTS_ENABLED = false; - VARIANTS = null; - } } - private @Nullable Object variant = null; + private @Nullable Variant variant = null; public ChickenData() {} - // TODO: When safe, 'variant' should have the type changed to 'Chicken.Variant' when 1.21.6 is minimum supported version - public ChickenData(@Nullable Object variant) { + public ChickenData(@Nullable Variant variant) { this.variant = variant; } @Override protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPattern, ParseResult parseResult) { - if (VARIANTS_ENABLED && exprs[0] != null) { + if (exprs[0] != null) { //noinspection unchecked - variant = ((Literal) exprs[0]).getSingle(); + variant = ((Literal) exprs[0]).getSingle(); } return true; } @Override protected boolean init(@Nullable Class entityClass, @Nullable Chicken chicken) { - if (chicken != null && VARIANTS_ENABLED) { + if (chicken != null) { variant = chicken.getVariant(); } return true; @@ -79,13 +61,11 @@ protected boolean init(@Nullable Class entityClass, @Nullable @Override public void set(Chicken chicken) { - if (VARIANTS_ENABLED) { - Variant variant = (Variant) this.variant; - if (variant == null) - variant = (Variant) CollectionUtils.getRandom(VARIANTS); - assert variant != null; - chicken.setVariant(variant); - } + Variant variant = this.variant; + if (variant == null) + variant = CollectionUtils.getRandom(VARIANTS); + assert variant != null; + chicken.setVariant(variant); } @Override @@ -122,9 +102,4 @@ public boolean isSupertypeOf(EntityData entityData) { return dataMatch(variant, other.variant); } - /** - * A dummy/placeholder class to ensure working operation on MC versions that do not have `Chicken.Variant` - */ - public static class ChickenVariantDummy {} - } diff --git a/src/main/java/ch/njol/skript/entity/CowData.java b/src/main/java/ch/njol/skript/entity/CowData.java index 242a791b064..fa05e6fcb5f 100644 --- a/src/main/java/ch/njol/skript/entity/CowData.java +++ b/src/main/java/ch/njol/skript/entity/CowData.java @@ -1,43 +1,26 @@ package ch.njol.skript.entity; -import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Cow; import org.bukkit.entity.Cow.Variant; -import org.bukkit.entity.Entity; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.Objects; public class CowData extends EntityData { - private static final boolean VARIANTS_ENABLED; private static final Object[] VARIANTS; - private static final Class COW_CLASS; - private static final @Nullable Method getVariantMethod; - private static final @Nullable Method setVariantMethod; static { - ClassInfo cowVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Cow$Variant", - "COW_VARIANT", - "cowvariant", - "cow variants" - ); - if (cowVariantClassInfo == null) { - // Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Cow.Variant' (1.21.4-) - cowVariantClassInfo = new ClassInfo<>(CowVariantDummy.class, "cowvariant"); - } - Classes.registerClass(cowVariantClassInfo + var cowVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.COW_VARIANT, "cowvariant", "cow variants"); + Classes.registerClass(cowVariantInfo .user("cow ?variants?") .name("Cow Variant") .description("Represents the variant of a cow entity.", @@ -46,78 +29,53 @@ public class CowData extends EntityData { .requiredPlugins("Minecraft 1.21.5+") .documentationId("CowVariant") ); + VARIANTS = Iterators.toArray(cowVariantInfo.getSupplier().get(), Variant.class); - Class cowClass = null; - - try { - //noinspection unchecked - cowClass = (Class) Class.forName("org.bukkit.entity.Cow"); - } catch (Exception ignored) {} - - COW_CLASS = cowClass; - register(CowData.class, "cow", COW_CLASS, 0, "cow"); - if (Skript.classExists("org.bukkit.entity.Cow$Variant")) { - VARIANTS_ENABLED = true; - VARIANTS = Iterators.toArray(Classes.getExactClassInfo(Cow.Variant.class).getSupplier().get(), Cow.Variant.class); - try { - getVariantMethod = COW_CLASS.getDeclaredMethod("getVariant"); - setVariantMethod = COW_CLASS.getDeclaredMethod("setVariant", Cow.Variant.class); - } catch (Exception e) { - throw new RuntimeException("Could not retrieve get/set variant methods for Cow.", e); - } - } else { - VARIANTS_ENABLED = false; - VARIANTS = null; - getVariantMethod = null; - setVariantMethod = null; - } + register(CowData.class, "cow", Cow.class, 0, "cow"); } - private @Nullable Object variant = null; + private @Nullable Variant variant = null; public CowData() {} - // TODO: When the api-version is 1.21.5, 'variant' should have the type changed to 'Cow.Variant' and reflection can be removed - public CowData(@Nullable Object variant) { + public CowData(@Nullable Variant variant) { this.variant = variant; } @Override protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPattern, ParseResult parseResult) { - if (VARIANTS_ENABLED && exprs[0] != null) { + if (exprs[0] != null) { //noinspection unchecked - variant = ((Literal) exprs[0]).getSingle(); + variant = ((Literal) exprs[0]).getSingle(); } return true; } @Override protected boolean init(@Nullable Class entityClass, @Nullable Cow cow) { - if (cow != null && VARIANTS_ENABLED) { - variant = getVariant(cow); + if (cow != null) { + variant = cow.getVariant(); } return true; } @Override public void set(Cow cow) { - if (VARIANTS_ENABLED) { - Variant variant = (Variant) this.variant; - if (variant == null) - variant = (Variant) CollectionUtils.getRandom(VARIANTS); - assert variant != null; - setVariant(cow, variant); - } + Variant variant = this.variant; + if (variant == null) + variant = (Variant) CollectionUtils.getRandom(VARIANTS); + assert variant != null; + cow.setVariant(variant); } @Override protected boolean match(Cow cow) { - return variant == null || getVariant(cow) == variant; + return variant == null || cow.getVariant() == variant; } @Override public Class getType() { - return COW_CLASS; + return Cow.class; } @Override @@ -144,52 +102,4 @@ public boolean isSupertypeOf(EntityData entityData) { return dataMatch(variant, other.variant); } - /** - * Due to the addition of 'AbstractCow' and 'api-version' being '1.19' - * This helper method is required in order to set the {@link #variant} of the {@link Cow} - * @param cow The {@link Cow} to set the variant - */ - public void setVariant(Cow cow) { - setVariant(cow, variant); - } - - /** - * Due to the addition of 'AbstractCow' and 'api-version' being '1.19' - * This helper method is required in order to set the {@code object} of the {@link Cow} - * @param cow The {@link Cow} to set the variant - * @param object The 'Cow.Variant' - */ - public void setVariant(Cow cow, Object object) { - if (!VARIANTS_ENABLED || setVariantMethod == null) - return; - Entity entity = COW_CLASS.cast(cow); - try { - setVariantMethod.invoke(entity, (Cow.Variant) object); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - /** - * Due to the addition of 'AbstractCow' and 'api-version' being '1.19' - * This helper method is required in order to get the 'Cow.Variant' of the {@link Cow} - * @param cow The {@link Cow} to get the variant - * @return The 'Cow.Variant' - */ - public @Nullable Object getVariant(Cow cow) { - if (!VARIANTS_ENABLED || getVariantMethod == null) - return null; - Entity entity = COW_CLASS.cast(cow); - try { - return getVariantMethod.invoke(entity); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - /** - * A dummy/placeholder class to ensure working operation on MC versions that do not have 'Cow.Variant' - */ - public static class CowVariantDummy {} - } diff --git a/src/main/java/ch/njol/skript/entity/FrogData.java b/src/main/java/ch/njol/skript/entity/FrogData.java index e5021428cd0..f775c2380a9 100644 --- a/src/main/java/ch/njol/skript/entity/FrogData.java +++ b/src/main/java/ch/njol/skript/entity/FrogData.java @@ -1,12 +1,13 @@ package ch.njol.skript.entity; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.Patterns; import ch.njol.util.coll.CollectionUtils; +import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Frog; import org.bukkit.entity.Frog.Variant; import org.jetbrains.annotations.NotNull; @@ -26,16 +27,8 @@ public class FrogData extends EntityData { private static final Variant[] VARIANTS; static { - EntityData.register(FrogData.class, "frog", Frog.class, 0, PATTERNS.getPatterns()); - VARIANTS = new Variant[]{Variant.TEMPERATE, Variant.WARM, Variant.COLD}; - ClassInfo frogVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Frog$Variant", - "FROG_VARIANT", - "frogvariant", - "frog variants" - ); - assert frogVariantClassInfo != null; - Classes.registerClass(frogVariantClassInfo + var frogVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.FROG_VARIANT, "frogvariant", "frog variants"); + Classes.registerClass(frogVariantInfo .user("frog ?variants?") .name("Frog Variant") .description("Represents the variant of a frog entity.", @@ -43,6 +36,9 @@ public class FrogData extends EntityData { .since("2.13") .documentationId("FrogVariant") ); + VARIANTS = Iterators.toArray(frogVariantInfo.getSupplier().get(), Variant.class); + + EntityData.register(FrogData.class, "frog", Frog.class, 0, PATTERNS.getPatterns()); } private @Nullable Variant variant = null; diff --git a/src/main/java/ch/njol/skript/entity/PigData.java b/src/main/java/ch/njol/skript/entity/PigData.java index 6fc309307c8..0be397c9173 100644 --- a/src/main/java/ch/njol/skript/entity/PigData.java +++ b/src/main/java/ch/njol/skript/entity/PigData.java @@ -1,8 +1,6 @@ package ch.njol.skript.entity; -import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; @@ -10,7 +8,9 @@ import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Pig; +import org.bukkit.entity.Pig.Variant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,8 +18,7 @@ public class PigData extends EntityData { - private static final boolean VARIANTS_ENABLED; - private static final Object[] VARIANTS; + private static final Variant[] VARIANTS; private static final Patterns PATTERNS = new Patterns<>(new Object[][]{ {"pig", Kleenean.UNKNOWN}, {"saddled pig", Kleenean.TRUE}, @@ -27,17 +26,8 @@ public class PigData extends EntityData { }); static { - ClassInfo pigVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Pig$Variant", - "PIG_VARIANT", - "pigvariant", - "pig variants" - ); - if (pigVariantClassInfo == null) { - // Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Pig.Variant' (1.21.4-) - pigVariantClassInfo = new ClassInfo<>(PigVariantDummy.class, "pigvariant"); - } - Classes.registerClass(pigVariantClassInfo + var pigVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.PIG_VARIANT, "pigvariant", "pig variants"); + Classes.registerClass(pigVariantInfo .user("pig ?variants?") .name("Pig Variant") .description("Represents the variant of a pig entity.", @@ -45,24 +35,17 @@ public class PigData extends EntityData { .since("2.12") .requiredPlugins("Minecraft 1.21.5+") .documentationId("PigVariant")); + VARIANTS = Iterators.toArray(pigVariantInfo.getSupplier().get(), Pig.Variant.class); register(PigData.class, "pig", Pig.class, 0, PATTERNS.getPatterns()); - if (Skript.classExists("org.bukkit.entity.Pig$Variant")) { - VARIANTS_ENABLED = true; - VARIANTS = Iterators.toArray(Classes.getExactClassInfo(Pig.Variant.class).getSupplier().get(), Pig.Variant.class); - } else { - VARIANTS_ENABLED = false; - VARIANTS = null; - } } private Kleenean saddled = Kleenean.UNKNOWN; - private @Nullable Object variant = null; + private @Nullable Variant variant; public PigData() {} - // TODO: When safe, 'variant' should have the type changed to 'Pig.Variant' when 1.21.5 is minimum supported version - public PigData(@Nullable Kleenean saddled, @Nullable Object variant) { + public PigData(@Nullable Kleenean saddled, @Nullable Variant variant) { this.saddled = saddled != null ? saddled : Kleenean.UNKNOWN; this.variant = variant; super.codeNameIndex = PATTERNS.getMatchedPattern(this.saddled, 0).orElse(0); @@ -71,7 +54,7 @@ public PigData(@Nullable Kleenean saddled, @Nullable Object variant) { @Override protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPattern, ParseResult parseResult) { saddled = PATTERNS.getInfo(matchedCodeName); - if (VARIANTS_ENABLED && exprs[0] != null) { + if (exprs[0] != null) { //noinspection unchecked variant = ((Literal) exprs[0]).getSingle(); } @@ -83,8 +66,7 @@ protected boolean init(@Nullable Class entityClass, @Nullable Pig if (pig != null) { saddled = Kleenean.get(pig.hasSaddle()); super.codeNameIndex = PATTERNS.getMatchedPattern(saddled, 0).orElse(0); - if (VARIANTS_ENABLED) - variant = pig.getVariant(); + variant = pig.getVariant(); } return true; } @@ -92,11 +74,9 @@ protected boolean init(@Nullable Class entityClass, @Nullable Pig @Override public void set(Pig pig) { pig.setSaddle(saddled.isTrue()); - if (VARIANTS_ENABLED) { - Object finalVariant = variant != null ? variant : CollectionUtils.getRandom(VARIANTS); - assert finalVariant != null; - pig.setVariant((Pig.Variant) finalVariant); - } + Variant finalVariant = variant != null ? variant : CollectionUtils.getRandom(VARIANTS); + assert finalVariant != null; + pig.setVariant(finalVariant); } @Override @@ -139,9 +119,4 @@ public boolean isSupertypeOf(EntityData entityData) { return variant == null || variant == other.variant; } - /** - * A dummy/placeholder class to ensure working operation on MC versions that do not have `Pig.Variant` - */ - public static class PigVariantDummy {} - } diff --git a/src/main/java/ch/njol/skript/entity/WolfData.java b/src/main/java/ch/njol/skript/entity/WolfData.java index a1f0613212c..3530ac29bec 100644 --- a/src/main/java/ch/njol/skript/entity/WolfData.java +++ b/src/main/java/ch/njol/skript/entity/WolfData.java @@ -1,8 +1,6 @@ package ch.njol.skript.entity; -import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; @@ -11,8 +9,10 @@ import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.DyeColor; import org.bukkit.entity.Wolf; +import org.bukkit.entity.Wolf.Variant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,21 +30,11 @@ public record WolfStates(Kleenean angry, Kleenean tamed) {} {"peaceful wolf", new WolfStates(Kleenean.FALSE, Kleenean.UNKNOWN)} }); - private static final boolean VARIANTS_ENABLED; - private static final Object[] VARIANTS; + private static final Variant[] VARIANTS; static { - ClassInfo wolfVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Wolf$Variant", - "WOLF_VARIANT", - "wolfvariant", - "wolf variants" - ); - if (wolfVariantClassInfo == null) { - // Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Wolf.Variant' (1.20.4-) - wolfVariantClassInfo = new ClassInfo<>(WolfVariantDummy.class, "wolfvariant"); - } - Classes.registerClass(wolfVariantClassInfo + var wolfVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.WOLF_VARIANT, "wolfvariant", "wolf variants"); + Classes.registerClass(wolfVariantInfo .user("wolf ?variants?") .name("Wolf Variant") .description("Represents the variant of a wolf entity.", @@ -52,18 +42,12 @@ public record WolfStates(Kleenean angry, Kleenean tamed) {} .since("2.10") .requiredPlugins("Minecraft 1.21+") .documentationId("WolfVariant")); + VARIANTS = Iterators.toArray(wolfVariantInfo.getSupplier().get(), Variant.class); EntityData.register(WolfData.class, "wolf", Wolf.class, 0, PATTERNS.getPatterns()); - if (Skript.classExists("org.bukkit.entity.Wolf$Variant")) { - VARIANTS_ENABLED = true; - VARIANTS = Iterators.toArray(Classes.getExactClassInfo(Wolf.Variant.class).getSupplier().get(), Wolf.Variant.class); - } else { - VARIANTS_ENABLED = false; - VARIANTS = null; - } } - private @Nullable Object variant = null; + private @Nullable Variant variant = null; private @Nullable DyeColor collarColor = null; private Kleenean isAngry = Kleenean.UNKNOWN; private Kleenean isTamed = Kleenean.UNKNOWN; @@ -94,9 +78,9 @@ protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPatte assert state != null; isAngry = state.angry; isTamed = state.tamed; - if (exprs[0] != null && VARIANTS_ENABLED) { + if (exprs[0] != null) { //noinspection unchecked - variant = ((Literal) exprs[0]).getSingle(); + variant = ((Literal) exprs[0]).getSingle(); } if (exprs[1] != null) { //noinspection unchecked @@ -111,8 +95,7 @@ protected boolean init(@Nullable Class entityClass, @Nullable Wo isAngry = Kleenean.get(wolf.isAngry()); isTamed = Kleenean.get(wolf.isTamed()); collarColor = wolf.getCollarColor(); - if (VARIANTS_ENABLED) - variant = wolf.getVariant(); + variant = wolf.getVariant(); super.codeNameIndex = PATTERNS.getMatchedPattern(new WolfStates(isAngry, isTamed), 0).orElse(0); } return true; @@ -124,11 +107,9 @@ public void set(Wolf wolf) { wolf.setTamed(isTamed.isTrue()); if (collarColor != null) wolf.setCollarColor(collarColor); - if (VARIANTS_ENABLED) { - Object variantSet = variant != null ? variant : CollectionUtils.getRandom(VARIANTS); - assert variantSet != null; - wolf.setVariant((Wolf.Variant) variantSet); - } + Variant variantSet = variant != null ? variant : CollectionUtils.getRandom(VARIANTS); + assert variantSet != null; + wolf.setVariant(variantSet); } @Override @@ -158,8 +139,7 @@ protected int hashCode_i() { result = prime * result + isAngry.hashCode(); result = prime * result + isTamed.hashCode(); result = prime * result + Objects.hashCode(collarColor); - if (VARIANTS_ENABLED) - result = prime * result + Objects.hashCode(variant); + result = prime * result + Objects.hashCode(variant); return result; } @@ -189,9 +169,4 @@ public boolean isSupertypeOf(EntityData entityData) { return dataMatch(variant, other.variant); } - /** - * A dummy/placeholder class to ensure working operation on MC versions that do not have `Wolf.Variant` - */ - public static class WolfVariantDummy {}; - } diff --git a/src/main/java/ch/njol/skript/util/PaperUtils.java b/src/main/java/ch/njol/skript/util/PaperUtils.java index 6f312a9c50e..2106fed72d0 100644 --- a/src/main/java/ch/njol/skript/util/PaperUtils.java +++ b/src/main/java/ch/njol/skript/util/PaperUtils.java @@ -13,9 +13,9 @@ public class PaperUtils { private static final boolean REGISTRY_KEY_EXISTS = Skript.classExists("io.papermc.paper.registry.RegistryKey"); /** - * Check if a registry exists within {@link RegistryKey} - * @param registry Registry to check for (Fully qualified name of registry) - * @return True if registry exists else false + * Check if a registry exists within {@link RegistryKey}. + * @param registry Registry to check for. + * @return True if registry exists else false. */ public static boolean registryExists(String registry) { return REGISTRY_ACCESS_EXISTS @@ -24,21 +24,34 @@ public static boolean registryExists(String registry) { } /** - * Gets the Bukkit {@link Registry} from Paper's {@link RegistryKey}. - * @param registry Registry to get (Fully qualified name of registry). + * Gets a Paper {@link RegistryKey}. + * @param registry Registry key to get. * @return The Bukkit {@link Registry} if registry exists else {@code null}. */ - public static @Nullable Registry getBukkitRegistry(String registry) { + public static @Nullable RegistryKey getBukkitRegistryKey(String registry) { if (!registryExists(registry)) return null; - RegistryKey registryKey; + RegistryKey registryKey; try { - registryKey = (RegistryKey) RegistryKey.class.getField(registry).get(null); + //noinspection unchecked + registryKey = (RegistryKey) RegistryKey.class.getField(registry).get(null); } catch (NoSuchFieldException | IllegalAccessException ignored) { return null; } - //noinspection unchecked - return (Registry) RegistryAccess.registryAccess().getRegistry(registryKey); + return registryKey; + } + + /** + * Gets the Bukkit {@link Registry} from Paper's {@link RegistryKey}. + * @param registry Registry to get. + * @return The Bukkit {@link Registry} if registry exists else {@code null}. + */ + public static @Nullable Registry getBukkitRegistry(String registry) { + RegistryKey registryKey = getBukkitRegistryKey(registry); + if (registryKey == null) { + return null; + } + return RegistryAccess.registryAccess().getRegistry(registryKey); } } diff --git a/src/main/java/org/skriptlang/skript/bukkit/damagesource/DamageSourceModule.java b/src/main/java/org/skriptlang/skript/bukkit/damagesource/DamageSourceModule.java index f3ea90c5eaf..aaaeaf03f4c 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/damagesource/DamageSourceModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/damagesource/DamageSourceModule.java @@ -43,7 +43,7 @@ protected void initSelf(SkriptAddon addon) { ); Classes.registerClass( - new RegistryClassInfo<>(DamageType.class, RegistryAccess.registryAccess().getRegistry(RegistryKey.DAMAGE_TYPE), + new RegistryClassInfo<>(DamageType.class, RegistryKey.DAMAGE_TYPE, "damagetype", "damage types") .user("damage ?types?") .name("Damage Type") diff --git a/src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentClassInfo.java b/src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentClassInfo.java index c5b83769200..57d8eb83f71 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentClassInfo.java +++ b/src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentClassInfo.java @@ -1,7 +1,6 @@ package org.skriptlang.skript.bukkit.enchantments.types; import ch.njol.skript.classes.registry.RegistryClassInfo; -import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; import org.bukkit.enchantments.Enchantment; import org.jetbrains.annotations.ApiStatus; @@ -10,7 +9,7 @@ public class EnchantmentClassInfo extends RegistryClassInfo { public EnchantmentClassInfo() { - super(Enchantment.class, RegistryAccess.registryAccess().getRegistry(RegistryKey.ENCHANTMENT), + super(Enchantment.class, RegistryKey.ENCHANTMENT, "enchantment", "enchantments"); user("enchantments?") diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/entitydata/ZombieNautilusData.java b/src/main/java/org/skriptlang/skript/bukkit/entity/entitydata/ZombieNautilusData.java index 8084009f35b..ed6eadc59e0 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/entity/entitydata/ZombieNautilusData.java +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/entitydata/ZombieNautilusData.java @@ -8,6 +8,7 @@ import ch.njol.skript.variables.Variables; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; +import com.google.common.collect.Iterators; import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; import org.bukkit.Registry; @@ -26,14 +27,14 @@ public static void register() { EntityData.register(ZombieNautilusData.class, "zombie nautilus", ZombieNautilus.class, 0, "zombie nautilus"); Variables.yggdrasil.registerSingleClass(Variant.class, "ZombieNautilus.Variant"); - Registry<@NotNull Variant> variantRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.ZOMBIE_NAUTILUS_VARIANT); - VARIANTS = variantRegistry.stream().toArray(Variant[]::new); - Classes.registerClass(new RegistryClassInfo<>(Variant.class, variantRegistry, "zombienautilusvariant", "zombie nautilus variants") + var zombieNautilusVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.ZOMBIE_NAUTILUS_VARIANT, "zombienautilusvariant", "zombie nautilus variants"); + Classes.registerClass(zombieNautilusVariantInfo .user("zombie ?nautilus ?variants?") .name("Zombie Nautilus Variant") .description("Represents the variant of a zombie nautilus.") .since("2.14") .documentationId("ZombieNautilusVariant")); + VARIANTS = Iterators.toArray(zombieNautilusVariantInfo.getSupplier().get(), Variant.class); } private Kleenean isTamed = Kleenean.UNKNOWN; diff --git a/src/main/java/org/skriptlang/skript/bukkit/potion/PotionModule.java b/src/main/java/org/skriptlang/skript/bukkit/potion/PotionModule.java index da3d82029cc..813f8d8862b 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/potion/PotionModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/potion/PotionModule.java @@ -1,19 +1,17 @@ package org.skriptlang.skript.bukkit.potion; import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; import ch.njol.skript.classes.*; import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.registrations.Classes; import ch.njol.yggdrasil.Fields; -import org.bukkit.Registry; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.event.entity.EntityPotionEffectEvent; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectTypeCategory; -import org.jetbrains.annotations.NotNull; import org.skriptlang.skript.addon.AddonModule; import org.skriptlang.skript.addon.HierarchicalAddonModule; import org.skriptlang.skript.addon.SkriptAddon; @@ -129,15 +127,7 @@ protected boolean canBeInstantiated() { } })); - Registry<@NotNull PotionEffectType> petRegistry; - if (BukkitUtils.registryExists("MOB_EFFECT")) { // Paper (1.21.4) - petRegistry = Registry.MOB_EFFECT; - } else if (BukkitUtils.registryExists("EFFECT")) { // Bukkit (1.20.3) - petRegistry = Registry.EFFECT; - } else { - throw new IllegalStateException("Potion effect registry does not exist"); - } - Classes.registerClass(new RegistryClassInfo<>(PotionEffectType.class, petRegistry, "potioneffecttype", "potion effect types") + Classes.registerClass(new RegistryClassInfo<>(PotionEffectType.class, RegistryKey.MOB_EFFECT, "potioneffecttype", "potion effect types") .user("potion ?effect ?types?") .name("Potion Effect Type") .description("A potion effect type, e.g. 'strength' or 'swiftness'.") diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fae9ec3e6fa..89543e79207 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -29,7 +29,7 @@ website: https://skriptlang.org main: ch.njol.skript.Skript version: @version@ -api-version: 1.21 +api-version: 1.21.5 commands: skript: From dd20ff25c305afadd4045fcda3ac3f5104fa7d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nova=20=E2=9C=A9?= <150085350+novystar@users.noreply.github.com> Date: Mon, 3 Aug 2026 21:04:49 +0100 Subject: [PATCH 04/11] Add Item Model Support (#8779) --- .../skript/bukkit/item/ItemModule.java | 2 + .../bukkit/item/elements/ExprItemModel.java | 82 +++++++++++++++++++ .../item/elements/ExprItemWithModel.java | 80 ++++++++++++++++++ .../skript/tests/bukkit/item/ExprItemModel.sk | 46 +++++++++++ 4 files changed, 210 insertions(+) create mode 100644 src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemModel.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemWithModel.java create mode 100644 src/test/skript/tests/bukkit/item/ExprItemModel.sk diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/ItemModule.java b/src/main/java/org/skriptlang/skript/bukkit/item/ItemModule.java index d3b5d311cda..759f7a4b236 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/item/ItemModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/item/ItemModule.java @@ -24,7 +24,9 @@ public Iterable children() { @Override public void loadSelf(SkriptAddon addon) { register(addon, + ExprItemModel::register, ExprItemWithLore::register, + ExprItemWithModel::register, ExprLore::register ); } diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemModel.java b/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemModel.java new file mode 100644 index 00000000000..45a69823aa6 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemModel.java @@ -0,0 +1,82 @@ +package org.skriptlang.skript.bukkit.item.elements; + +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.bukkitutil.NamespacedUtils; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.NamespacedKey; +import org.bukkit.event.Event; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Item Model") +@Description(""" + The item model of an item. \ + Accepts a Namespaced Key which takes the form of "namespace:key", e.g. "minecraft:dirt". \ + See this article for more detail. \ + """) +@Example("set the item model of player's held item to \"diamond\"") +@Example("set the item model of {_item} to \"minecraft:dirt\"") +@Since("INSERT VERSION") +public class ExprItemModel extends SimplePropertyExpression { + + public static void register(SyntaxRegistry syntaxRegistry) { + syntaxRegistry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder(ExprItemModel.class, String.class, "item model", "itemtypes", true) + .supplier(ExprItemModel::new) + .build() + ); + } + + @Override + public @Nullable String convert(ItemType from) { + NamespacedKey key = from.getItemMeta().getItemModel(); + if (key == null) + return null; + + return key.asString(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, RESET -> CollectionUtils.array(String.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + NamespacedKey key = null; + if (delta != null) { + key = NamespacedUtils.checkValidationAndSend((String) delta[0], this); + if (key == null) { + return; + } + } + + for (ItemType item : getExpr().getArray(event)) { + ItemMeta meta = item.getItemMeta(); + meta.setItemModel(key); + item.setItemMeta(meta); + } + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + protected String getPropertyName() { + return "item model"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemWithModel.java b/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemWithModel.java new file mode 100644 index 00000000000..2e390b46d02 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemWithModel.java @@ -0,0 +1,80 @@ +package org.skriptlang.skript.bukkit.item.elements; + +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.bukkitutil.NamespacedUtils; +import ch.njol.skript.doc.*; +import ch.njol.skript.expressions.base.PropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.NamespacedKey; +import org.bukkit.event.Event; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.DefaultSyntaxInfos; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Item With Model") +@Description(""" + Returns a copy of an item with a custom item model. \ + Accepts a Namespaced Key which takes the form of "namespace:key", e.g. "minecraft:dirt". \ + See this article for more detail. \ + """) +@Example(""" + set {_item} to emerald with model "minecraft:diamond" named "fake diamond"' + give {_item} to player + """) +@Since("INSERT VERSION") +@Keywords("item model") +public class ExprItemWithModel extends PropertyExpression { + + public static void register(SyntaxRegistry syntaxRegistry) { + syntaxRegistry.register( + SyntaxRegistry.EXPRESSION, + DefaultSyntaxInfos.Expression.builder(ExprItemWithModel.class, ItemType.class) + .addPattern("%itemtype% with [the] [item] model %string%") + .supplier(ExprItemWithModel::new) + .build() + ); + } + + private Expression key; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + setExpr((Expression) exprs[0]); + key = (Expression) exprs[1]; + return true; + } + + @Override + protected ItemType[] get(Event event, ItemType[] source) { + String key = this.key.getSingle(event); + if (key == null) + return source; + + NamespacedKey namespacedKey = NamespacedUtils.checkValidationAndSend(key, this); + if (namespacedKey == null) + return source; + + return get(source, itemType -> { + itemType = itemType.clone(); + ItemMeta itemMeta = itemType.getItemMeta(); + itemMeta.setItemModel(namespacedKey); + itemType.setItemMeta(itemMeta); + return itemType; + }); + } + + @Override + public Class getReturnType() { + return ItemType.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return getExpr().toString(event, debug) + " with item model " + key.toString(event, debug); + } + +} diff --git a/src/test/skript/tests/bukkit/item/ExprItemModel.sk b/src/test/skript/tests/bukkit/item/ExprItemModel.sk new file mode 100644 index 00000000000..5236e990e07 --- /dev/null +++ b/src/test/skript/tests/bukkit/item/ExprItemModel.sk @@ -0,0 +1,46 @@ +using error catching + +test "ExprItemModel": + set {_item} to diamond + + set item model of {_item} to "emerald" + assert item model of {_item} is "minecraft:emerald" with "Set item model of item" + + set {_inv} to chest inventory with 1 row named "test slots" + set slot 1 of {_inv} to diamond + + set item model of slot 1 of {_inv} to "emerald" + + assert item model of slot 1 of {_inv} is "minecraft:emerald" with "Set item model of slot from container" + + set {_item} to dirt with item model "diamond" + assert item model of {_item} is "minecraft:diamond" with "ItemType with item model" + + reset item model of {_item} + assert item model of {_item} is not set with "Reset item model of item" + + set item model of {_item} to "minecraft:redstone" + delete item model of {_item} + assert item model of {_item} is not set with "Delete item model of item" + + set {_string} to "test_string" + set item model of {_item} to {_string} + assert item model of {_item} is "minecraft:test_string" with "Set item model from variable string" + + set item model of {_item} to {_} + assert item model of {_item} is not set with "Set item model of item to null" + + set {_item} to diamond with model {_} + assert item model of {_item} is not set with "Item with null item model" + + set {_item} to emerald with model {_string} + assert item model of {_item} is "minecraft:test_string" with "Item with item model of variable string" + + set {_item} to diamond + catch runtime errors: + set item model of {_item} to "with spaces" + assert last caught runtime errors are set with "Runtime errors produced for invalid key for item model" + + catch runtime errors: + set {_item} to diamond with model "with spaces" + assert last caught runtime errors are set with "Runtime errors produced for invalid key for item with model" From 7d1db84ae5ebdaa8044aa122f267b7c35c8fbdf1 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Wed, 26 Aug 2026 17:32:49 -0400 Subject: [PATCH 05/11] Add X/Z support for Chunks (#8809) --- .../skript/classes/data/BukkitClasses.java | 74 ----------- .../skript/bukkit/BukkitModule.java | 1 + .../skript/bukkit/types/ChunkClassInfo.java | 118 ++++++++++++++++++ 3 files changed, 119 insertions(+), 74 deletions(-) create mode 100644 src/main/java/org/skriptlang/skript/bukkit/types/ChunkClassInfo.java 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 7089b80c64a..3cd3a529c17 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -347,80 +347,6 @@ public String toVariableNameString(InventoryHolder holder) { .since("2.0") .after("itemtype", "itemstack", "entitydata", "entitytype")); - Classes.registerClass(new ClassInfo<>(Chunk.class, "chunk") - .user("chunks?") - .name("Chunk") - .description("A chunk is a cuboid of 16×16×128 (x×z×y) blocks. Chunks are spread on a fixed rectangular grid in their world.") - .usage("") - .examples("") - .since("2.0") - .parser(new Parser<>() { - @Override - public boolean canParse(final ParseContext context) { - return false; - } - - @Override - public String toString(final Chunk c, final int flags) { - return "chunk (" + c.getX() + "," + c.getZ() + ") of " + c.getWorld().getName(); - } - - @Override - public String toVariableNameString(final Chunk c) { - return c.getWorld().getName() + ":" + c.getX() + "," + c.getZ(); - } - }) - .serializer(new Serializer<>() { - @Override - public Fields serialize(Chunk chunk) { - final Fields f = new Fields(); - f.putObject("world", chunk.getWorld()); - f.putPrimitive("x", chunk.getX()); - f.putPrimitive("z", chunk.getZ()); - return f; - } - - @Override - public boolean canBeInstantiated() { - return false; - } - - @Override - protected Chunk deserialize(Fields fields) throws StreamCorruptedException { - World world = fields.getObject("world", World.class); - if (world == null) - throw new StreamCorruptedException("Missing world"); - - int x = fields.getPrimitive("x", int.class); - int z = fields.getPrimitive("z", int.class); - return world.getChunkAt(x, z); - } - - // return c.getWorld().getName() + ":" + c.getX() + "," + c.getZ(); - @Override - @Nullable - public Chunk deserialize(final String s) { - final String[] split = s.split("[:,]"); - if (split.length != 3) - return null; - final World w = Bukkit.getWorld(split[0]); - if (w == null) - return null; - try { - final int x = Integer.parseInt(split[1]); - final int z = Integer.parseInt(split[1]); - return w.getChunkAt(x, z); - } catch (final NumberFormatException e) { - return null; - } - } - - @Override - public boolean mustSyncDeserialization() { - return true; - } - })); - Material[] allMaterials = Material.values(); Classes.registerClass(new ClassInfo<>(Material.class, "material") .name(ClassInfo.NO_DOC) diff --git a/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java index ffa8d4bab03..f7b9d49c80d 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java @@ -63,6 +63,7 @@ public Iterable children() { @Override protected void initSelf(SkriptAddon addon) { Classes.registerClass(new BlockClassInfo()); + Classes.registerClass(new ChunkClassInfo()); Classes.registerClass(new EntityClassInfo()); Classes.registerClass(new InventoryClassInfo()); Classes.registerClass(new ItemStackClassInfo()); diff --git a/src/main/java/org/skriptlang/skript/bukkit/types/ChunkClassInfo.java b/src/main/java/org/skriptlang/skript/bukkit/types/ChunkClassInfo.java new file mode 100644 index 00000000000..2aa1fd89997 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/types/ChunkClassInfo.java @@ -0,0 +1,118 @@ +package org.skriptlang.skript.bukkit.types; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.classes.Serializer; +import ch.njol.skript.lang.ParseContext; +import ch.njol.yggdrasil.Fields; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.handlers.WXYZHandler; +import org.skriptlang.skript.lang.properties.handlers.base.PropertyHandler; + +import java.io.StreamCorruptedException; + +@ApiStatus.Internal +public class ChunkClassInfo extends ClassInfo { + + public ChunkClassInfo() { + super(Chunk.class, "chunk"); + user("chunks?") + .name("Chunk") + .description(""" + A chunk is a 16 by 16 segment of a world, stretching from the world's bottom to its top. \ + Chunks are spread on a fixed rectangular grid in their world.\ + """) + .since("2.0") + .parser(new ChunkParser()) + .serializer(new ChunkSerializer()) + .property(Property.WXYZ, + "The X or Z coordinate of the chunk. Added in INSERT VERSION.", + Skript.instance(), + new ChunkWXYZHandler()); + } + + private static class ChunkParser extends Parser { + // + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public String toString(Chunk chunk, int flags) { + return "chunk (" + chunk.getX() + "," + chunk.getZ() + ") of " + chunk.getWorld().getName(); + } + + @Override + public String toVariableNameString(Chunk chunk) { + return chunk.getWorld().getName() + ":" + chunk.getX() + "," + chunk.getZ(); + } + // + } + + private static class ChunkSerializer extends Serializer { + // + @Override + public Fields serialize(Chunk chunk) { + Fields fields = new Fields(); + fields.putObject("world", chunk.getWorld()); + fields.putPrimitive("x", chunk.getX()); + fields.putPrimitive("z", chunk.getZ()); + return fields; + } + + @Override + public boolean canBeInstantiated() { + return false; + } + + @Override + protected Chunk deserialize(Fields fields) throws StreamCorruptedException { + World world = fields.getObject("world", World.class); + if (world == null) { + throw new StreamCorruptedException("Missing world"); + } + int x = fields.getPrimitive("x", int.class); + int z = fields.getPrimitive("z", int.class); + return world.getChunkAt(x, z); + } + + @Override + public boolean mustSyncDeserialization() { + return true; + } + // + } + + private static class ChunkWXYZHandler extends WXYZHandler { + // + @Override + public PropertyHandler newInstance() { + var instance = new ChunkClassInfo.ChunkWXYZHandler(); + instance.axis(axis); + return instance; + } + + @Override + public Integer convert(Chunk chunk) { + return axis == Axis.X ? chunk.getX() : chunk.getZ(); + } + + @Override + public boolean supportsAxis(Axis axis) { + return axis == Axis.X || axis == Axis.Z; + } + + @Override + public @NotNull Class returnType() { + return Integer.class; + } + // + } + +} From c482d752b33c99d971d6df92040595325dca87a3 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Wed, 26 Aug 2026 17:38:35 -0400 Subject: [PATCH 06/11] Add support for obtaining all registries through an addon instance (#8833) --- .../java/ch/njol/skript/ModernSkriptBridge.java | 6 ++++++ src/main/java/ch/njol/skript/SkriptAddon.java | 6 ++++++ .../java/org/skriptlang/skript/SkriptImpl.java | 16 ++++++++++++++++ .../skriptlang/skript/addon/SkriptAddon.java | 6 ++++++ .../skript/addon/SkriptAddonImpl.java | 17 +++++++++++++++-- .../skript/addon/BaseSkriptAddonTests.java | 8 ++++++++ 6 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/ModernSkriptBridge.java b/src/main/java/ch/njol/skript/ModernSkriptBridge.java index 194badaf0ac..09ecb7cf9f1 100644 --- a/src/main/java/ch/njol/skript/ModernSkriptBridge.java +++ b/src/main/java/ch/njol/skript/ModernSkriptBridge.java @@ -8,6 +8,7 @@ import org.skriptlang.skript.util.Registry; import java.util.Collection; +import java.util.List; import java.util.function.Supplier; /** @@ -75,6 +76,11 @@ public > R registry(Class registryClass, Supplier pu return unmodifiableSkript.registry(registryClass, putIfAbsent); } + @Override + public Collection> registries() { + return unmodifiableSkript.registries(); + } + @Override public SyntaxRegistry syntaxRegistry() { return unmodifiableSkript.syntaxRegistry(); diff --git a/src/main/java/ch/njol/skript/SkriptAddon.java b/src/main/java/ch/njol/skript/SkriptAddon.java index 82137f507a5..e8ba4310008 100644 --- a/src/main/java/ch/njol/skript/SkriptAddon.java +++ b/src/main/java/ch/njol/skript/SkriptAddon.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.util.Collection; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -154,6 +155,11 @@ public > R registry(Class registryClass, Supplier pu return addon.registry(registryClass, putIfAbsent); } + @Override + public Collection> registries() { + return addon.registries(); + } + @Override public SyntaxRegistry syntaxRegistry() { return addon.syntaxRegistry(); diff --git a/src/main/java/org/skriptlang/skript/SkriptImpl.java b/src/main/java/org/skriptlang/skript/SkriptImpl.java index 096312f9682..23d12ff575d 100644 --- a/src/main/java/org/skriptlang/skript/SkriptImpl.java +++ b/src/main/java/org/skriptlang/skript/SkriptImpl.java @@ -13,6 +13,7 @@ import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; @@ -65,6 +66,11 @@ public > R registry(Class registryClass, Supplier pu return (R) registries.computeIfAbsent(registryClass, key -> putIfAbsent.get()); } + @Override + public Collection> registries() { + return List.copyOf(registries.values()); + } + /* * SkriptAddon Management */ @@ -181,6 +187,11 @@ public > R registry(Class registryClass, Supplier pu return skript.registry(registryClass, putIfAbsent); } + @Override + public Collection> registries() { + return skript.registries(); + } + @Override public SyntaxRegistry syntaxRegistry() { return registry(SyntaxRegistry.class); @@ -256,6 +267,11 @@ public > R registry(Class registryClass, Supplier pu return unmodifiableAddon.registry(registryClass, putIfAbsent); } + @Override + public Collection> registries() { + return unmodifiableAddon.registries(); + } + @Override public SyntaxRegistry syntaxRegistry() { return unmodifiableAddon.syntaxRegistry(); diff --git a/src/main/java/org/skriptlang/skript/addon/SkriptAddon.java b/src/main/java/org/skriptlang/skript/addon/SkriptAddon.java index 0e1bab8dc81..be9adf53b4f 100644 --- a/src/main/java/org/skriptlang/skript/addon/SkriptAddon.java +++ b/src/main/java/org/skriptlang/skript/addon/SkriptAddon.java @@ -8,6 +8,7 @@ import org.skriptlang.skript.util.ViewProvider; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.function.Supplier; @@ -72,6 +73,11 @@ public interface SkriptAddon extends ViewProvider { */ > R registry(Class registryClass, Supplier putIfAbsent); + /** + * @return A collection of all registries accessible through this addon. + */ + Collection> registries(); + /** * @return A syntax registry for this addon's syntax. */ diff --git a/src/main/java/org/skriptlang/skript/addon/SkriptAddonImpl.java b/src/main/java/org/skriptlang/skript/addon/SkriptAddonImpl.java index 7f8faa06670..fa34ab9d0c7 100644 --- a/src/main/java/org/skriptlang/skript/addon/SkriptAddonImpl.java +++ b/src/main/java/org/skriptlang/skript/addon/SkriptAddonImpl.java @@ -5,6 +5,7 @@ import org.skriptlang.skript.util.Registry; import org.skriptlang.skript.util.ViewProvider; +import java.util.Collection; import java.util.function.Supplier; class SkriptAddonImpl { @@ -47,9 +48,9 @@ public boolean hasRegistry(Class> registryClass) { @Override public > R registry(Class registryClass) { R registry = addon.registry(registryClass); - if (registry instanceof ViewProvider) { + if (registry instanceof ViewProvider viewProvider) { //noinspection unchecked - registry = ((ViewProvider) registry).unmodifiableView(); + registry = (R) viewProvider.unmodifiableView(); } return registry; } @@ -59,6 +60,18 @@ public > R registry(Class registryClass, Supplier pu throw new UnsupportedOperationException("Cannot store registries on an unmodifiable addon"); } + @Override + public Collection> registries() { + return addon.registries().stream() + .map(registry -> { + if (registry instanceof ViewProvider viewProvider) { + return (Registry) viewProvider.unmodifiableView(); + } + return registry; + }) + .toList(); + } + @Override public SyntaxRegistry syntaxRegistry() { return addon.syntaxRegistry().unmodifiableView(); diff --git a/src/test/java/org/skriptlang/skript/addon/BaseSkriptAddonTests.java b/src/test/java/org/skriptlang/skript/addon/BaseSkriptAddonTests.java index c2d41ddb7b1..5c06436348b 100644 --- a/src/test/java/org/skriptlang/skript/addon/BaseSkriptAddonTests.java +++ b/src/test/java/org/skriptlang/skript/addon/BaseSkriptAddonTests.java @@ -80,6 +80,10 @@ public void testRegistry() { assertEquals(registry, unmodifiable.registry(MockRegistry.class)); assertTrue(addon.hasRegistry(MockRegistry.class)); assertTrue(unmodifiable.hasRegistry(MockRegistry.class)); + + // access all registries + assertTrue(addon.registries().contains(registry)); + assertTrue(unmodifiable.registries().contains(registry)); } @Test @@ -91,6 +95,10 @@ public void testSyntaxRegistry() { assertNotNull(unmodifiable.syntaxRegistry()); // unmodifiable's syntax registry should be unmodifiable (different) assertNotEquals(addon.syntaxRegistry(), unmodifiable.syntaxRegistry()); + assertNotEquals(addon.syntaxRegistry(), unmodifiable.registries().stream() + .filter(registry -> registry instanceof SyntaxRegistry) + .findFirst() + .orElseThrow()); } @Test From d17418d89d6d96713b45314a82f8129bbbf6b81e Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Wed, 26 Aug 2026 17:46:30 -0400 Subject: [PATCH 07/11] Improve Warning Suppression (#8832) --- .../skript/effects/EffSuppressTypeHints.java | 62 ------------ .../skript/effects/EffSuppressWarnings.java | 61 ------------ .../skript/common/CommonModule.java | 3 + .../effects/EffSecSuppressTypeHints.java | 77 +++++++++++++++ .../effects/EffSecSuppressWarnings.java | 94 +++++++++++++++++++ .../tests/common/EffSuppressWarnings.sk | 29 ++++++ src/test/skript/tests/misc/type hints.sk | 5 +- 7 files changed, 205 insertions(+), 126 deletions(-) delete mode 100644 src/main/java/ch/njol/skript/effects/EffSuppressTypeHints.java delete mode 100644 src/main/java/ch/njol/skript/effects/EffSuppressWarnings.java create mode 100644 src/main/java/org/skriptlang/skript/common/elements/effects/EffSecSuppressTypeHints.java create mode 100644 src/main/java/org/skriptlang/skript/common/elements/effects/EffSecSuppressWarnings.java create mode 100644 src/test/skript/tests/common/EffSuppressWarnings.sk diff --git a/src/main/java/ch/njol/skript/effects/EffSuppressTypeHints.java b/src/main/java/ch/njol/skript/effects/EffSuppressTypeHints.java deleted file mode 100644 index db7c550d283..00000000000 --- a/src/main/java/ch/njol/skript/effects/EffSuppressTypeHints.java +++ /dev/null @@ -1,62 +0,0 @@ -package ch.njol.skript.effects; - -import ch.njol.skript.Skript; -import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Example; -import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.Since; -import ch.njol.skript.lang.Effect; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.skript.registrations.Feature; -import ch.njol.util.Kleenean; -import org.bukkit.event.Event; -import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.lang.experiment.ExperimentData; -import org.skriptlang.skript.lang.experiment.SimpleExperimentalSyntax; - -@Name("Suppress Type Hints (Experimental)") -@Description({ - "An effect to suppress local variable type hint errors for the syntax lines that follow this effect.", - "NOTE: Suppressing type hints also prevents syntax from providing new type hints." + - " For example, with type hints suppressed, 'set {_x} to true' would not provide 'boolean' as a type hint for '{_x}'" -}) -@Example(""" - start suppressing local variable type hints - # potentially unsafe code goes here - stop suppressing local variable type hints -""") -@Since("2.12") -public class EffSuppressTypeHints extends Effect implements SimpleExperimentalSyntax { - - private static final ExperimentData EXPERIMENT_DATA = ExperimentData.createSingularData(Feature.TYPE_HINTS); - - static { - Skript.registerEffect(EffSuppressTypeHints.class, - "[stop:un]suppress [local variable] type hints", - "(start|:stop) suppressing [local variable] type hints"); - } - - private boolean stop; - - @Override - public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - stop = parseResult.hasTag("stop"); - getParser().getHintManager().setActive(stop); - return true; - } - - @Override - protected void execute(Event event) { } - - @Override - public String toString(@Nullable Event event, boolean debug) { - return (stop ? "stop" : "start") + " suppressing type hints"; - } - - @Override - public ExperimentData getExperimentData() { - return EXPERIMENT_DATA; - } - -} diff --git a/src/main/java/ch/njol/skript/effects/EffSuppressWarnings.java b/src/main/java/ch/njol/skript/effects/EffSuppressWarnings.java deleted file mode 100644 index 9a3f9897471..00000000000 --- a/src/main/java/ch/njol/skript/effects/EffSuppressWarnings.java +++ /dev/null @@ -1,61 +0,0 @@ -package ch.njol.skript.effects; - -import ch.njol.skript.Skript; -import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Example; -import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.Since; -import ch.njol.skript.lang.Effect; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser; -import ch.njol.util.Kleenean; -import org.bukkit.event.Event; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; -import org.skriptlang.skript.lang.script.ScriptWarning; - -@Name("Locally Suppress Warning") -@Description("Suppresses target warnings from the current script.") -@Example("locally suppress missing conjunction warnings") -@Example("suppress the variable save warnings") -@Since("2.3") -public class EffSuppressWarnings extends Effect { - - static { - StringBuilder warnings = new StringBuilder(); - ScriptWarning[] values = ScriptWarning.values(); - for (int i = 0; i < values.length; i++) { - if (i != 0) - warnings.append('|'); - warnings.append(values[i].ordinal()).append(':').append(values[i].getPattern()); - } - Skript.registerEffect(EffSuppressWarnings.class, "[local[ly]] suppress [the] (" + warnings + ") warning[s]"); - } - - private @UnknownNullability ScriptWarning warning; - - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - if (!getParser().isActive()) { - Skript.error("You can't suppress warnings outside of a script!"); - return false; - } - - warning = ScriptWarning.values()[parseResult.mark]; - if (warning.isDeprecated()) { - Skript.warning(warning.getDeprecationMessage()); - } else { - getParser().getCurrentScript().suppressWarning(warning); - } - return true; - } - - @Override - protected void execute(Event event) { } - - @Override - public String toString(@Nullable Event event, boolean debug) { - return "suppress " + warning.getWarningName() + " warnings"; - } - -} diff --git a/src/main/java/org/skriptlang/skript/common/CommonModule.java b/src/main/java/org/skriptlang/skript/common/CommonModule.java index 762ec04c158..c94012adb6e 100644 --- a/src/main/java/org/skriptlang/skript/common/CommonModule.java +++ b/src/main/java/org/skriptlang/skript/common/CommonModule.java @@ -4,6 +4,7 @@ import org.skriptlang.skript.addon.AddonModule; import org.skriptlang.skript.addon.HierarchicalAddonModule; import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.common.elements.effects.*; import org.skriptlang.skript.common.elements.expressions.*; import org.skriptlang.skript.common.properties.PropertiesModule; import org.skriptlang.skript.common.types.*; @@ -30,6 +31,8 @@ protected void initSelf(SkriptAddon addon) { @Override protected void loadSelf(SkriptAddon addon) { register(addon, + EffSecSuppressTypeHints::register, + EffSecSuppressWarnings::register, ExprColorFromHexCode::register, ExprHexCode::register, ExprRecursiveSize::register, diff --git a/src/main/java/org/skriptlang/skript/common/elements/effects/EffSecSuppressTypeHints.java b/src/main/java/org/skriptlang/skript/common/elements/effects/EffSecSuppressTypeHints.java new file mode 100644 index 00000000000..c4e6055a9a5 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/common/elements/effects/EffSecSuppressTypeHints.java @@ -0,0 +1,77 @@ +package org.skriptlang.skript.common.elements.effects; + +import ch.njol.skript.config.SectionNode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.EffectSection; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.registrations.Feature; +import ch.njol.skript.variables.HintManager; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.experiment.ExperimentData; +import org.skriptlang.skript.lang.experiment.SimpleExperimentalSyntax; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.List; + +@Name("Suppress Type Hints (Experimental)") +@Description(""" + An effect to suppress local variable type hint errors for the syntax lines that follow this effect. + NOTE: Suppressing type hints also prevents syntax from providing new type hints. \ + For example, with type hints suppressed, 'set {_x} to true' would not provide 'boolean' as a type hint for '{_x}' + """) +@Example(""" + start suppressing local variable type hints + # potentially unsafe code goes here + stop suppressing local variable type hints + """) +@Since({"2.12", "INSERT VERSION (suppressing in a section)"}) +public class EffSecSuppressTypeHints extends EffectSection implements SimpleExperimentalSyntax { + + private static final ExperimentData EXPERIMENT_DATA = ExperimentData.createSingularData(Feature.TYPE_HINTS); + + public static void register(SyntaxRegistry syntaxRegistry) { + syntaxRegistry.register(SyntaxRegistry.SECTION, SyntaxInfo.simple(EffSecSuppressTypeHints.class, EffSecSuppressTypeHints::new, + "[stop:un]suppress [local variable] type hints", + "(start|:stop) suppressing [local variable] type hints")); + } + + private boolean stopSuppression; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult, + @Nullable SectionNode sectionNode, @Nullable List triggerItems) { + stopSuppression = parseResult.hasTag("stop"); + HintManager hintManager = getParser().getHintManager(); + boolean wasActive = hintManager.isActive(); + hintManager.setActive(stopSuppression); + if (sectionNode != null) { + loadCode(sectionNode); + hintManager.setActive(wasActive); + } + return true; + } + + @Override + protected @Nullable TriggerItem walk(Event event) { + return walk(event, true); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return (stopSuppression ? "stop" : "start") + " suppressing type hints"; + } + + @Override + public ExperimentData getExperimentData() { + return EXPERIMENT_DATA; + } + +} diff --git a/src/main/java/org/skriptlang/skript/common/elements/effects/EffSecSuppressWarnings.java b/src/main/java/org/skriptlang/skript/common/elements/effects/EffSecSuppressWarnings.java new file mode 100644 index 00000000000..cf30eaadb85 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/common/elements/effects/EffSecSuppressWarnings.java @@ -0,0 +1,94 @@ +package org.skriptlang.skript.common.elements.effects; + +import ch.njol.skript.Skript; +import ch.njol.skript.config.SectionNode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.EffectSection; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.parser.ParserInstance; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.script.Script; +import org.skriptlang.skript.lang.script.ScriptWarning; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.List; + +@Name("Locally Suppress Warning") +@Description("Suppresses target warnings from the current script.") +@Example("locally suppress missing conjunction warnings") +@Example("suppress the variable save warnings") +@Since({"2.3", "INSERT VERSION (suppressing in a section)"}) +public class EffSecSuppressWarnings extends EffectSection { + + public static void register(SyntaxRegistry syntaxRegistry) { + StringBuilder warnings = new StringBuilder(); + ScriptWarning[] values = ScriptWarning.values(); + for (int i = 0; i < values.length; i++) { + if (i != 0) { + warnings.append('|'); + } + warnings.append(values[i].ordinal()) + .append(':') + .append(values[i].getPattern()); + } + syntaxRegistry.register(SyntaxRegistry.SECTION, SyntaxInfo.simple(EffSecSuppressWarnings.class, EffSecSuppressWarnings::new, + "[local[ly]] [stop:un]suppress [the] (" + warnings + ") warning[s]", + "[start|:stop] [local[ly]] suppressing [the] (" + warnings + ") warning[s]")); + } + + private ScriptWarning warning; + private boolean stop; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult, + @Nullable SectionNode sectionNode, @Nullable List triggerItems) { + ParserInstance parser = getParser(); + if (!parser.isActive()) { + Skript.error("You can't suppress warnings outside of a script!"); + return false; + } + + warning = ScriptWarning.values()[parseResult.mark]; + stop = parseResult.hasTag("stop"); + if (warning.isDeprecated()) { + Skript.warning(warning.getDeprecationMessage()); + } + + Script script = parser.getCurrentScript(); + boolean wasSuppressed = script.suppressesWarning(warning); + if (stop) { + script.allowWarning(warning); + } else { + script.suppressWarning(warning); + } + if (sectionNode != null) { + loadCode(sectionNode); + if (wasSuppressed) { + script.suppressWarning(warning); + } else { + script.allowWarning(warning); + } + } + + return true; + } + + @Override + protected @Nullable TriggerItem walk(Event event) { + return walk(event, true); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return (stop ? "un" : "") + "suppress " + warning.getWarningName() + " warnings"; + } + +} diff --git a/src/test/skript/tests/common/EffSuppressWarnings.sk b/src/test/skript/tests/common/EffSuppressWarnings.sk new file mode 100644 index 00000000000..c1a4f21c915 --- /dev/null +++ b/src/test/skript/tests/common/EffSuppressWarnings.sk @@ -0,0 +1,29 @@ +test "EffSuppressWarnings": + + parse: + stop + stop + assert last parse logs is "Unreachable code. The previous statement stops further execution." + + suppress unreachable code warnings + parse: + stop + stop + assert last parse logs is not set + unsuppress unreachable code warnings + + parse: + stop + stop + assert last parse logs is "Unreachable code. The previous statement stops further execution." + + suppress unreachable code warnings: + parse: + stop + stop + assert last parse logs is not set + + parse: + stop + stop + assert last parse logs is "Unreachable code. The previous statement stops further execution." diff --git a/src/test/skript/tests/misc/type hints.sk b/src/test/skript/tests/misc/type hints.sk index 09b3f7df10d..a578e628b88 100644 --- a/src/test/skript/tests/misc/type hints.sk +++ b/src/test/skript/tests/misc/type hints.sk @@ -17,9 +17,8 @@ test "type hints": set {_a} to {_x} in lowercase unsuppress local variable type hints set {_x} to 10 - suppress local variable type hints - set {_a} to {_x} in lowercase - unsuppress local variable type hints + suppress local variable type hints: + set {_a} to {_x} in lowercase assert last parse logs is not set with "last parse logs is set (%last parse logs%)" delete {_x} From 18be5bfca5e5bca7151c732569ac28a63c304775 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Wed, 26 Aug 2026 17:56:52 -0400 Subject: [PATCH 08/11] Use correct Skript class in some registry constructors (#8831) --- src/main/java/ch/njol/skript/Skript.java | 4 ++-- .../lang/eventvalue/EventValueRegistry.java | 10 +++++++++- .../eventvalue/EventValueRegistryImpl.java | 4 ++-- .../lang/properties/PropertyRegistry.java | 18 +++++++++++++----- .../eventvalue/EventValueRegistryTest.java | 3 ++- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 216bc6687d8..03749dc4f5a 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -472,10 +472,10 @@ public void onEnable() { experimentRegistry = new ExperimentRegistry(this); Feature.registerAll(getAddonInstance(), experimentRegistry); - skript.storeRegistry(PropertyRegistry.class, new PropertyRegistry(this)); + skript.storeRegistry(PropertyRegistry.class, new PropertyRegistry(skript)); Property.registerDefaultProperties(); - EventValueRegistry eventValueRegistry = EventValueRegistry.empty(this); + EventValueRegistry eventValueRegistry = EventValueRegistry.empty(skript); skript.storeRegistry(EventValueRegistry.class, eventValueRegistry); //noinspection removal EventValues.setEventValueRegistry(eventValueRegistry); diff --git a/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistry.java b/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistry.java index 0af4822de4d..cf0507faefd 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistry.java +++ b/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistry.java @@ -1,9 +1,9 @@ package org.skriptlang.skript.bukkit.lang.eventvalue; -import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; import org.bukkit.event.Event; import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.Skript; import org.skriptlang.skript.util.Registry; import org.skriptlang.skript.util.ViewProvider; @@ -32,6 +32,14 @@ static EventValueRegistry empty(Skript skript) { return new EventValueRegistryImpl(skript); } + /** + * @deprecated Use {@link #empty(Skript)}. + */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) + static EventValueRegistry empty(ch.njol.skript.Skript ignored) { + return empty(ch.njol.skript.Skript.instance()); + } + /** * Registers a new {@link EventValue}. * diff --git a/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryImpl.java b/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryImpl.java index 8e641044cad..fa03c76b964 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryImpl.java +++ b/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryImpl.java @@ -1,10 +1,10 @@ package org.skriptlang.skript.bukkit.lang.eventvalue; -import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; import com.google.common.base.Preconditions; import org.bukkit.event.Event; import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.Skript; import org.skriptlang.skript.lang.converter.Converter; import org.skriptlang.skript.lang.converter.Converters; import org.skriptlang.skript.util.ClassUtils; @@ -32,7 +32,7 @@ public void register(EventValue eventValue) { if (eventValue instanceof ConvertedEventValue) throw new SkriptAPIException("Cannot register a converted event value: " + eventValue); if (isRegistered(eventValue)) { - Skript.warning(eventValue + " is already registered."); + ch.njol.skript.Skript.warning(eventValue + " is already registered."); return; } List> eventValues = eventValues(eventValue.time()); 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 4239ce0fbbe..7308946e835 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyRegistry.java @@ -1,9 +1,9 @@ package org.skriptlang.skript.lang.properties; -import ch.njol.skript.Skript; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.Skript; import org.skriptlang.skript.util.Registry; import java.util.*; @@ -25,14 +25,22 @@ public PropertyRegistry(Skript skript) { this.properties = new HashMap<>(); } + /** + * @deprecated Use {@link #PropertyRegistry(Skript)}. + */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public PropertyRegistry(ch.njol.skript.Skript ignored) { + this(ch.njol.skript.Skript.instance()); + } + 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() + "."); + ch.njol.skript.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() + "."); + ch.njol.skript.Skript.debug("Registered property '" + name + "' provided by " + property.provider().name() + "."); return true; } @@ -44,11 +52,11 @@ public boolean unregister(@NotNull Property property) { 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."); + ch.njol.skript.Skript.error("Property '" + name + "' is not registered and cannot be unregistered."); return false; // Property not registered } properties.remove(name); - Skript.debug("Unregistered property '" + name + "'."); + ch.njol.skript.Skript.debug("Unregistered property '" + name + "'."); return true; } diff --git a/src/test/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryTest.java b/src/test/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryTest.java index 30c80b2eb70..03cf0643d7f 100644 --- a/src/test/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryTest.java +++ b/src/test/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryTest.java @@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.Test; +import org.skriptlang.skript.Skript; import org.skriptlang.skript.lang.converter.Converters; import static org.junit.Assert.*; @@ -15,7 +16,7 @@ public class EventValueRegistryTest { @Before public void setUp() { - registry = EventValueRegistry.empty(null); + registry = EventValueRegistry.empty(Skript.of(EventValueRegistryTest.class, "EventValueRegistryTest")); } private static class TestEvent extends Event { From 616b9c1ce0b19a52640a0a8d84cec05e6111abf7 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Wed, 26 Aug 2026 18:02:48 -0400 Subject: [PATCH 09/11] Proper Experiment Registry (#8830) --- src/main/java/ch/njol/skript/Skript.java | 14 ++--- .../ch/njol/skript/registrations/Feature.java | 5 +- .../lang/experiment/ExperimentRegistry.java | 57 +++++++++++++++---- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 03749dc4f5a..472876b3edc 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -203,8 +203,6 @@ public static void updateMinecraftVersion() { @Nullable private static Version version = null; - @Deprecated(since = "2.9.0", forRemoval = true) // TODO this field will be replaced by a proper registry later - private static @UnknownNullability ExperimentRegistry experimentRegistry; public static Version getVersion() { final Version v = version; @@ -339,9 +337,13 @@ public static void disableHookRegistration(Class>... hooks) { /** * @return The manager for experimental, optional features. + * @deprecated {@link ExperimentRegistry} is now a regular registry, and should be accessed as such. + * See {@link org.skriptlang.skript.addon.SkriptAddon#registry(Class)}. */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) public static ExperimentRegistry experiments() { - return experimentRegistry; + // intentionally returning the modifiable view + return skript.registry(ExperimentRegistry.class); } /** @@ -469,8 +471,8 @@ public void onEnable() { // initialize the old Skript SkriptAddon instance getAddonInstance(); - experimentRegistry = new ExperimentRegistry(this); - Feature.registerAll(getAddonInstance(), experimentRegistry); + skript.storeRegistry(ExperimentRegistry.class, new ExperimentRegistry(skript)); + Feature.registerAll(skript, skript.registry(ExperimentRegistry.class)); skript.storeRegistry(PropertyRegistry.class, new PropertyRegistry(skript)); Property.registerDefaultProperties(); @@ -1286,8 +1288,6 @@ public void onDisable() { Skript.exception(e, "An error occurred while shutting down.", "This might or might not cause any issues."); } } - - this.experimentRegistry = null; } // ================ CONSTANTS, OPTIONS & OTHER ================ diff --git a/src/main/java/ch/njol/skript/registrations/Feature.java b/src/main/java/ch/njol/skript/registrations/Feature.java index e1e57512645..6d175deef81 100644 --- a/src/main/java/ch/njol/skript/registrations/Feature.java +++ b/src/main/java/ch/njol/skript/registrations/Feature.java @@ -1,17 +1,17 @@ package ch.njol.skript.registrations; -import ch.njol.skript.SkriptAddon; import ch.njol.skript.doc.Documentable; import ch.njol.skript.patterns.PatternCompiler; import ch.njol.skript.patterns.SkriptPattern; import com.google.common.base.Preconditions; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.addon.SkriptAddon; import org.skriptlang.skript.lang.experiment.Experiment; import org.skriptlang.skript.lang.experiment.ExperimentRegistry; import org.skriptlang.skript.lang.experiment.LifeCycle; -import java.util.Collection; import java.util.List; /** @@ -209,6 +209,7 @@ set the damage location to location(0, 0, 10) }; } + @ApiStatus.Internal public static void registerAll(SkriptAddon addon, ExperimentRegistry manager) { for (Feature value : values()) { manager.register(addon, value); diff --git a/src/main/java/org/skriptlang/skript/lang/experiment/ExperimentRegistry.java b/src/main/java/org/skriptlang/skript/lang/experiment/ExperimentRegistry.java index ec6a5ae5157..3d0b5ee33dd 100644 --- a/src/main/java/org/skriptlang/skript/lang/experiment/ExperimentRegistry.java +++ b/src/main/java/org/skriptlang/skript/lang/experiment/ExperimentRegistry.java @@ -1,10 +1,13 @@ package org.skriptlang.skript.lang.experiment; -import ch.njol.skript.Skript; -import ch.njol.skript.SkriptAddon; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.Skript; +import org.skriptlang.skript.addon.SkriptAddon; import org.skriptlang.skript.lang.script.Script; +import org.skriptlang.skript.util.Registry; +import org.skriptlang.skript.util.ViewProvider; import java.util.LinkedHashSet; import java.util.Set; @@ -12,21 +15,41 @@ /** * A manager for registering (and identifying) experimental feature flags. */ -/* -* TODO -* This is designed to be (replaced by|refactored into) a proper registry when the registries rework PR -* is completed. The overall skeleton is designed to remain, so that there should be no breaking changes -* for anything using it. I.e. you will still be able to use Skript#experiments() and obtain 'this' class -* although these will just become helper methods for the proper registry behaviour. -* */ -public class ExperimentRegistry implements Experimented { +public class ExperimentRegistry implements Registry, ViewProvider, Experimented { private final Skript skript; private final Set experiments; + private final @Nullable ExperimentRegistry source; public ExperimentRegistry(Skript skript) { this.skript = skript; this.experiments = new LinkedHashSet<>(); + this.source = null; + } + + /** + * Internal constructor for creating an unmodifiable view of an experiment registry. + */ + private ExperimentRegistry(ExperimentRegistry source) { + this.skript = source.skript; + this.experiments = source.experiments; + this.source = source; + } + + /** + * @deprecated Use {@link #ExperimentRegistry(Skript)}. + */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public ExperimentRegistry(ch.njol.skript.Skript ignored) { + this(ch.njol.skript.Skript.instance()); + } + + /** + * @return An unmodifiable view of this experiment registry. + */ + @Override + public ExperimentRegistry unmodifiableView() { + return new ExperimentRegistry(this); } /** @@ -52,6 +75,14 @@ public Experiment[] registered() { return experiments.toArray(new Experiment[0]); } + /** + * @return An unmodifiable set containing all currently-registered experiments. + */ + @Override + public @Unmodifiable Set elements() { + return Set.copyOf(experiments); + } + /** * Registers a new experimental feature flag, which will be available to scripts * with the {@code using %name%} structure. @@ -60,6 +91,9 @@ public Experiment[] registered() { * @param experiment The experimental feature flag. */ public void register(SkriptAddon addon, Experiment experiment) { + if (source != null) { + throw new UnsupportedOperationException("Cannot register experiments using an unmodifiable registry."); + } // the addon instance is requested for now in case we need it in future (for error triage) this.experiments.add(experiment); } @@ -81,6 +115,9 @@ public void registerAll(SkriptAddon addon, Experiment... experiments) { * @param experiment The experimental feature flag. */ public void unregister(SkriptAddon addon, Experiment experiment) { + if (source != null) { + throw new UnsupportedOperationException("Cannot unregister experiments using an unmodifiable registry."); + } // the addon instance is requested for now in case we need it in future (for error triage) this.experiments.remove(experiment); } From c50dc159f2463d5a2b9ca69bdb557956b0f86fd8 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Fri, 28 Aug 2026 12:39:55 -0400 Subject: [PATCH 10/11] Sign Side Support (#8811) --- .../classes/data/BukkitEventValues.java | 3 - .../skript/conditions/CondGlowingText.java | 53 ------------ .../njol/skript/effects/EffGlowingText.java | 80 ------------------ .../ch/njol/skript/events/SimpleEvents.java | 6 -- .../skript/bukkit/block/sign/SignModule.java | 36 ++++++++ .../elements/conditions/CondChangedSide.java | 66 +++++++++++++++ .../elements/conditions/CondGlowingText.java | 78 +++++++++++++++++ .../sign/elements/effects/EffGlowingText.java | 77 +++++++++++++++++ .../elements/expressions/ExprSignText.java | 83 +++++++++++++++---- .../tests/bukkit/block/sign/EffGlowingText.sk | 28 +++++++ .../tests/bukkit/block/sign/ExprSignText.sk | 3 + .../tests/syntaxes/effects/EffGlowingText.sk | 18 ---- 12 files changed, 355 insertions(+), 176 deletions(-) delete mode 100644 src/main/java/ch/njol/skript/conditions/CondGlowingText.java delete mode 100644 src/main/java/ch/njol/skript/effects/EffGlowingText.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/conditions/CondChangedSide.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/conditions/CondGlowingText.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/effects/EffGlowingText.java create mode 100644 src/test/skript/tests/bukkit/block/sign/EffGlowingText.sk delete mode 100644 src/test/skript/tests/syntaxes/effects/EffGlowingText.sk diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index 8f615650611..c37b6c89de1 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -189,9 +189,6 @@ public static void register(EventValueRegistry registry) { if (Skript.methodExists(BlockCanBuildEvent.class, "getPlayer")) { registry.register(EventValue.simple(BlockCanBuildEvent.class, Player.class, BlockCanBuildEvent::getPlayer)); } - // SignChangeEvent - registry.register(EventValue.simple(SignChangeEvent.class, Player.class, SignChangeEvent::getPlayer)); - registry.register(EventValue.simple(SignChangeEvent.class, Component[].class, event -> event.lines().toArray(new Component[0]))); // === EntityEvents === registry.register(EventValue.builder(EntityEvent.class, Entity.class) diff --git a/src/main/java/ch/njol/skript/conditions/CondGlowingText.java b/src/main/java/ch/njol/skript/conditions/CondGlowingText.java deleted file mode 100644 index 693c84120a9..00000000000 --- a/src/main/java/ch/njol/skript/conditions/CondGlowingText.java +++ /dev/null @@ -1,53 +0,0 @@ -package ch.njol.skript.conditions; - -import ch.njol.skript.Skript; -import ch.njol.skript.aliases.ItemType; -import org.bukkit.block.Block; - -import ch.njol.skript.conditions.base.PropertyCondition; -import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Example; -import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.Since; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.inventory.meta.BlockStateMeta; -import org.bukkit.inventory.meta.ItemMeta; - -@Name("Has Glowing Text") -@Description("Checks whether a sign (either a block or an item) has glowing text") -@Example("if target block has glowing text") -@Since("2.8.0") -public class CondGlowingText extends PropertyCondition { - - static { - if (Skript.methodExists(Sign.class, "isGlowingText")) - register(CondGlowingText.class, PropertyType.HAVE, "glowing text", "blocks/itemtypes"); - } - - @Override - public boolean check(Object obj) { - if (obj instanceof Block block) { - BlockState state = block.getState(); - return state instanceof Sign sign && sign.isGlowingText(); - } else if (obj instanceof ItemType itemType) { - ItemMeta meta = itemType.getItemMeta(); - if (meta instanceof BlockStateMeta blockStateMeta) { - BlockState state = blockStateMeta.getBlockState(); - return state instanceof Sign sign && sign.isGlowingText(); - } - } - return false; - } - - @Override - protected PropertyType getPropertyType() { - return PropertyType.HAVE; - } - - @Override - protected String getPropertyName() { - return "glowing text"; - } - -} diff --git a/src/main/java/ch/njol/skript/effects/EffGlowingText.java b/src/main/java/ch/njol/skript/effects/EffGlowingText.java deleted file mode 100644 index 3f54bd94fc0..00000000000 --- a/src/main/java/ch/njol/skript/effects/EffGlowingText.java +++ /dev/null @@ -1,80 +0,0 @@ -package ch.njol.skript.effects; - -import ch.njol.skript.Skript; -import ch.njol.skript.aliases.ItemType; -import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Example; -import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.Since; -import ch.njol.skript.lang.Effect; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.util.Kleenean; - -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.event.Event; -import org.bukkit.inventory.meta.BlockStateMeta; -import org.bukkit.inventory.meta.ItemMeta; -import org.jetbrains.annotations.Nullable; - -@Name("Make Sign Glow") -@Description("Makes a sign (either a block or item) have glowing text or normal text") -@Example("make target block of player have glowing text") -@Since("2.8.0") -public class EffGlowingText extends Effect { - - static { - if (Skript.methodExists(Sign.class, "setGlowingText", boolean.class)) { - Skript.registerEffect(EffGlowingText.class, - "make %blocks/itemtypes% have glowing text", - "make %blocks/itemtypes% have (normal|non[-| ]glowing) text" - ); - } - } - - @SuppressWarnings("NotNullFieldNotInitialized") - private Expression objects; - - private boolean glowing; - - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - objects = exprs[0]; - glowing = matchedPattern == 0; - return true; - } - - @Override - protected void execute(Event event) { - for (Object obj : objects.getArray(event)) { - if (obj instanceof Block) { - BlockState state = ((Block) obj).getState(); - if (state instanceof Sign) { - ((Sign) state).setGlowingText(glowing); - state.update(); - } - } else if (obj instanceof ItemType) { - ItemType item = (ItemType) obj; - ItemMeta meta = item.getItemMeta(); - if (!(meta instanceof BlockStateMeta)) - return; - BlockStateMeta blockMeta = (BlockStateMeta) meta; - BlockState state = blockMeta.getBlockState(); - if (!(state instanceof Sign)) - return; - ((Sign) state).setGlowingText(glowing); - state.update(); - blockMeta.setBlockState(state); - item.setItemMeta(meta); - } - } - } - - @Override - public String toString(@Nullable Event event, boolean debug) { - return "make " + objects.toString(event, debug) + " have " + (glowing ? "glowing text" : "normal text"); - } - -} diff --git a/src/main/java/ch/njol/skript/events/SimpleEvents.java b/src/main/java/ch/njol/skript/events/SimpleEvents.java index c06ba440459..8b9175ed4aa 100644 --- a/src/main/java/ch/njol/skript/events/SimpleEvents.java +++ b/src/main/java/ch/njol/skript/events/SimpleEvents.java @@ -245,12 +245,6 @@ public class SimpleEvents { "\tif projectile is an arrow:", "\t\tsend \"you shot an arrow!\" to shooter") .since("1.0"); - Skript.registerEvent("Sign Change", SimpleEvent.class, SignChangeEvent.class, "sign (chang[e]|edit)[ing]", "[player] (chang[e]|edit)[ing] [a] sign") - .description("As signs are placed empty, this event is called when a player is done editing a sign.") - .examples("on sign change:", - " line 2 is empty", - " set line 1 to \"<red>%line 1%\"") - .since("1.0"); Skript.registerEvent("Spawn Change", SimpleEvent.class, SpawnChangeEvent.class, "[world] spawn change") .description("Called when the spawn point of a world changes.") .examples("on spawn change:", diff --git a/src/main/java/org/skriptlang/skript/bukkit/block/sign/SignModule.java b/src/main/java/org/skriptlang/skript/bukkit/block/sign/SignModule.java index 59df5d1efb5..6d8a77aebb9 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/block/sign/SignModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/block/sign/SignModule.java @@ -1,9 +1,21 @@ package org.skriptlang.skript.bukkit.block.sign; +import ch.njol.skript.lang.util.SimpleEvent; +import net.kyori.adventure.text.Component; +import org.bukkit.entity.Player; +import org.bukkit.event.block.SignChangeEvent; import org.skriptlang.skript.addon.AddonModule; import org.skriptlang.skript.addon.HierarchicalAddonModule; import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.bukkit.block.sign.elements.conditions.CondChangedSide; +import org.skriptlang.skript.bukkit.block.sign.elements.conditions.CondGlowingText; +import org.skriptlang.skript.bukkit.block.sign.elements.effects.EffGlowingText; import org.skriptlang.skript.bukkit.block.sign.elements.expressions.ExprSignText; +import org.skriptlang.skript.bukkit.lang.eventvalue.EventValue; +import org.skriptlang.skript.bukkit.lang.eventvalue.EventValueRegistry; +import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos; +import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos.Event; +import org.skriptlang.skript.registration.SyntaxRegistry; public class SignModule extends HierarchicalAddonModule { @@ -14,8 +26,32 @@ public SignModule(AddonModule parentModule) { @Override protected void loadSelf(SkriptAddon addon) { register(addon, + CondChangedSide::register, + CondGlowingText::register, + EffGlowingText::register, ExprSignText::register ); + + SyntaxRegistry syntaxRegistry = moduleRegistry(addon); + EventValueRegistry eventValueRegistry = addon.registry(EventValueRegistry.class); + + // SignChangeEvent + syntaxRegistry.register(BukkitSyntaxInfos.Event.KEY, Event.builder(SimpleEvent.class, "Sign Change") + .supplier(() -> new SimpleEvent("sign change")) + .addEvent(SignChangeEvent.class) + .addPatterns("sign (chang[e]|edit)[ing]", + "[player] (chang[e]|edit)[ing] [a] sign") + .addDescription("As signs are placed empty, this event is called when a player is done editing a sign.") + .addExample(""" + on sign change: + line 2 is empty + set line 1 to "%line 1%" + """) + .addSince("1.0") + .build()); + eventValueRegistry.register(EventValue.simple(SignChangeEvent.class, Player.class, SignChangeEvent::getPlayer)); + eventValueRegistry.register(EventValue.simple(SignChangeEvent.class, Component[].class, + event -> event.lines().toArray(new Component[0]))); } @Override diff --git a/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/conditions/CondChangedSide.java b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/conditions/CondChangedSide.java new file mode 100644 index 00000000000..62896b082cd --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/conditions/CondChangedSide.java @@ -0,0 +1,66 @@ +package org.skriptlang.skript.bukkit.block.sign.elements.conditions; + +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Events; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.block.sign.Side; +import org.bukkit.event.Event; +import org.bukkit.event.block.SignChangeEvent; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Changed Sign Side") +@Description("Checks whether a side was changed in a sign change event.") +@Example(""" + on sign change: + the back side was changed + cancel the event + send "It is not possible to change the back side of a sign!" to the player + """) +@Since("INSERT VERSION") +@Events("sign change") +public class CondChangedSide extends Condition implements EventRestrictedSyntax { + + public static void register(SyntaxRegistry syntaxRegistry) { + syntaxRegistry.register(SyntaxRegistry.CONDITION, SyntaxInfo.simple(CondChangedSide.class, CondChangedSide::new, + "[the] (front|:back) [side] (is|was) changed", + "[the] (front|:back) [side] (is not|isn't|was not|wasn't) changed", + "[the] changed side (is|was) [the] (front|:back)", + "[the] changed side (is not|isn't|was not|wasn't) [the] (front|:back)")); + } + + private Side expectedSide; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + expectedSide = parseResult.hasTag("back") ? Side.BACK : Side.FRONT; + setNegated(matchedPattern % 2 == 1); + return true; + } + + @Override + public Class[] supportedEvents() { + //noinspection unchecked + return new Class[]{SignChangeEvent.class}; + } + + @Override + public boolean check(Event event) { + return event instanceof SignChangeEvent signChangeEvent && + (signChangeEvent.getSide() == expectedSide) != isNegated(); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "the " + (expectedSide == Side.FRONT ? "front" : "back") + " side was " + (isNegated() ? "not " : "") + "changed"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/conditions/CondGlowingText.java b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/conditions/CondGlowingText.java new file mode 100644 index 00000000000..116935613ab --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/conditions/CondGlowingText.java @@ -0,0 +1,78 @@ +package org.skriptlang.skript.bukkit.block.sign.elements.conditions; + +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.block.Block; + +import ch.njol.skript.conditions.base.PropertyCondition; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; +import org.bukkit.event.Event; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.Arrays; + +@Name("Has Glowing Text") +@Description(""" + Checks whether a sign (either a block or an item) has glowing text. \ + If a sign side is not specified, the front side will be used by default. + """) +@Example("if target block has glowing text") +@Since({"2.8.0", "INSERT VERSION (sign side support)"}) +public class CondGlowingText extends PropertyCondition { + + public static void register(SyntaxRegistry syntaxRegistry) { + syntaxRegistry.register(SyntaxRegistry.CONDITION, SyntaxInfo.simple(CondGlowingText.class, CondGlowingText::new, + Arrays.stream(getPatterns(PropertyType.HAVE, "glowing text", "blocks/itemtypes")) + .map(pattern -> "[[the] (front|:back) [side[s]] of]" + pattern) + .toArray(String[]::new))); + } + + private Side side; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + side = parseResult.hasTag("back") ? Side.BACK : Side.FRONT; + return super.init(expressions, matchedPattern, isDelayed, parseResult); + } + + @Override + public boolean check(Object obj) { + BlockState blockState = switch (obj) { + case Block block -> block.getState(); + case ItemType itemType when itemType.getItemMeta() instanceof BlockStateMeta blockStateMeta -> + blockStateMeta.getBlockState(); + default -> null; + }; + if (blockState instanceof Sign sign) { + return sign.getSide(side).isGlowingText(); + } + return false; + } + + @Override + protected PropertyType getPropertyType() { + return PropertyType.HAVE; + } + + @Override + protected String getPropertyName() { + return "glowing text"; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "the " + (side == Side.FRONT ? "front" : "back") + " side of " + super.toString(event, debug); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/effects/EffGlowingText.java b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/effects/EffGlowingText.java new file mode 100644 index 00000000000..6b18b63c214 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/effects/EffGlowingText.java @@ -0,0 +1,77 @@ +package org.skriptlang.skript.bukkit.block.sign.elements.effects; + +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; +import org.bukkit.event.Event; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Make Sign Glow") +@Description(""" + Makes a sign (either a block or item) have glowing text or normal text. \ + If a sign side is not specified, the front side will be used by default. + """) +@Example("make target block of player have glowing text") +@Since({"2.8.0", "INSERT VERSION (sign side support)"}) +public class EffGlowingText extends Effect { + + public static void register(SyntaxRegistry syntaxRegistry) { + syntaxRegistry.register(SyntaxRegistry.EFFECT, SyntaxInfo.simple(EffGlowingText.class, EffGlowingText::new, + "make [[the] (front|:back) [side[s]] of] %blocks/itemtypes% have glowing text", + "make [[the] (front|:back) [side[s]] of] %blocks/itemtypes% have (normal|non[-| ]glowing) text")); + } + + private Side side; + private Expression objects; + private boolean glowing; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + side = parseResult.hasTag("back") ? Side.BACK : Side.FRONT; + objects = expressions[0]; + glowing = matchedPattern == 0; + return true; + } + + @Override + protected void execute(Event event) { + for (Object object : objects.getArray(event)) { + if (object instanceof Block block) { + BlockState state = block.getState(); + if (state instanceof Sign sign) { + sign.getSide(side).setGlowingText(glowing); + state.update(); + } + } else if (object instanceof ItemType itemType) { + if (!(itemType.getItemMeta() instanceof BlockStateMeta blockStateMeta) || + !(blockStateMeta.getBlockState() instanceof Sign sign)) { + continue; + } + sign.getSide(side).setGlowingText(glowing); + sign.update(); + blockStateMeta.setBlockState(sign); + itemType.setItemMeta(blockStateMeta); + } + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "make " + objects.toString(event, debug) + " have " + (glowing ? "glowing text" : "normal text"); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/expressions/ExprSignText.java b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/expressions/ExprSignText.java index 7669a8a5112..f06c538f797 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/expressions/ExprSignText.java +++ b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/expressions/ExprSignText.java @@ -1,5 +1,6 @@ package org.skriptlang.skript.bukkit.block.sign.elements.expressions; +import ch.njol.skript.Skript; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Example; @@ -9,6 +10,7 @@ import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.SyntaxStringBuilder; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.util.Kleenean; @@ -24,42 +26,61 @@ import org.skriptlang.skript.registration.SyntaxRegistry; @Name("Sign Text") -@Description("A line of text on a sign. Can be changed, but note that there is a 16 character limit per line.") +@Description(""" + A line of text on a sign. \ + Can be changed, but note that there is a 16 character limit per line. \ + If a sign side is not specified, the front side will be used by default, unless in a sign change event and a block is not explicitly specified. + """) @Example(""" on right click: clicked block is tagged as "minecraft:all_signs" if line 2 of the clicked block is "[Heal]": heal the player """) -@Since("1.3") -// TODO SignSide support +@Example(""" + on sign change: + any of the lines contain "bad word" + cancel the event + send "You may not write profanity on signs!" to the player + """) +@Since({"1.3", "INSERT VERSION (sign side support)"}) public class ExprSignText extends SimpleExpression { public static void register(SyntaxRegistry syntaxRegistry) { syntaxRegistry.register(SyntaxRegistry.EXPRESSION, SyntaxInfo.Expression.builder(ExprSignText.class, Component.class) .supplier(ExprSignText::new) .priority(PropertyExpression.DEFAULT_PRIORITY) - .addPatterns("line %integer% [of %block%]", - "[the] (1:1st|1:first|2:2nd|2:second|3:3rd|3:third|4:4th|4:fourth) line [of %block%]") + .addPatterns("line %integer% [of [the] (:front|:back) [side]] [of %block%]", + "[the] (1:1st|1:first|2:2nd|2:second|3:3rd|3:third|4:4th|4:fourth) line [of [the] (:front|:back) [side]] [of %block%]", + "[all [[of] the]|the] lines [of [the] (:front|:back) [side]] [of %block%]") .build()); } - private Expression line; + private @Nullable Expression line; private Expression block; + private @Nullable Side side; @Override @SuppressWarnings("unchecked") - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { if (matchedPattern == 0) { line = (Expression) exprs[0]; - } else { + } else if (matchedPattern == 1) { line = new SimpleLiteral<>(parseResult.mark, false); } block = (Expression) exprs[exprs.length - 1]; + if (parseResult.hasTag("front")) { + side = Side.FRONT; + } else if (parseResult.hasTag("back")) { + side = Side.BACK; + } return true; } private int getLine(Event event) { + if (this.line == null) { + return 0; + } Integer line = this.line.getSingle(event); if (line == null) { return -1; @@ -78,18 +99,36 @@ protected Component[] get(Event event) { if (line == -1) { return new Component[0]; } - if (getTime() >= 0 && block.isDefault() && event instanceof SignChangeEvent signEvent && !Delay.isDelayed(event)) { - return new Component[]{signEvent.line(line)}; + + Side side = this.side; + if (getTime() >= 0 && block.isDefault() && event instanceof SignChangeEvent signEvent && (side == null || side == signEvent.getSide())) { + if (Delay.isDelayed(event)) { // event is delayed, obtain with regular methods on the correct side + side = signEvent.getSide(); + } else if (this.line == null) { + return signEvent.lines().toArray(new Component[0]); + } else { + return new Component[]{signEvent.line(line)}; + } + } else if (side == null) { + side = Side.FRONT; } + Block block = this.block.getSingle(event); if (block == null || !(block.getState() instanceof Sign signState)) { return new Component[0]; } - return new Component[]{signState.getSide(Side.FRONT).line(line)}; + if (this.line == null) { + return signState.getSide(side).lines().toArray(new Component[0]); + } + return new Component[]{signState.getSide(side).line(line)}; } @Override public Class @Nullable [] acceptChange(ChangeMode mode) { + if (this.line == null) { + Skript.error("It is only possible to change a specific sign line, not all lines at once."); + return null; + } // TODO allow add, remove, and remove all (see ExprLore) return switch (mode) { case SET, DELETE -> CollectionUtils.array(Component.class); @@ -104,16 +143,23 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { return; } - if (getTime() >= 0 && block.isDefault() && event instanceof SignChangeEvent signEvent && !Delay.isDelayed(event)) { - signEvent.line(line, delta == null ? null : (Component) delta[0]); - return; + Side side = this.side; + if (getTime() >= 0 && block.isDefault() && event instanceof SignChangeEvent signEvent && (side == null || side == signEvent.getSide())) { + if (Delay.isDelayed(event)) { // event is delayed, modify with regular methods on the correct side + side = signEvent.getSide(); + } else { + signEvent.line(line, delta == null ? null : (Component) delta[0]); + return; + } + } else if (side == null) { + side = Side.FRONT; } Block block = this.block.getSingle(event); if (block == null || !(block.getState() instanceof Sign signState)) { return; } - signState.getSide(Side.FRONT).line(line, delta == null ? Component.empty() : (Component) delta[0]); + signState.getSide(side).line(line, delta == null ? Component.empty() : (Component) delta[0]); signState.update(false, false); } @@ -129,7 +175,12 @@ public Class getReturnType() { @Override public String toString(@Nullable Event event, boolean debug) { - return "line " + line.toString(event, debug) + " of " + block.toString(event, debug); + return new SyntaxStringBuilder(event, debug) + .appendIf(line == null, "the lines") + .appendIf(line != null, "line", line) + .appendIf(side != null, "of the", (side == Side.FRONT ? "front" : "back"), "side") + .append("of", block) + .toString(); } @Override diff --git a/src/test/skript/tests/bukkit/block/sign/EffGlowingText.sk b/src/test/skript/tests/bukkit/block/sign/EffGlowingText.sk new file mode 100644 index 00000000000..0b1fabd6167 --- /dev/null +++ b/src/test/skript/tests/bukkit/block/sign/EffGlowingText.sk @@ -0,0 +1,28 @@ +test "glowing sign blocks": + set {_loc} to spawn of "world" + set {_original block} to type of block at {_loc} + set block at {_loc} to oak wall sign + assert block at {_loc} doesn't have glowing text with "Sign had glowing text erroneously (1)" + assert the front side of block at {_loc} doesn't have glowing text with "Sign front had glowing text erroneously (1)" + assert the back side of block at {_loc} doesn't have glowing text with "Sign back had glowing text erroneously (1)" + make block at {_loc} have glowing text + assert block at {_loc} has glowing text with "Sign had normal text erroneously" + assert the front side of block at {_loc} has glowing text with "Sign front had normal text erroneously" + assert the back side of the block at {_loc} doesn't have glowing text with "Sign back had glowing text erroneously (2)" + make block at {_loc} have normal text + assert block at {_loc} doesn't have glowing text with "Sign had glowing text erroneously (2)" + assert the front side of block at {_loc} doesn't have glowing text with "Sign front had glowing text erroneously (2)" + set block at {_loc} to {_original block} + +test "glowing sign items": + set {_sign} to oak sign + assert {_sign} doesn't have glowing text with "Sign had glowing text erroneously (1)" + assert the front side of {_sign} doesn't have glowing text with "Sign front had glowing text erroneously (1)" + assert the back side of {_sign} doesn't have glowing text with "Sign back had glowing text erroneously (1)" + make {_sign} have glowing text + assert {_sign} has glowing text with "Sign had normal text erroneously" + assert the front side of {_sign} has glowing text with "Sign front had normal text erroneously" + assert the back side of {_sign} doesn't have glowing text with "Sign back had glowing text erroneously (2)" + make {_sign} have normal text + assert {_sign} doesn't have glowing text with "Sign had glowing text erroneously (2)" + assert the front side of {_sign} doesn't have glowing text with "Sign front had glowing text erroneously (2)" diff --git a/src/test/skript/tests/bukkit/block/sign/ExprSignText.sk b/src/test/skript/tests/bukkit/block/sign/ExprSignText.sk index eb06a826652..d5d1decc771 100644 --- a/src/test/skript/tests/bukkit/block/sign/ExprSignText.sk +++ b/src/test/skript/tests/bukkit/block/sign/ExprSignText.sk @@ -9,7 +9,10 @@ test "ExprSignText": loop 4 times: set line loop-number of event-block to "%loop-number%" + set line loop-number of the back side of event-block to "%loop-number% back" assert line loop-number of event-block is "%loop-number%" with "setting line %loop-number% did not work" + assert line loop-number of the front side of event-block is "%loop-number%" with "setting line %loop-number% did not work" + assert line loop-number of the back side of event-block is "%loop-number% back" with "setting line %loop-number% did not work" set block at event-block to {_old_block} set block below event-block to {_old_block_below} diff --git a/src/test/skript/tests/syntaxes/effects/EffGlowingText.sk b/src/test/skript/tests/syntaxes/effects/EffGlowingText.sk deleted file mode 100644 index 4fab70da34e..00000000000 --- a/src/test/skript/tests/syntaxes/effects/EffGlowingText.sk +++ /dev/null @@ -1,18 +0,0 @@ -test "glowing sign blocks": - set {_loc} to spawn of "world" - set {_original block} to type of block at {_loc} - set block at {_loc} to oak wall sign - assert block at {_loc} doesn't have glowing text with "Sign had glowing text erroneously (1)" - make block at {_loc} have glowing text - assert block at {_loc} has glowing text with "Sign had normal text erroneously" - make block at {_loc} have normal text - assert block at {_loc} doesn't have glowing text with "Sign had glowing text erroneously (2)" - set block at {_loc} to {_original block} - -test "glowing sign items": - set {_sign} to oak sign - assert {_sign} doesn't have glowing text with "Sign had glowing text erroneously (1)" - make {_sign} have glowing text - assert {_sign} has glowing text with "Sign had normal text erroneously" - make {_sign} have normal text - assert {_sign} doesn't have glowing text with "Sign had glowing text erroneously (2)" From d72591b6aeb38fe290dc678a0125f868bbd85d16 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Fri, 4 Sep 2026 13:49:42 -0400 Subject: [PATCH 11/11] Experiment Registry Compatibility Fix (#8869) --- .../lang/experiment/ExperimentRegistry.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/org/skriptlang/skript/lang/experiment/ExperimentRegistry.java b/src/main/java/org/skriptlang/skript/lang/experiment/ExperimentRegistry.java index 3d0b5ee33dd..b6b35f4676b 100644 --- a/src/main/java/org/skriptlang/skript/lang/experiment/ExperimentRegistry.java +++ b/src/main/java/org/skriptlang/skript/lang/experiment/ExperimentRegistry.java @@ -98,6 +98,14 @@ public void register(SkriptAddon addon, Experiment experiment) { this.experiments.add(experiment); } + /** + * @deprecated Use {@link #register(SkriptAddon, Experiment)}. + */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public void register(ch.njol.skript.SkriptAddon addon, Experiment experiment) { + register((SkriptAddon) addon, experiment); + } + /** * @see #register(SkriptAddon, Experiment) */ @@ -107,6 +115,14 @@ public void registerAll(SkriptAddon addon, Experiment... experiments) { } } + /** + * @deprecated Use {@link #registerAll(SkriptAddon, Experiment...)}. + */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public void registerAll(ch.njol.skript.SkriptAddon addon, Experiment... experiments) { + registerAll((SkriptAddon) addon, experiments); + } + /** * Unregisters an experimental feature flag. * Loaded scripts currently using the flag will not have it disabled. @@ -122,6 +138,14 @@ public void unregister(SkriptAddon addon, Experiment experiment) { this.experiments.remove(experiment); } + /** + * @deprecated Use {@link #unregister(SkriptAddon, Experiment)}. + */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public void unregister(ch.njol.skript.SkriptAddon addon, Experiment experiment) { + unregister((SkriptAddon) addon, experiment); + } + /** * Creates (and registers) a new experimental feature flag, which will be available to scripts * with the {@code using %name%} structure. @@ -138,6 +162,14 @@ public Experiment register(SkriptAddon addon, String codeName, LifeCycle phase, return experiment; } + /** + * @deprecated Use {@link #register(SkriptAddon, String, LifeCycle, String...)}. + */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public Experiment register(ch.njol.skript.SkriptAddon addon, String codeName, LifeCycle phase, String... patterns) { + return register((SkriptAddon) addon, codeName, phase, patterns); + } + @Override public boolean hasExperiment(Experiment experiment) { return experiments.contains(experiment);