From 7f6451b217348f1c0bcf2be51b5a823eb61aaf60 Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Fri, 17 Oct 2025 12:07:58 -0400 Subject: [PATCH 1/5] Initial Commit --- .../skript/classes/data/BukkitClasses.java | 42 ++- .../njol/skript/conditions/CondIsWaxed.java | 54 ++++ .../java/ch/njol/skript/effects/EffWax.java | 105 +++++++ .../njol/skript/entity/CopperGolemData.java | 116 ++++++++ .../njol/skript/entity/SimpleEntityData.java | 1 - .../ExprCopperGolemOxidationTime.java | 106 +++++++ .../expressions/ExprCopperGolemPose.java | 68 +++++ .../skript/expressions/ExprCopperState.java | 181 ++++++++++++ .../ch/njol/skript/paperutil/CopperState.java | 51 ++++ .../skriptlang/skript/util/ClassLoader.java | 2 +- .../skriptlang/skript/util/ReflectUtils.java | 266 ++++++++++++++++++ src/main/resources/lang/default.lang | 27 +- src/test/skript/tests/misc/EntityData.sk | 44 +++ .../skript/tests/syntaxes/effects/EffWax.sk | 26 ++ .../ExprCopperGolemOxidationTime.sk | 10 + .../expressions/ExprCopperGolemPose.sk | 9 + .../syntaxes/expressions/ExprCopperState.sk | 39 +++ .../tests/syntaxes/sections/EffSecSpawn.sk | 44 +++ 18 files changed, 1183 insertions(+), 8 deletions(-) create mode 100644 src/main/java/ch/njol/skript/conditions/CondIsWaxed.java create mode 100644 src/main/java/ch/njol/skript/effects/EffWax.java create mode 100644 src/main/java/ch/njol/skript/entity/CopperGolemData.java create mode 100644 src/main/java/ch/njol/skript/expressions/ExprCopperGolemOxidationTime.java create mode 100644 src/main/java/ch/njol/skript/expressions/ExprCopperGolemPose.java create mode 100644 src/main/java/ch/njol/skript/expressions/ExprCopperState.java create mode 100644 src/main/java/ch/njol/skript/paperutil/CopperState.java create mode 100644 src/main/java/org/skriptlang/skript/util/ReflectUtils.java create mode 100644 src/test/skript/tests/syntaxes/effects/EffWax.sk create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprCopperGolemOxidationTime.sk create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprCopperGolemPose.sk create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprCopperState.sk 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 9794bfc2e22..09f03904541 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -4,12 +4,17 @@ import ch.njol.skript.bukkitutil.BukkitUtils; import ch.njol.skript.bukkitutil.EntityUtils; import ch.njol.skript.bukkitutil.SkriptTeleportFlag; -import ch.njol.skript.classes.*; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.EnumClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.classes.PatternedParser; +import ch.njol.skript.classes.Serializer; import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.expressions.ExprDamageCause; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.lang.util.SimpleLiteral; +import ch.njol.skript.paperutil.CopperState; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.BlockUtils; import ch.njol.skript.util.PotionEffectUtils; @@ -23,10 +28,16 @@ import org.bukkit.block.DoubleChest; import org.bukkit.block.banner.PatternType; import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.CopperGolemStatue; import org.bukkit.command.CommandSender; import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.EnchantmentOffer; -import org.bukkit.entity.*; +import org.bukkit.entity.EntitySnapshot; +import org.bukkit.entity.Item; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Vehicle; +import org.bukkit.entity.Villager; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityPotionEffectEvent; @@ -52,8 +63,14 @@ import org.bukkit.util.CachedServerIcon; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.bukkit.base.types.*; +import org.skriptlang.skript.bukkit.base.types.BlockClassInfo; +import org.skriptlang.skript.bukkit.base.types.EntityClassInfo; import org.skriptlang.skript.bukkit.base.types.EntityClassInfo.EntityChanger; +import org.skriptlang.skript.bukkit.base.types.InventoryClassInfo; +import org.skriptlang.skript.bukkit.base.types.ItemStackClassInfo; +import org.skriptlang.skript.bukkit.base.types.NameableClassInfo; +import org.skriptlang.skript.bukkit.base.types.OfflinePlayerClassInfo; +import org.skriptlang.skript.bukkit.base.types.PlayerClassInfo; import org.skriptlang.skript.lang.properties.Property; import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; @@ -1140,5 +1157,24 @@ public String toVariableNameString(WorldBorder border) { .since("2.12") ); + //noinspection unchecked,rawtypes + Classes.registerClass(new EnumClassInfo<>((Class) CopperState.getStateClass(), "weatheringcopperstate", "weathering copper states") + .user("(weathering ?)?copper ?states?") + .name("Weathering Copper State") + .description("The weathering state of a copper golem or copper block.") + .requiredPlugins("Minecraft 1.21.9+") + .since("INSERT VERSION") + ); + + if (Skript.classExists("org.bukkit.block.data.type.CopperGolemStatue$Pose")) { + Classes.registerClass(new EnumClassInfo<>(CopperGolemStatue.Pose.class, "coppergolempose", "copper golem poses") + .user("copper ? golem ?(statue ?)?poses?") + .name("Copper Golem Pose") + .description("The pose of a copper golem statue.") + .requiredPlugins("Minecraft 1.21.9+") + .since("INSERT VERSION") + ); + } + } } diff --git a/src/main/java/ch/njol/skript/conditions/CondIsWaxed.java b/src/main/java/ch/njol/skript/conditions/CondIsWaxed.java new file mode 100644 index 00000000000..db69003f20b --- /dev/null +++ b/src/main/java/ch/njol/skript/conditions/CondIsWaxed.java @@ -0,0 +1,54 @@ +package ch.njol.skript.conditions; + +import ch.njol.skript.Skript; +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.RequiredPlugins; +import ch.njol.skript.doc.Since; +import com.destroystokyo.paper.MaterialTags; +import org.bukkit.block.Block; +import org.bukkit.entity.CopperGolem; +import org.bukkit.entity.CopperGolem.Oxidizing.Waxed; +import org.skriptlang.skript.util.ReflectUtils; + +@Name("Is Waxed") +@Description("Whether a copper golem or copper block is waxed.") +@Example(""" + if last spawned copper golem is not waxed: + wax last spawned copper golem + """) +@Example(""" + if {_block} is waxed: + unwax {_block} + """) +@RequiredPlugins("Minecraft 1.21.9+ (copper golems)") +@Since("INSERT VERSION") +public class CondIsWaxed extends PropertyCondition { + + private static final boolean COPPER_GOLEM_EXISTS = ReflectUtils.classExists("org.bukkit.entity.CopperGolem"); + + static { + String type = "blocks"; + if (Skript.classExists("org.bukkit.entity.CopperGolem")) + type = "entities/blocks"; + register(CondIsWaxed.class, "waxed", type); + } + + @Override + public boolean check(Object object) { + if (COPPER_GOLEM_EXISTS && object instanceof CopperGolem golem) { + return golem.getOxidizing() instanceof Waxed; + } else if (object instanceof Block block) { + return MaterialTags.WAXED_COPPER_BLOCKS.isTagged(block); + } + return false; + } + + @Override + protected String getPropertyName() { + return "waxed"; + } + +} diff --git a/src/main/java/ch/njol/skript/effects/EffWax.java b/src/main/java/ch/njol/skript/effects/EffWax.java new file mode 100644 index 00000000000..c2faa3a2d34 --- /dev/null +++ b/src/main/java/ch/njol/skript/effects/EffWax.java @@ -0,0 +1,105 @@ +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.RequiredPlugins; +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.lang.SyntaxStringBuilder; +import ch.njol.util.Kleenean; +import com.destroystokyo.paper.MaterialTags; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.CopperGolem; +import org.bukkit.entity.CopperGolem.Oxidizing; +import org.bukkit.entity.CopperGolem.Oxidizing.Waxed; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.util.ReflectUtils; + +@Name("Wax") +@Description(""" + Wax or unwax a copper golem or copper block. + This does not change the weathering copper state of entities and blocks. + """) +@Example(""" + if last spawned copper golem is not waxed: + wax last spawned copper golem + """) +@Example(""" + if {_block} is waxed: + unwax {_block} + """) +@RequiredPlugins("Minecraft 1.21.9+ (copper golems)") +@Since("INSERT VERSION") +public class EffWax extends Effect { + + + private static final BiMap WAX_CONVERSION; + private static final BiMap UNWAX_CONVERSION = HashBiMap.create(); + private static final boolean COPPER_GOLEM_EXISTS = ReflectUtils.classExists("org.bukkit.entity.CopperGolem"); + + static { + String type = "%blocks%"; + if (COPPER_GOLEM_EXISTS) + type = "%entities/blocks%"; + Skript.registerEffect(EffWax.class, "[:un]wax " + type); + + for (Material waxed : MaterialTags.WAXED_COPPER_BLOCKS.getValues()) { + Material unwaxed = Material.valueOf(waxed.name().replaceAll("WAXED_", "")); + UNWAX_CONVERSION.put(waxed, unwaxed); + } + WAX_CONVERSION = UNWAX_CONVERSION.inverse(); + } + + private boolean wax; + private Expression objects; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + objects = exprs[0]; + wax = !parseResult.hasTag("un"); + return true; + } + + @Override + protected void execute(Event event) { + for (Object object : objects.getArray(event)) { + if (COPPER_GOLEM_EXISTS && object instanceof CopperGolem golem) { + boolean isWaxed = golem.getOxidizing() instanceof Waxed; + if (wax == isWaxed) + continue; + CopperGolem.Oxidizing oxidizing = wax ? Oxidizing.waxed() : Oxidizing.unset(); + golem.setOxidizing(oxidizing); + } else if (object instanceof Block block) { + if (!MaterialTags.COPPER_BLOCKS.isTagged(block)) + continue; + BiMap conversion = wax ? WAX_CONVERSION : UNWAX_CONVERSION; + if (conversion.containsKey(block.getType())) { + Material material = conversion.get(block.getType()); + assert material != null; + block.setType(material); + } + } + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug); + if (wax) { + builder.append("wax"); + } else { + builder.append("unwax"); + } + builder.append(objects); + return builder.toString(); + } + +} diff --git a/src/main/java/ch/njol/skript/entity/CopperGolemData.java b/src/main/java/ch/njol/skript/entity/CopperGolemData.java new file mode 100644 index 00000000000..31cd225bb0f --- /dev/null +++ b/src/main/java/ch/njol/skript/entity/CopperGolemData.java @@ -0,0 +1,116 @@ +package ch.njol.skript.entity; + +import ch.njol.skript.Skript; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.localization.Language; +import ch.njol.util.Kleenean; +import io.papermc.paper.world.WeatheringCopperState; +import org.bukkit.entity.CopperGolem; +import org.bukkit.entity.CopperGolem.Oxidizing.Waxed; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class CopperGolemData extends EntityData { + + static { + if (Skript.classExists("org.bukkit.entity.CopperGolem")) + register(CopperGolemData.class, "copper golem", CopperGolem.class, 0, "copper golem"); + } + + private Kleenean waxed = Kleenean.UNKNOWN; + private @Nullable WeatheringCopperState state; + + public CopperGolemData() {} + + public CopperGolemData(@Nullable Kleenean waxed, @Nullable WeatheringCopperState state) { + this.waxed = waxed == null ? Kleenean.UNKNOWN : waxed; + this.state = state; + } + + @Override + protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPattern, ParseResult parseResult) { + //noinspection unchecked + Literal expr = (Literal) exprs[0]; + if (expr != null) + state = expr.getSingle(); + if (matchedPattern == 1) { + waxed = Kleenean.TRUE; + } else if (matchedPattern == 2) { + waxed = Kleenean.FALSE; + } + return true; + } + + @Override + protected boolean init(@Nullable Class entityClass, @Nullable CopperGolem golem) { + if (golem != null) { + state = golem.getWeatheringState(); + waxed = Kleenean.get(golem.getOxidizing() instanceof Waxed); + } + return true; + } + + @Override + public void set(CopperGolem golem) { + if (state != null) + golem.setWeatheringState(state); + if (waxed.isTrue()) + golem.setOxidizing(CopperGolem.Oxidizing.waxed()); + } + + @Override + protected boolean match(CopperGolem golem) { + if (!dataMatch(state, golem.getWeatheringState())) + return false; + return kleeneanMatch(waxed, Kleenean.get(golem.getOxidizing() instanceof Waxed)); + } + + @Override + public Class getType() { + return CopperGolem.class; + } + + @Override + public @NotNull EntityData getSuperType() { + return new CopperGolemData(); + } + + @Override + protected int hashCode_i() { + return Objects.hashCode(state) + waxed.hashCode(); + } + + @Override + protected boolean equals_i(EntityData entityData) { + if (!(entityData instanceof CopperGolemData other)) + return false; + return state == other.state && waxed == other.waxed; + } + + @Override + public boolean isSupertypeOf(EntityData entityData) { + if (!(entityData instanceof CopperGolemData other)) + return false; + return dataMatch(state, other.state) && kleeneanMatch(waxed, other.waxed); + } + + @Override + public String toString(int flags) { + StringBuilder builder = new StringBuilder(); + if (waxed.isTrue()) { + builder.append("waxed "); + } else if (waxed.isFalse()) { + builder.append("unwaxed "); + } + if (state != null) + builder.append(Language.getList("weathering copper states." + state.name())[0] + " "); + builder.append("copper golem"); + if (isPlural().isTrue()) + builder.append("s"); + return builder.toString(); + } + +} diff --git a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java index d723321dca0..ed0292a3391 100644 --- a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java +++ b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java @@ -241,7 +241,6 @@ private static void addSuperEntity(String codeName, Class enti addSimpleEntity("happy ghast", HappyGhast.class); if (Skript.isRunningMinecraft(1, 21, 9)) { - addSimpleEntity("copper golem", CopperGolem.class); addSimpleEntity("mannequin", Mannequin.class); } diff --git a/src/main/java/ch/njol/skript/expressions/ExprCopperGolemOxidationTime.java b/src/main/java/ch/njol/skript/expressions/ExprCopperGolemOxidationTime.java new file mode 100644 index 00000000000..0658ff329a9 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprCopperGolemOxidationTime.java @@ -0,0 +1,106 @@ +package ch.njol.skript.expressions; + +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.RequiredPlugins; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.util.Timespan; +import ch.njol.skript.util.Timespan.TimePeriod; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.CopperGolem; +import org.bukkit.entity.Entity; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.util.ReflectUtils; + +import java.lang.reflect.Method; + +@Name("Time Until Oxidation") +@Description(""" + The time until a copper golem goes through oxidation til reaching the max oxidation phase. (Normal -> Exposed -> Weathered -> Oxidized) + Copper golems that are waxed do not go through oxidation. + Setting or resetting the time until oxidation on a waxed copper golem will remove the waxed state. + Resetting the time until oxidation uses vanilla behavior of generating a random time. + """) +@Example("set {_time} to the time until oxidation of last spawned copper golem") +@Example("set the time until oxidation of last spawned copper golem to 10 seconds") +@Example("clear the time until oxidation of last spawned copper golem") +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class ExprCopperGolemOxidationTime extends SimplePropertyExpression { + + private static Method getOxidizingMethod; + private static Class atTimeClass; + private static Method getTimeMethod; + private static Method newTimeMethod; + private static Method unsetMethod; + + static { + if (ReflectUtils.classExists("org.bukkit.entity.CopperGolem")) { + register(ExprCopperGolemOxidationTime.class, Timespan.class, "time until oxidation", "entities"); + + getOxidizingMethod = ReflectUtils.getMethod("org.bukkit.entity.CopperGolem", "getOxidizing"); + atTimeClass = ReflectUtils.getClass("org.bukkit.entity.CopperGolem$Oxidizing$AtTime"); + getTimeMethod = ReflectUtils.getMethod(atTimeClass, "time"); + newTimeMethod = ReflectUtils.getMethod("org.bukkit.entity.CopperGolem$Oxidizing", "atTime", long.class); + unsetMethod = ReflectUtils.getMethod("org.bukkit.entity.CopperGolem$Oxidizing", "unset"); + } + } + + @Override + public @Nullable Timespan convert(Entity entity) { + if (entity instanceof CopperGolem golem) { + Object oxidizing = ReflectUtils.methodInvoke(getOxidizingMethod, golem); + if (!(atTimeClass.isInstance(oxidizing))) + return null; + long worldTime = golem.getWorld().getGameTime(); + //noinspection DataFlowIssue + long oxidationTime = ReflectUtils.methodInvoke(getTimeMethod, oxidizing); + if (worldTime > oxidationTime) + return null; + return new Timespan(TimePeriod.TICK, oxidationTime - worldTime); + } + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.RESET) + return CollectionUtils.array(Timespan.class); + return null; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + if (mode == ChangeMode.SET) { + assert delta != null; + long ticks = ((Timespan) delta[0]).getAs(TimePeriod.TICK); + for (Entity entity : getExpr().getArray(event)) { + if (!(entity instanceof CopperGolem golem)) + continue; + long worldTime = golem.getWorld().getGameTime(); + golem.setOxidizing(ReflectUtils.methodInvoke(newTimeMethod, null, worldTime + ticks)); + } + } else if (mode == ChangeMode.RESET) { + for (Entity entity : getExpr().getArray(event)) { + if (!(entity instanceof CopperGolem golem)) + continue; + golem.setOxidizing(ReflectUtils.methodInvoke(unsetMethod)); + } + } + } + + @Override + public Class getReturnType() { + return Timespan.class; + } + + @Override + protected String getPropertyName() { + return "time until oxidation"; + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprCopperGolemPose.java b/src/main/java/ch/njol/skript/expressions/ExprCopperGolemPose.java new file mode 100644 index 00000000000..c65c68c8920 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprCopperGolemPose.java @@ -0,0 +1,68 @@ +package ch.njol.skript.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; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.RequiredPlugins; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.block.Block; +import org.bukkit.block.data.type.CopperGolemStatue; +import org.bukkit.block.data.type.CopperGolemStatue.Pose; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Copper Golem Statue Pose") +@Description("The pose of a copper golem statue.") +@Example("set {_pose} to the copper golem statue pose of {_statue}") +@Example("set the copper golem pose of {_statue} to running") +@RequiredPlugins("Minecraft 1.21.9+") +@Since("INSERT VERSION") +public class ExprCopperGolemPose extends SimplePropertyExpression { + + static { + if (Skript.classExists("org.bukkit.block.data.type.CopperGolemStatue")) + register(ExprCopperGolemPose.class, Pose.class, "copper golem [statue] pose[s]", "blocks"); + } + + @Override + public @Nullable Pose convert(Block block) { + if (block.getBlockData() instanceof CopperGolemStatue statue) + return statue.getCopperGolemPose(); + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET) + return CollectionUtils.array(Pose.class); + return null; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + assert delta != null; + Pose pose = (Pose) delta[0]; + + for (Block block : getExpr().getArray(event)) { + if (block.getBlockData() instanceof CopperGolemStatue statue) { + statue.setCopperGolemPose(pose); + block.setBlockData(statue, true); + } + } + } + + @Override + public Class getReturnType() { + return Pose.class; + } + + @Override + protected String getPropertyName() { + return "copper golem pose"; + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprCopperState.java b/src/main/java/ch/njol/skript/expressions/ExprCopperState.java new file mode 100644 index 00000000000..629fb7552f9 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprCopperState.java @@ -0,0 +1,181 @@ +package ch.njol.skript.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; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.RequiredPlugins; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.paperutil.CopperState; +import ch.njol.util.coll.CollectionUtils; +import com.destroystokyo.paper.MaterialTags; +import io.papermc.paper.world.WeatheringCopperState; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.CopperGolem; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.util.ReflectUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +@Name("Weathering Copper State") +@Description(""" + The weathering copper state of a copper golem or copper block. + Changing the copper state does not change the waxed state. + """) +@Example(""" + if the copper state of last spawned copper golem is weathered: + set the copper state of last spawned golem to normal + """) +@Example(""" + if the weathering copper state of {_block} is not oxidized: + set the weathering copper state of {_block} to oxidized + """) +@RequiredPlugins("Minecraft 1.21.9+ (copper golems)") +@Since("INSERT VERSION") +public class ExprCopperState extends SimplePropertyExpression { + + // TODO: Remove 'CopperState' and change all instances of 'Enum' to 'WeatheringCopperState' + + private static final CopperStateMaterialMap STATE_MATERIALS = new CopperStateMaterialMap(); + private static final List STATE_REPLACEMENTS = new ArrayList<>(); + private static final boolean COPPER_GOLEM_EXISTS = ReflectUtils.classExists("org.bukkit.entity.CopperGolem"); + + static { + String type = "blocks"; + if (Skript.classExists("org.bukkit.entity.CopperGolem")) + type = "entities/blocks"; + + register(ExprCopperState.class, Object.class, "[weathering] copper state[s]", type); + + for (Enum state : CopperState.getValues()) { + if (state.name().equals("UNAFFECTED")) + continue; + STATE_REPLACEMENTS.add(state.name() + "_"); + } + + for (Material material : MaterialTags.COPPER_BLOCKS.getValues()) { + Enum state = getMaterialCopperState(material); + assert state != null; + String blockType = getBlockType(material); + STATE_MATERIALS.putMaterial(blockType, state, material); + } + } + + /** + * Gets the string of the block type {@code material} relates to. + * i.e. stair, slab, door, trapdoor, etc. + * @param material The {@link Material} to get the block type from. + * @return The resulting string block type. + */ + public static String getBlockType(Material material) { + String type = material.name(); + for (String replace : STATE_REPLACEMENTS) + type = type.replaceAll(replace, ""); + if (type.equals("COPPER_BLOCK")) { + type = "COPPER"; + } else if (type.equals("WAXED_COPPER_BLOCK")) { + type = "WAXED_COPPER"; + } + return type; + } + + /** + * Gets the {@link CopperState} or 'WeatheringCopperState' a {@link Material} belongs to. + * @param material The {@link Material} to check. + * @return The resulting {@link CopperState} or 'WeatheringCopperState' if found, otherwise {@code null}. + */ + public static @Nullable Enum getMaterialCopperState(Material material) { + if (!MaterialTags.COPPER_BLOCKS.isTagged(material)) + return null; + if (MaterialTags.EXPOSED_COPPER_BLOCKS.isTagged(material)) { + return CopperState.get(CopperState.EXPOSED); + } else if (MaterialTags.WEATHERED_COPPER_BLOCKS.isTagged(material)) { + return CopperState.get(CopperState.WEATHERED); + } else if (MaterialTags.OXIDIZED_COPPER_BLOCKS.isTagged(material)) { + return CopperState.get(CopperState.OXIDIZED); + } + return CopperState.get(CopperState.UNAFFECTED); + } + + @Override + public @Nullable Object convert(Object object) { + if (COPPER_GOLEM_EXISTS && object instanceof CopperGolem golem) { + return golem.getWeatheringState(); + } else if (object instanceof Block block) { + return getMaterialCopperState(block.getType()); + } + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.RESET) + return CollectionUtils.array(CopperState.getStateClass()); + return null; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Enum state = CopperState.get(CopperState.UNAFFECTED); + if (delta != null) + state = (Enum) delta[0]; + + for (Object object : getExpr().getArray(event)) { + if (COPPER_GOLEM_EXISTS && object instanceof CopperGolem golem) { + golem.setWeatheringState((WeatheringCopperState) state); + } else if (object instanceof Block block) { + Material material = getConvertedCopperMaterial(block.getType(), state); + if (material != null) + block.setType(material); + } + } + } + + /** + * Gets the {@link Material} that is the same block type as {@code material} and in the {@code state}. + * @param material The current {@link Material} to convert from. + * @param state The {@link CopperState} or 'WeatheringCopperState' to get the same block type as {@code material}. + * @return The resulting {@link Material} if found, otherwise {@code null}. + */ + public static @Nullable Material getConvertedCopperMaterial(Material material, Enum state) { + if (!MaterialTags.COPPER_BLOCKS.isTagged(material)) + return null; + String type = getBlockType(material); + return STATE_MATERIALS.getMaterial(type, state); + } + + @Override + public Class getReturnType() { + return CopperState.getStateClass(); + } + + @Override + protected String getPropertyName() { + return "weathering copper state"; + } + + /** + * Map for storing a block type, a {@link CopperState} or 'WeatheringCopperState' and the {@link Material} that is the block type and state. + */ + private static class CopperStateMaterialMap extends HashMap { + + public void putMaterial(String key, Enum state, Material material) { + computeIfAbsent(key, array -> new Material[CopperState.getValues().length])[state.ordinal()] = material; + } + + public @Nullable Material getMaterial(String key, Enum state) { + if (!containsKey(key)) + return null; + Material[] materials = get(key); + return materials[state.ordinal()]; + } + + } + +} diff --git a/src/main/java/ch/njol/skript/paperutil/CopperState.java b/src/main/java/ch/njol/skript/paperutil/CopperState.java new file mode 100644 index 00000000000..878480ce564 --- /dev/null +++ b/src/main/java/ch/njol/skript/paperutil/CopperState.java @@ -0,0 +1,51 @@ +package ch.njol.skript.paperutil; + +import org.jetbrains.annotations.ApiStatus.Internal; +import org.skriptlang.skript.util.ReflectUtils; + +import java.lang.reflect.Method; + +/** + * Temp enum to mimic 'WeatheringCopperState'. + */ +@Internal +public enum CopperState { + + EXPOSED, OXIDIZED, UNAFFECTED, WEATHERED; + + private static final Class WEATHERING_CLASS; + private static final Enum[] WEATHERING_VALUES; + private static final Method WEATHERING_VALUE_METHOD; + + static { + WEATHERING_CLASS = ReflectUtils.getClass("io.papermc.paper.world.WeatheringCopperState"); + if (WEATHERING_CLASS != null) { + Method valuesMethod = ReflectUtils.getMethod(WEATHERING_CLASS, "values"); + WEATHERING_VALUES = ReflectUtils.methodInvoke(valuesMethod); + WEATHERING_VALUE_METHOD = ReflectUtils.getMethod(WEATHERING_CLASS, "valueOf", String.class); + } else { + WEATHERING_VALUES = null; + WEATHERING_VALUE_METHOD = null; + } + } + + /** + * @return {@link CopperState} class or 'WeatheringCopperState' if it exists. + */ + public static Class getStateClass() { + return WEATHERING_CLASS != null ? WEATHERING_CLASS : CopperState.class; + } + + public static Enum[] getValues() { + if (WEATHERING_CLASS != null) + return WEATHERING_VALUES; + return CopperState.values(); + } + + public static Enum get(CopperState state) { + if (WEATHERING_CLASS != null) + return ReflectUtils.methodInvoke(WEATHERING_VALUE_METHOD, null, state.name()); + return state; + } + +} diff --git a/src/main/java/org/skriptlang/skript/util/ClassLoader.java b/src/main/java/org/skriptlang/skript/util/ClassLoader.java index d6f8ed44da4..cb48dcbcdfe 100644 --- a/src/main/java/org/skriptlang/skript/util/ClassLoader.java +++ b/src/main/java/org/skriptlang/skript/util/ClassLoader.java @@ -192,7 +192,7 @@ public void loadClasses(Class source, @Nullable JarFile jar) { if (this.forEachClass != null) { this.forEachClass.accept(clazz); } - } catch (ClassNotFoundException ex) { + } catch (ClassNotFoundException | NoClassDefFoundError ex) { throw new RuntimeException("Failed to load class: " + className, ex); } catch (ExceptionInInitializerError err) { throw new RuntimeException(className + " generated an exception while loading", err.getCause()); diff --git a/src/main/java/org/skriptlang/skript/util/ReflectUtils.java b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java new file mode 100644 index 00000000000..2d35d4cfb70 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java @@ -0,0 +1,266 @@ +package org.skriptlang.skript.util; + +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * Utility class for reflection. + */ +public class ReflectUtils { + + /** + * Cache for classes. + */ + private static final Map> CLASSES = new HashMap<>(); + + /** + * Cache for methods of classes. + */ + private static final MethodMap METHODS = new MethodMap(); + + /** + * Cache for fields of classes. + */ + private static final FieldMap FIELDS = new FieldMap(); + + /** + * @param className The full package and class name. + * @return Whether the class exists. + */ + public static boolean classExists(String className) { + return getClass(className) != null; + } + + /** + * @param className The full package and class name. + * @return The resulting {@link Class} if found, otherwise {@code null}. + */ + public static @Nullable Class getClass(String className) { + if (CLASSES.containsKey(className)) + return CLASSES.get(className); + Class c = null; + try { + c = Class.forName(className); + } catch (ClassNotFoundException ignored) {} + CLASSES.put(className, c); + return c; + } + + /** + * @param className The full package and class name. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @return Whether the method exists. + */ + public static boolean methodExists(String className, String methodName, Class @Nullable ... params) { + Class c = getClass(className); + if (c == null) + return false; + return methodExists(c, methodName, params); + } + + /** + * @param c The {@link Class} to check the method for. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @return Whether the method exists. + */ + public static boolean methodExists(Class c, String methodName, Class @Nullable ... params) { + return getMethod(c, methodName, params) != null; + } + + /** + * @param className The full package and class name. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @return The resulting {@link Method} if it exists, otherwise {@code null}. + */ + public static @Nullable Method getMethod(String className, String methodName, Class @Nullable ... params) { + Class c = getClass(className); + if (c == null) + return null; + return getMethod(c, methodName, params); + } + + /** + * @param c The {@link Class} to get the method from. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @return The resulting {@link Method} if it exists, otherwise {@code null}. + */ + public static @Nullable Method getMethod(Class c, String methodName, Class @Nullable ... params) { + MethodID methodID = new MethodID(c, methodName, params); + if (METHODS.contains(c, methodID)) + return METHODS.get(c, methodID); + Method method = null; + try { + method = c.getDeclaredMethod(methodName, params); + } catch (NoSuchMethodException ignored) {} + + METHODS.put(c, methodID, method); + return method; + } + + /** + * @param className The full package and class name. + * @param fieldName The name of the field. + * @return Whether the field exists. + */ + public static boolean fieldExists(String className, String fieldName) { + Class c = getClass(className); + if (c == null) + return false; + return fieldExists(c, fieldName); + } + + /** + * @param c The {@link Class} to check the field for. + * @param fieldName The name of the field. + * @return Whether the field exists. + */ + public static boolean fieldExists(Class c, String fieldName) { + return getField(c, fieldName) != null; + } + + /** + * @param className The full package and class name. + * @param fieldName The name of the field. + * @return The resulting {@link Field} if it exists, otherwise {@code null}. + */ + public static @Nullable Field getField(String className, String fieldName) { + Class c = getClass(className); + if (c == null) + return null; + return getField(c, fieldName); + } + + /** + * @param c The {@link Class} to get the field from. + * @param fieldName The name of the field. + * @return The resulting {@link Field} if it exists, otherwise {@code null}. + */ + public static @Nullable Field getField(Class c, String fieldName) { + if (FIELDS.contains(c, fieldName)) + return FIELDS.get(c, fieldName); + Field field = null; + try { + field = c.getDeclaredField(fieldName); + } catch (NoSuchFieldException ignored) {} + + FIELDS.put(c, fieldName, field); + return field; + } + + /** + * Invoke a static {@link Method}. + * @param method The {@link Method} to invoke. + * @return The result of the invocation if successful, otherwise {@code null}. + * @param The expected return type from the invocation. + */ + public static @Nullable Type methodInvoke(Method method) { + return methodInvoke(method, null); + } + + /** + * Invoke a {@link Method}. + * @param method The {@link Method} to invoke. + * @param holder The holder object to invoke for. + * @param params The parameters to pass into the invocation. + * @return The result of the invocation if successful, otherwise {@code null}. + * @param The expected return type from the invocation. + */ + public static @Nullable Type methodInvoke(Method method, @Nullable Object holder, Object @Nullable ... params) { + method.setAccessible(true); + try { + //noinspection unchecked + return (Type) method.invoke(holder, params); + } catch (IllegalAccessException | InvocationTargetException ignored) {} + return null; + } + + /** + * Gets the values of a static {@link Field}. + * @param field The {@link Field} to get from. + * @return The value of the {@link Field}. + * @param The expected return type. + */ + public static @Nullable Type fieldGet(Field field) { + return fieldGet(field, null); + } + + /** + * Gets the values of a {@link Field}. + * @param field The {@link Field} to get from. + * @param holder The holder object to get the field for. + * @return The value of the {@link Field}. + * @param The expected return type. + */ + public static @Nullable Type fieldGet(Field field, @Nullable Object holder) { + field.setAccessible(true); + try { + //noinspection unchecked + return (Type) field.get(holder); + } catch (IllegalAccessException ignored) {} + return null; + } + + /** + * Record for caching data for a method. + * @param c The {@link Class} the method belongs to. + * @param methodName The name of the method. + * @param params The types of parameters for the method. + */ + private record MethodID(Class c, String methodName, Class @Nullable ... params) {} + + /** + * Custom map for correlating a {@link Class} to a {@link Method}. + */ + private static class MethodMap extends HashMap, Map> { + + public boolean contains(Class c, MethodID methodID) { + if (!containsKey(c)) + return false; + return get(c).containsKey(methodID); + } + + public void put(Class c, MethodID methodID, Method method) { + computeIfAbsent(c, map -> new HashMap<>()).put(methodID, method); + } + + public @Nullable Method get(Class c, MethodID methodID) { + if (!contains(c, methodID)) + return null; + return get(c).get(methodID); + } + + } + + /** + * Custom map for correlating a {@link Class} to a {@link Field}. + */ + private static class FieldMap extends HashMap, Map> { + + public boolean contains(Class c, String fieldName) { + if (!containsKey(c)) + return false; + return get(c).containsKey(fieldName); + } + + public void put(Class c, String fieldName, Field field) { + computeIfAbsent(c, map -> new HashMap<>()).put(fieldName, field); + } + + public @Nullable Field get(Class c, String fieldName) { + if (!contains(c, fieldName)) + return null; + return get(c).get(fieldName); + } + + } + +} diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index e9853b2c01a..9e5d938754e 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -640,6 +640,14 @@ entities: 0: [%-cowvariant%] cow[plural:s] 1: baby:[%-cowvariant%] cal(f|plural:ves) + # CopperGolemData + copper golem: + name: copper golem¦s + patterns: + 0: [%-weatheringcopperstate%] copper golem[plural:s] + 1: waxed [%-weatheringcopperstate%] copper golem[plural:s] + 2: unwaxed [%-weatheringcopperstate%] copper golem[plural:s] + # CreeperData creeper: name: creeper¦s @@ -1590,9 +1598,6 @@ entities: 1: baby:[happy] ghastling[plural:s] # 1.21.9 Entities - copper golem: - name: copper golem¦s - pattern: copper golem[plural:s] mannequin: name: mannequin¦s pattern: mannequin[plural:s] @@ -3013,6 +3018,20 @@ damage types: wither: wither wither_skull: wither skull +# -- Weathering Copper States -- +weathering copper states: + exposed: exposed, exposed state, exposed copper, exposed copper state + oxidized: oxidized, oxidized state, oxidized copper, oxidized copper state + unaffected: normal state, normal copper state, unaffected, unaffected state, unaffected copper, unaffected copper state + weathered: weathered, weathered state, weathered copper, weathered copper state + +# -- Copper Golem Poses -- +copper golem poses: + running: running, running pose, running statue pose, statue running pose + sitting: sitting, sitting pose, sitting statue pose, statue sitting pose + standing: standing, standing pose, standing statue pose, statue standing pose + star: star, star pose, star statue pose, statue star pose + # -- Boolean -- boolean: true: @@ -3133,6 +3152,8 @@ types: frogvariant: frog variant¦s @a itemcomponent: item component¦s @an equippablecomponent: equippable component¦s @an + weatheringcopperstate: weathering copper state¦s @a + coppergolempose: copper golem pose¦s @a # Skript weathertype: weather type¦s @a diff --git a/src/test/skript/tests/misc/EntityData.sk b/src/test/skript/tests/misc/EntityData.sk index b1e02b2826e..39adec02f3e 100644 --- a/src/test/skript/tests/misc/EntityData.sk +++ b/src/test/skript/tests/misc/EntityData.sk @@ -233,6 +233,50 @@ test "chicken data": testData(temperate chick) testData(cold chick) +test "copper golem data" when running minecraft "1.21.9": + setSuperTypes(copper golem) + testData(copper golem) + testData(waxed copper golem) + testData(unwaxed copper golem) + testData(unaffected copper golem) + testData(exposed copper golem) + testData(weathered copper golem) + testData(oxidized copper golem) + + setSuperTypes(waxed copper golem) + testData(waxed copper golem) + testData(waxed unaffected copper golem) + testData(waxed exposed copper golem) + testData(waxed weathered copper golem) + testData(waxed oxidized copper golem) + + setSuperTypes(unwaxed copper golem) + testData(unwaxed copper golem) + testData(unwaxed unaffected copper golem) + testData(unwaxed exposed copper golem) + testData(unwaxed weathered copper golem) + testData(unwaxed oxidized copper golem) + + setSuperTypes(unaffected copper golem) + testData(unaffected copper golem) + testData(waxed unaffected copper golem) + testData(unwaxed unaffected copper golem) + + setSuperTypes(exposed copper golem) + testData(exposed copper golem) + testData(waxed exposed copper golem) + testData(unwaxed exposed copper golem) + + setSuperTypes(weathered copper golem) + testData(weathered copper golem) + testData(waxed weathered copper golem) + testData(unwaxed weathered copper golem) + + setSuperTypes(oxidized copper golem) + testData(oxidized copper golem) + testData(waxed oxidized copper golem) + testData(unwaxed oxidized copper golem) + test "cow data": setSuperTypes(cow) testData(cow) diff --git a/src/test/skript/tests/syntaxes/effects/EffWax.sk b/src/test/skript/tests/syntaxes/effects/EffWax.sk new file mode 100644 index 00000000000..a84a461c8a5 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffWax.sk @@ -0,0 +1,26 @@ +test "wax copper golem" when running minecraft "1.21.9": + spawn a copper golem at test-location: + set {_entity} to entity + + assert {_entity} is not waxed with "Copper golem should not be waxed after spawn" + wax {_entity} + assert {_entity} is waxed with "Copper golem should be waxed" + unwax {_entity} + assert {_entity} is not waxed with "Copper golem should be unwaxed" + + clear entity within {_entity} + +test "wax copper blocks": + set {_old} to the block data of test-block + + set {_waxTag} to paper tag "waxed_copper_blocks" + set {_unwaxTag} to paper tag "unwaxed_copper_blocks" + set {_blocks::*} to the tag contents of {_waxTag} + loop {_blocks::*}: + set test-block to loop-value + unwax test-block + assert test-block is tagged with {_unwaxTag} with "%loop-value% should be unwaxed, but was %type of test-block%" + wax test-block + assert test-block is tagged with {_waxTag} with "%loop-value% should be waxed, but was %type of test-block%" + + set the block data of test-block to {_old} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprCopperGolemOxidationTime.sk b/src/test/skript/tests/syntaxes/expressions/ExprCopperGolemOxidationTime.sk new file mode 100644 index 00000000000..0a5d225f9a1 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprCopperGolemOxidationTime.sk @@ -0,0 +1,10 @@ +test "copper golem oxidation" when running minecraft "1.21.9": + spawn a copper golem at test-location: + set {_entity} to entity + + set the time until oxidation of {_entity} to 10 seconds + assert the time until oxidation of {_entity} is 10 seconds with "Copper golem oxidation time did not change" + reset the time until oxidation of {_entity} + assert the time until oxidation of {_entity} is not set with "Copper golem oxidation reset takes a tick to be scheduled again" + + clear the entity within {_entity} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprCopperGolemPose.sk b/src/test/skript/tests/syntaxes/expressions/ExprCopperGolemPose.sk new file mode 100644 index 00000000000..3b3377d2e14 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprCopperGolemPose.sk @@ -0,0 +1,9 @@ +test "copper golem pose" when running minecraft "1.21.9": + set {_old} to the block data of test-block + + set test-block to a copper golem statue + loop all copper golem poses: + set the copper golem pose of test-block to loop-value + assert the copper golem pose of test-block is loop-value with "Copper golem pose should be %loop-value%" + + set the block data of test-block to {_old} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprCopperState.sk b/src/test/skript/tests/syntaxes/expressions/ExprCopperState.sk new file mode 100644 index 00000000000..4014c33c891 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprCopperState.sk @@ -0,0 +1,39 @@ +test "copper state of copper golem" when running minecraft "1.21.9": + spawn a copper golem at test-location: + set {_entity} to entity + + loop all weathering copper states: + set the copper state of {_entity} to loop-value + assert the copper state of {_entity} is loop-value with "Copper state of copper golem did not change" + + clear entity within {_entity} + +test "copper state of copper blocks": + set {_old} to the block data of test-block + + set {_exposedTag} to paper tag "exposed_copper_blocks" + set {_weatheredTag} to paper tag "weathered_copper_blocks" + set {_oxidizedTag} to paper tag "oxidized_copper_blocks" + set {_blocks::*} to the tag contents of paper tag "copper_blocks" + filter {_blocks::*} to match: + input is not tagged with {_exposedTag} + input is not tagged with {_weatheredTag} + input is not tagged with {_oxidizedTag} + + parse if running minecraft "1.21.9": + filter {_blocks::*} to match: + input is not a copper torch + input is not a copper wall torch + + loop {_blocks::*}: + set test-block to loop-value + + assert the copper state of test-block is normal copper state with "Copper block should be normal copper state, but was %type of test-block%" + set the copper state of test-block to exposed copper state + assert the copper state of test-block is exposed copper state with "Copper block should be exposed copper state, but was %type of test-block%" + set the copper state of test-block to weathered copper state + assert the copper state of test-block is weathered copper state with "Copper block should be weathered copper state, but was %type of test-block%" + set the copper state of test-block to oxidized copper state + assert the copper state of test-block is oxidized copper state with "Copper block should be oxidized copper state, but was %type of test-block%" + + set the block data of test-block to {_old} diff --git a/src/test/skript/tests/syntaxes/sections/EffSecSpawn.sk b/src/test/skript/tests/syntaxes/sections/EffSecSpawn.sk index 265bf06bed7..a242afc1d89 100644 --- a/src/test/skript/tests/syntaxes/sections/EffSecSpawn.sk +++ b/src/test/skript/tests/syntaxes/sections/EffSecSpawn.sk @@ -268,3 +268,47 @@ test "spawn baby cow by variant" when running minecraft "1.21.5": assert size of all calves is 5 with "Only temperate calves should have been deleted" delete all warm calves assert size of all warm calves is 0 with "Warm calves were not deleted" + +test "spawn copper golems" when running minecraft "1.21.9": + clear all copper golems + spawn 2 copper golems at test-location + spawn 2 waxed copper golems at test-location + spawn 2 unwaxed copper golems at test-location + + spawn 2 unaffected copper golems at test-location + spawn 2 waxed unaffected copper golems at test-location + spawn 2 unwaxed unaffected copper golems at test-location + + spawn 2 exposed copper golems at test-location + spawn 2 waxed exposed copper golems at test-location + spawn 2 unwaxed exposed copper golems at test-location + + spawn 2 weathered copper golems at test-location + spawn 2 waxed weathered copper golems at test-location + spawn 2 unwaxed weathered copper golems at test-location + + spawn 2 oxidized copper golems at test-location + spawn 2 waxed oxidized copper golems at test-location + spawn 2 unwaxed oxidized copper golems at test-location + + assert size of all copper golems is 30 with "Size of all copper golems is not 30" + assert size of all waxed copper golems is 10 with "Size of all waxed copper golems is not 10" + assert size of all unwaxed copper golems is 20 with "Size of all unwaxed copper golems is not 20" + + assert size of all unaffected copper golems is 12 with "Size of all unaffected copper golems is not 12" + assert size of all waxed unaffected copper golems is 4 with "Size of all waxed unaffected copper golems is not 4" + assert size of all unwaxed unaffected copper golems is 8 with "Size of all unwaxed unaffected copper golems is not 8" + + assert size of all exposed copper golems is 6 with "Size of all exposed copper golems is not 6" + assert size of all waxed exposed copper golems is 2 with "Size of all waxed exposed copper golems is not 2" + assert size of all unwaxed exposed copper golems is 4 with "Size of all unwaxed exposed copper golems is not 4" + + assert size of all weathered copper golems is 6 with "Size of all weathered copper golems is not 6" + assert size of all waxed weathered copper golems is 2 with "Size of all waxed weathered copper golems is not 2" + assert size of all unwaxed weathered copper golems is 4 with "Size of all unwaxed weathered copper golems is not 4" + + assert size of all oxidized copper golems is 6 with "Size of all oxidized copper golems is not 6" + assert size of all waxed oxidized copper golems is 2 with "Size of all waxed oxidized copper golems is not 2" + assert size of all unwaxed oxidized copper golems is 4 with "Size of all unwaxed oxidized copper golems is not 4" + + clear all copper golems From d4a8dc05e012a049780aa56501b488521c4f2d7d Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Thu, 23 Oct 2025 16:38:07 -0400 Subject: [PATCH 2/5] Requested Changes --- src/main/java/ch/njol/skript/Skript.java | 278 +++++++++++++----- .../skript/classes/data/BukkitClasses.java | 5 +- .../skript/bukkit/BukkitModule.java | 49 +++ .../elements}/conditions/CondIsWaxed.java | 15 +- .../bukkit/elements}/effects/EffWax.java | 25 +- .../ExprCopperGolemOxidationTime.java | 50 ++-- .../expressions/ExprCopperGolemPose.java | 19 +- .../expressions/ExprCopperState.java | 31 +- .../skript/bukkit}/paperutil/CopperState.java | 21 +- .../skriptlang/skript/util/ReflectUtils.java | 105 +++++-- 10 files changed, 432 insertions(+), 166 deletions(-) create mode 100644 src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/elements}/conditions/CondIsWaxed.java (74%) rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/elements}/effects/EffWax.java (86%) rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/elements}/expressions/ExprCopperGolemOxidationTime.java (62%) rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/elements}/expressions/ExprCopperGolemPose.java (81%) rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/elements}/expressions/ExprCopperState.java (91%) rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit}/paperutil/CopperState.java (57%) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index e38c523eb62..5c1a315572b 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -90,15 +90,13 @@ import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.notification.Failure; +import org.skriptlang.skript.bukkit.BukkitModule; import org.skriptlang.skript.bukkit.SkriptMetrics; import org.skriptlang.skript.bukkit.breeding.BreedingModule; -import org.skriptlang.skript.bukkit.brewing.BrewingModule; -import org.skriptlang.skript.bukkit.damagesource.DamageSourceModule; import org.skriptlang.skript.bukkit.displays.DisplayModule; import org.skriptlang.skript.bukkit.fishing.FishingModule; import org.skriptlang.skript.bukkit.furnace.FurnaceModule; import org.skriptlang.skript.bukkit.input.InputModule; -import org.skriptlang.skript.bukkit.itemcomponents.ItemComponentModule; import org.skriptlang.skript.bukkit.log.runtime.BukkitRuntimeErrorConsumer; import org.skriptlang.skript.bukkit.loottables.LootTableModule; import org.skriptlang.skript.bukkit.registration.BukkitRegistryKeys; @@ -122,11 +120,13 @@ import org.skriptlang.skript.registration.SyntaxOrigin; import org.skriptlang.skript.registration.SyntaxRegistry; import org.skriptlang.skript.util.ClassLoader; +import org.skriptlang.skript.util.ReflectUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.Thread.UncaughtExceptionHandler; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -187,6 +187,7 @@ public final class Skript extends JavaPlugin implements Listener { private static org.skriptlang.skript.@UnknownNullability Skript skript = null; private static org.skriptlang.skript.@UnknownNullability Skript unmodifiableSkript = null; + private static final ReflectUtils REFLECT_UTILS = ReflectUtils.getInstance(); private static boolean disabled = false; private static boolean partDisabled = false; @@ -596,10 +597,8 @@ public void onEnable() { FurnaceModule.load(); LootTableModule.load(); skript.loadModules( - new DamageSourceModule(), - new ItemComponentModule(), - new BrewingModule(), - new CommonModule() + new CommonModule(), + new BukkitModule() ); } catch (final Exception e) { exception(e, "Could not load required .class files: " + e.getLocalizedMessage()); @@ -1125,80 +1124,6 @@ public static boolean isRunningMinecraft(final Version v) { return minecraftVersion.compareTo(v) >= 0; } - /** - * Tests whether a given class exists in the classpath. - * - * @param className The {@link Class#getCanonicalName() canonical name} of the class - * @return Whether the given class exists. - */ - public static boolean classExists(final String className) { - try { - Class.forName(className); - return true; - } catch (final ClassNotFoundException e) { - return false; - } - } - - /** - * Tests whether a method exists in the given class. - * - * @param c The class - * @param methodName The name of the method - * @param parameterTypes The parameter types of the method - * @return Whether the given method exists. - */ - public static boolean methodExists(final Class c, final String methodName, final Class... parameterTypes) { - try { - c.getDeclaredMethod(methodName, parameterTypes); - return true; - } catch (final NoSuchMethodException e) { - return false; - } catch (final SecurityException e) { - return false; - } - } - - /** - * Tests whether a method exists in the given class, and whether the return type matches the expected one. - *

- * Note that this method doesn't work properly if multiple methods with the same name and parameters exist but have different return types. - * - * @param c The class - * @param methodName The name of the method - * @param parameterTypes The parameter types of the method - * @param returnType The expected return type - * @return Whether the given method exists. - */ - public static boolean methodExists(final Class c, final String methodName, final Class[] parameterTypes, final Class returnType) { - try { - final Method m = c.getDeclaredMethod(methodName, parameterTypes); - return m.getReturnType() == returnType; - } catch (final NoSuchMethodException e) { - return false; - } catch (final SecurityException e) { - return false; - } - } - - /** - * Tests whether a field exists in the given class. - * - * @param c The class - * @param fieldName The name of the field - * @return Whether the given field exists. - */ - public static boolean fieldExists(final Class c, final String fieldName) { - try { - c.getDeclaredField(fieldName); - return true; - } catch (final NoSuchFieldException e) { - return false; - } catch (final SecurityException e) { - return false; - } - } - @Nullable static Metrics metrics; @@ -2138,4 +2063,195 @@ public SkriptUpdater getUpdater() { return updater; } + // + /** + * Tests whether a given class exists in the classpath. + * + * @param className The {@link Class#getCanonicalName() canonical name} of the class + * @return Whether the given class exists. + */ + public static boolean classExists(String className) { + return REFLECT_UTILS.classExists(className); + } + + /** + * @param className The full package and class name. + * @return The resulting {@link Class} if found, otherwise {@code null}. + */ + public static @Nullable Class getClass(String className) { + return REFLECT_UTILS.getClass(className); + } + + /** + * @param className The full package and class name. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @return Whether the method exists. + */ + public static boolean methodExists(String className, String methodName, Class @Nullable ... params) { + return REFLECT_UTILS.methodExists(className, methodName, params); + } + + /** + * @param className The full package and class name. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @param returnType The return type of the desired method. + * @return Whether the method exists. + */ + public static boolean methodExists(String className, String methodName, Class @Nullable [] params, @Nullable Class returnType) { + return REFLECT_UTILS.methodExists(className, methodName, params, returnType); + } + + /** + * Tests whether a method exists in the given class. + * + * @param c The class + * @param methodName The name of the method + * @param parameterTypes The parameter types of the method + * @return Whether the given method exists. + */ + public static boolean methodExists(Class c, String methodName, Class @Nullable ... parameterTypes) { + return REFLECT_UTILS.methodExists(c, methodName, parameterTypes); + } + + /** + * Tests whether a method exists in the given class, and whether the return type matches the expected one. + *

+ * Note that this method doesn't work properly if multiple methods with the same name and parameters exist but have different return types. + * + * @param c The class + * @param methodName The name of the method + * @param parameterTypes The parameter types of the method + * @param returnType The expected return type + * @return Whether the given method exists. + */ + public static boolean methodExists(Class c, String methodName, Class @Nullable [] parameterTypes, Class returnType) { + return REFLECT_UTILS.methodExists(c, methodName, parameterTypes, returnType); + } + + /** + * @param className The full package and class name. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @return The resulting {@link Method} if it exists, otherwise {@code null}. + */ + public static @Nullable Method getMethod(String className, String methodName, Class @Nullable ... params) { + return REFLECT_UTILS.getMethod(className, methodName, params); + } + + /** + * @param className The full package and class name. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @param returnType The return type of the desired method. + * @return The resulting {@link Method} if it exists, otherwise {@code null}. + */ + public static @Nullable Method getMethod(String className, String methodName, Class @Nullable [] params, @Nullable Class returnType) { + return REFLECT_UTILS.getMethod(className, methodName, params, returnType); + } + + /** + * @param c The {@link Class} to get the method from. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @return The resulting {@link Method} if it exists, otherwise {@code null}. + */ + public static @Nullable Method getMethod(Class c, String methodName, Class @Nullable ... params) { + return REFLECT_UTILS.getMethod(c, methodName, params); + } + + /** + * @param c The {@link Class} to get the method from. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @param returnType The return type of the desired method. + * @return The resulting {@link Method} if it exists, otherwise {@code null}. + */ + public static @Nullable Method getMethod(Class c, String methodName, Class @Nullable [] params, @Nullable Class returnType) { + return REFLECT_UTILS.getMethod(c, methodName, params, returnType); + } + + /** + * @param className The full package and class name. + * @param fieldName The name of the field. + * @return Whether the field exists. + */ + public static boolean fieldExists(String className, String fieldName) { + return REFLECT_UTILS.fieldExists(className, fieldName); + } + + /** + * Tests whether a field exists in the given class. + * + * @param c The class + * @param fieldName The name of the field + * @return Whether the given field exists. + */ + public static boolean fieldExists(Class c, String fieldName) { + return REFLECT_UTILS.fieldExists(c, fieldName); + } + + /** + * @param className The full package and class name. + * @param fieldName The name of the field. + * @return The resulting {@link Field} if it exists, otherwise {@code null}. + */ + public static @Nullable Field getField(String className, String fieldName) { + return REFLECT_UTILS.getField(className, fieldName); + } + + /** + * @param c The {@link Class} to get the field from. + * @param fieldName The name of the field. + * @return The resulting {@link Field} if it exists, otherwise {@code null}. + */ + public static @Nullable Field getField(Class c, String fieldName) { + return REFLECT_UTILS.getField(c, fieldName); + } + + /** + * Invoke a static {@link Method}. + * @param method The {@link Method} to invoke. + * @return The result of the invocation if successful, otherwise {@code null}. + * @param The expected return type from the invocation. + */ + public static @Nullable Type methodInvoke(Method method) { + return REFLECT_UTILS.methodInvoke(method); + } + + /** + * Invoke a {@link Method}. + * @param method The {@link Method} to invoke. + * @param holder The holder object to invoke for. + * @param params The parameters to pass into the invocation. + * @return The result of the invocation if successful, otherwise {@code null}. + * @param The expected return type from the invocation. + */ + public static @Nullable Type methodInvoke(Method method, @Nullable Object holder, Object @Nullable ... params) { + return REFLECT_UTILS.methodInvoke(method, holder, params); + } + + /** + * Gets the values of a static {@link Field}. + * @param field The {@link Field} to get from. + * @return The value of the {@link Field}. + * @param The expected return type. + */ + public static @Nullable Type fieldGet(Field field) { + return REFLECT_UTILS.fieldGet(field); + } + + /** + * Gets the values of a {@link Field}. + * @param field The {@link Field} to get from. + * @param holder The holder object to get the field for. + * @return The value of the {@link Field}. + * @param The expected return type. + */ + public static @Nullable Type fieldGet(Field field, @Nullable Object holder) { + return REFLECT_UTILS.fieldGet(field, holder); + } + // + } 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 09f03904541..a78702ecfec 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -14,7 +14,7 @@ import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.lang.util.SimpleLiteral; -import ch.njol.skript.paperutil.CopperState; +import org.skriptlang.skript.bukkit.paperutil.CopperState; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.BlockUtils; import ch.njol.skript.util.PotionEffectUtils; @@ -1162,13 +1162,12 @@ public String toVariableNameString(WorldBorder border) { .user("(weathering ?)?copper ?states?") .name("Weathering Copper State") .description("The weathering state of a copper golem or copper block.") - .requiredPlugins("Minecraft 1.21.9+") .since("INSERT VERSION") ); if (Skript.classExists("org.bukkit.block.data.type.CopperGolemStatue$Pose")) { Classes.registerClass(new EnumClassInfo<>(CopperGolemStatue.Pose.class, "coppergolempose", "copper golem poses") - .user("copper ? golem ?(statue ?)?poses?") + .user("copper ?golem ?(statue ?)?poses?") .name("Copper Golem Pose") .description("The pose of a copper golem statue.") .requiredPlugins("Minecraft 1.21.9+") diff --git a/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java new file mode 100644 index 00000000000..86add3965e9 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java @@ -0,0 +1,49 @@ +package org.skriptlang.skript.bukkit; + +import ch.njol.skript.Skript; +import org.skriptlang.skript.addon.AddonModule; +import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.bukkit.brewing.BrewingModule; +import org.skriptlang.skript.bukkit.damagesource.DamageSourceModule; +import org.skriptlang.skript.bukkit.elements.conditions.CondIsWaxed; +import org.skriptlang.skript.bukkit.elements.effects.EffWax; +import org.skriptlang.skript.bukkit.elements.expressions.ExprCopperGolemOxidationTime; +import org.skriptlang.skript.bukkit.elements.expressions.ExprCopperGolemPose; +import org.skriptlang.skript.bukkit.elements.expressions.ExprCopperState; +import org.skriptlang.skript.bukkit.itemcomponents.ItemComponentModule; +import org.skriptlang.skript.registration.SyntaxRegistry; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; + +public class BukkitModule implements AddonModule { + + @Override + public void load(SkriptAddon addon) { + addon.loadModules( + new DamageSourceModule(), + new ItemComponentModule(), + new BrewingModule() + ); + + Set> consumers = new HashSet<>(); + + consumers.addAll(Set.of( + CondIsWaxed::register, + + EffWax::register, + + ExprCopperState::register + )); + + if (Skript.classExists("org.bukkit.entity.CopperGolem")) + consumers.add(ExprCopperGolemOxidationTime::register); + if (Skript.classExists("org.bukkit.block.data.type.CopperGolemStatue")) + consumers.add(ExprCopperGolemPose::register); + + SyntaxRegistry registry = addon.syntaxRegistry(); + consumers.forEach(consumer -> consumer.accept(registry)); + } + +} diff --git a/src/main/java/ch/njol/skript/conditions/CondIsWaxed.java b/src/main/java/org/skriptlang/skript/bukkit/elements/conditions/CondIsWaxed.java similarity index 74% rename from src/main/java/ch/njol/skript/conditions/CondIsWaxed.java rename to src/main/java/org/skriptlang/skript/bukkit/elements/conditions/CondIsWaxed.java index db69003f20b..2cec6d85289 100644 --- a/src/main/java/ch/njol/skript/conditions/CondIsWaxed.java +++ b/src/main/java/org/skriptlang/skript/bukkit/elements/conditions/CondIsWaxed.java @@ -1,4 +1,4 @@ -package ch.njol.skript.conditions; +package org.skriptlang.skript.bukkit.elements.conditions; import ch.njol.skript.Skript; import ch.njol.skript.conditions.base.PropertyCondition; @@ -11,7 +11,7 @@ import org.bukkit.block.Block; import org.bukkit.entity.CopperGolem; import org.bukkit.entity.CopperGolem.Oxidizing.Waxed; -import org.skriptlang.skript.util.ReflectUtils; +import org.skriptlang.skript.registration.SyntaxRegistry; @Name("Is Waxed") @Description("Whether a copper golem or copper block is waxed.") @@ -27,13 +27,18 @@ @Since("INSERT VERSION") public class CondIsWaxed extends PropertyCondition { - private static final boolean COPPER_GOLEM_EXISTS = ReflectUtils.classExists("org.bukkit.entity.CopperGolem"); + private static final boolean COPPER_GOLEM_EXISTS = Skript.classExists("org.bukkit.entity.CopperGolem"); - static { + public static void register(SyntaxRegistry registry) { String type = "blocks"; if (Skript.classExists("org.bukkit.entity.CopperGolem")) type = "entities/blocks"; - register(CondIsWaxed.class, "waxed", type); + registry.register( + SyntaxRegistry.CONDITION, + infoBuilder(CondIsWaxed.class, PropertyType.BE, "waxed", type) + .supplier(CondIsWaxed::new) + .build() + ); } @Override diff --git a/src/main/java/ch/njol/skript/effects/EffWax.java b/src/main/java/org/skriptlang/skript/bukkit/elements/effects/EffWax.java similarity index 86% rename from src/main/java/ch/njol/skript/effects/EffWax.java rename to src/main/java/org/skriptlang/skript/bukkit/elements/effects/EffWax.java index c2faa3a2d34..d9640769745 100644 --- a/src/main/java/ch/njol/skript/effects/EffWax.java +++ b/src/main/java/org/skriptlang/skript/bukkit/elements/effects/EffWax.java @@ -1,4 +1,4 @@ -package ch.njol.skript.effects; +package org.skriptlang.skript.bukkit.elements.effects; import ch.njol.skript.Skript; import ch.njol.skript.doc.Description; @@ -21,6 +21,8 @@ import org.bukkit.entity.CopperGolem.Oxidizing.Waxed; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; import org.skriptlang.skript.util.ReflectUtils; @Name("Wax") @@ -43,14 +45,9 @@ public class EffWax extends Effect { private static final BiMap WAX_CONVERSION; private static final BiMap UNWAX_CONVERSION = HashBiMap.create(); - private static final boolean COPPER_GOLEM_EXISTS = ReflectUtils.classExists("org.bukkit.entity.CopperGolem"); + private static final boolean COPPER_GOLEM_EXISTS = Skript.classExists("org.bukkit.entity.CopperGolem"); static { - String type = "%blocks%"; - if (COPPER_GOLEM_EXISTS) - type = "%entities/blocks%"; - Skript.registerEffect(EffWax.class, "[:un]wax " + type); - for (Material waxed : MaterialTags.WAXED_COPPER_BLOCKS.getValues()) { Material unwaxed = Material.valueOf(waxed.name().replaceAll("WAXED_", "")); UNWAX_CONVERSION.put(waxed, unwaxed); @@ -58,6 +55,20 @@ public class EffWax extends Effect { WAX_CONVERSION = UNWAX_CONVERSION.inverse(); } + public static void register(SyntaxRegistry registry) { + String type = "%blocks%"; + if (COPPER_GOLEM_EXISTS) + type = "%entities/blocks%"; + + registry.register( + SyntaxRegistry.EFFECT, + SyntaxInfo.builder(EffWax.class) + .addPatterns("[:un]wax " + type) + .supplier(EffWax::new) + .build() + ); + } + private boolean wax; private Expression objects; diff --git a/src/main/java/ch/njol/skript/expressions/ExprCopperGolemOxidationTime.java b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemOxidationTime.java similarity index 62% rename from src/main/java/ch/njol/skript/expressions/ExprCopperGolemOxidationTime.java rename to src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemOxidationTime.java index 0658ff329a9..2f1b4b9065a 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprCopperGolemOxidationTime.java +++ b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemOxidationTime.java @@ -1,4 +1,4 @@ -package ch.njol.skript.expressions; +package org.skriptlang.skript.bukkit.elements.expressions; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; @@ -11,19 +11,19 @@ import ch.njol.skript.util.Timespan.TimePeriod; import ch.njol.util.coll.CollectionUtils; import org.bukkit.entity.CopperGolem; +import org.bukkit.entity.CopperGolem.Oxidizing; +import org.bukkit.entity.CopperGolem.Oxidizing.AtTime; import org.bukkit.entity.Entity; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.util.ReflectUtils; - -import java.lang.reflect.Method; +import org.skriptlang.skript.registration.SyntaxRegistry; @Name("Time Until Oxidation") @Description(""" - The time until a copper golem goes through oxidation til reaching the max oxidation phase. (Normal -> Exposed -> Weathered -> Oxidized) + The time until a copper golem oxidizes to its next state. (Normal -> Exposed -> Weathered -> Oxidized) Copper golems that are waxed do not go through oxidation. Setting or resetting the time until oxidation on a waxed copper golem will remove the waxed state. - Resetting the time until oxidation uses vanilla behavior of generating a random time. + Resetting the time until oxidation uses vanilla behavior of generating a random time between 7 hours and 7 hours 40 minutes. """) @Example("set {_time} to the time until oxidation of last spawned copper golem") @Example("set the time until oxidation of last spawned copper golem to 10 seconds") @@ -32,33 +32,27 @@ @Since("INSERT VERSION") public class ExprCopperGolemOxidationTime extends SimplePropertyExpression { - private static Method getOxidizingMethod; - private static Class atTimeClass; - private static Method getTimeMethod; - private static Method newTimeMethod; - private static Method unsetMethod; - - static { - if (ReflectUtils.classExists("org.bukkit.entity.CopperGolem")) { - register(ExprCopperGolemOxidationTime.class, Timespan.class, "time until oxidation", "entities"); - - getOxidizingMethod = ReflectUtils.getMethod("org.bukkit.entity.CopperGolem", "getOxidizing"); - atTimeClass = ReflectUtils.getClass("org.bukkit.entity.CopperGolem$Oxidizing$AtTime"); - getTimeMethod = ReflectUtils.getMethod(atTimeClass, "time"); - newTimeMethod = ReflectUtils.getMethod("org.bukkit.entity.CopperGolem$Oxidizing", "atTime", long.class); - unsetMethod = ReflectUtils.getMethod("org.bukkit.entity.CopperGolem$Oxidizing", "unset"); - } + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprCopperGolemOxidationTime.class, + Timespan.class, + "time until oxidation", + "entities", + false + ).supplier(ExprCopperGolemOxidationTime::new) + .build() + ); } @Override public @Nullable Timespan convert(Entity entity) { if (entity instanceof CopperGolem golem) { - Object oxidizing = ReflectUtils.methodInvoke(getOxidizingMethod, golem); - if (!(atTimeClass.isInstance(oxidizing))) + if (!(golem.getOxidizing() instanceof AtTime atTime)) return null; long worldTime = golem.getWorld().getGameTime(); - //noinspection DataFlowIssue - long oxidationTime = ReflectUtils.methodInvoke(getTimeMethod, oxidizing); + long oxidationTime = atTime.time(); if (worldTime > oxidationTime) return null; return new Timespan(TimePeriod.TICK, oxidationTime - worldTime); @@ -82,13 +76,13 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { if (!(entity instanceof CopperGolem golem)) continue; long worldTime = golem.getWorld().getGameTime(); - golem.setOxidizing(ReflectUtils.methodInvoke(newTimeMethod, null, worldTime + ticks)); + golem.setOxidizing(Oxidizing.atTime(worldTime + ticks)); } } else if (mode == ChangeMode.RESET) { for (Entity entity : getExpr().getArray(event)) { if (!(entity instanceof CopperGolem golem)) continue; - golem.setOxidizing(ReflectUtils.methodInvoke(unsetMethod)); + golem.setOxidizing(Oxidizing.unset()); } } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprCopperGolemPose.java b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemPose.java similarity index 81% rename from src/main/java/ch/njol/skript/expressions/ExprCopperGolemPose.java rename to src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemPose.java index c65c68c8920..0cb02a25632 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprCopperGolemPose.java +++ b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemPose.java @@ -1,6 +1,5 @@ -package ch.njol.skript.expressions; +package org.skriptlang.skript.bukkit.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; @@ -14,6 +13,7 @@ import org.bukkit.block.data.type.CopperGolemStatue.Pose; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxRegistry; @Name("Copper Golem Statue Pose") @Description("The pose of a copper golem statue.") @@ -23,9 +23,18 @@ @Since("INSERT VERSION") public class ExprCopperGolemPose extends SimplePropertyExpression { - static { - if (Skript.classExists("org.bukkit.block.data.type.CopperGolemStatue")) - register(ExprCopperGolemPose.class, Pose.class, "copper golem [statue] pose[s]", "blocks"); + public static void register(SyntaxRegistry registry) { + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprCopperGolemPose.class, + Pose.class, + "copper golem [statue] pose[s]", + "blocks", + false + ).supplier(ExprCopperGolemPose::new) + .build() + ); } @Override diff --git a/src/main/java/ch/njol/skript/expressions/ExprCopperState.java b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperState.java similarity index 91% rename from src/main/java/ch/njol/skript/expressions/ExprCopperState.java rename to src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperState.java index 629fb7552f9..9e83519992d 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprCopperState.java +++ b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperState.java @@ -1,4 +1,4 @@ -package ch.njol.skript.expressions; +package org.skriptlang.skript.bukkit.elements.expressions; import ch.njol.skript.Skript; import ch.njol.skript.classes.Changer.ChangeMode; @@ -8,7 +8,7 @@ import ch.njol.skript.doc.RequiredPlugins; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import ch.njol.skript.paperutil.CopperState; +import org.skriptlang.skript.bukkit.paperutil.CopperState; import ch.njol.util.coll.CollectionUtils; import com.destroystokyo.paper.MaterialTags; import io.papermc.paper.world.WeatheringCopperState; @@ -17,6 +17,7 @@ import org.bukkit.entity.CopperGolem; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxRegistry; import org.skriptlang.skript.util.ReflectUtils; import java.util.ArrayList; @@ -44,15 +45,9 @@ public class ExprCopperState extends SimplePropertyExpression { private static final CopperStateMaterialMap STATE_MATERIALS = new CopperStateMaterialMap(); private static final List STATE_REPLACEMENTS = new ArrayList<>(); - private static final boolean COPPER_GOLEM_EXISTS = ReflectUtils.classExists("org.bukkit.entity.CopperGolem"); + private static final boolean COPPER_GOLEM_EXISTS = Skript.classExists("org.bukkit.entity.CopperGolem"); static { - String type = "blocks"; - if (Skript.classExists("org.bukkit.entity.CopperGolem")) - type = "entities/blocks"; - - register(ExprCopperState.class, Object.class, "[weathering] copper state[s]", type); - for (Enum state : CopperState.getValues()) { if (state.name().equals("UNAFFECTED")) continue; @@ -67,6 +62,24 @@ public class ExprCopperState extends SimplePropertyExpression { } } + public static void register(SyntaxRegistry registry) { + String type = "blocks"; + if (Skript.classExists("org.bukkit.entity.CopperGolem")) + type = "entities/blocks"; + + registry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder( + ExprCopperState.class, + Object.class, + "[weathering] copper state[s]", + type, + false + ).supplier(ExprCopperState::new) + .build() + ); + } + /** * Gets the string of the block type {@code material} relates to. * i.e. stair, slab, door, trapdoor, etc. diff --git a/src/main/java/ch/njol/skript/paperutil/CopperState.java b/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java similarity index 57% rename from src/main/java/ch/njol/skript/paperutil/CopperState.java rename to src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java index 878480ce564..70d21c8d2d1 100644 --- a/src/main/java/ch/njol/skript/paperutil/CopperState.java +++ b/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java @@ -1,7 +1,7 @@ -package ch.njol.skript.paperutil; +package org.skriptlang.skript.bukkit.paperutil; +import ch.njol.skript.Skript; import org.jetbrains.annotations.ApiStatus.Internal; -import org.skriptlang.skript.util.ReflectUtils; import java.lang.reflect.Method; @@ -18,11 +18,11 @@ public enum CopperState { private static final Method WEATHERING_VALUE_METHOD; static { - WEATHERING_CLASS = ReflectUtils.getClass("io.papermc.paper.world.WeatheringCopperState"); + WEATHERING_CLASS = Skript.getClass("io.papermc.paper.world.WeatheringCopperState"); if (WEATHERING_CLASS != null) { - Method valuesMethod = ReflectUtils.getMethod(WEATHERING_CLASS, "values"); - WEATHERING_VALUES = ReflectUtils.methodInvoke(valuesMethod); - WEATHERING_VALUE_METHOD = ReflectUtils.getMethod(WEATHERING_CLASS, "valueOf", String.class); + Method valuesMethod = Skript.getMethod(WEATHERING_CLASS, "values"); + WEATHERING_VALUES = Skript.methodInvoke(valuesMethod); + WEATHERING_VALUE_METHOD = Skript.getMethod(WEATHERING_CLASS, "valueOf", String.class); } else { WEATHERING_VALUES = null; WEATHERING_VALUE_METHOD = null; @@ -36,15 +36,22 @@ public static Class getStateClass() { return WEATHERING_CLASS != null ? WEATHERING_CLASS : CopperState.class; } + /** + * @return The enum values for {@link CopperState} or 'WeatheringCopperState' if it exists. + */ public static Enum[] getValues() { if (WEATHERING_CLASS != null) return WEATHERING_VALUES; return CopperState.values(); } + /** + * @param state The {@link CopperState} enum value. + * @return {@code state} or the state in 'WeatheringCopperState' if it exists. + */ public static Enum get(CopperState state) { if (WEATHERING_CLASS != null) - return ReflectUtils.methodInvoke(WEATHERING_VALUE_METHOD, null, state.name()); + return Skript.methodInvoke(WEATHERING_VALUE_METHOD, null, state.name()); return state; } diff --git a/src/main/java/org/skriptlang/skript/util/ReflectUtils.java b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java index 2d35d4cfb70..323939afdf1 100644 --- a/src/main/java/org/skriptlang/skript/util/ReflectUtils.java +++ b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java @@ -13,26 +13,36 @@ */ public class ReflectUtils { + private static ReflectUtils instance; + + private ReflectUtils() {} + + public static ReflectUtils getInstance() { + if (instance == null) + instance = new ReflectUtils(); + return instance; + } + /** * Cache for classes. */ - private static final Map> CLASSES = new HashMap<>(); + private final Map> CLASSES = new HashMap<>(); /** * Cache for methods of classes. */ - private static final MethodMap METHODS = new MethodMap(); + private final MethodMap METHODS = new MethodMap(); /** * Cache for fields of classes. */ - private static final FieldMap FIELDS = new FieldMap(); + private final FieldMap FIELDS = new FieldMap(); /** * @param className The full package and class name. * @return Whether the class exists. */ - public static boolean classExists(String className) { + public boolean classExists(String className) { return getClass(className) != null; } @@ -40,7 +50,7 @@ public static boolean classExists(String className) { * @param className The full package and class name. * @return The resulting {@link Class} if found, otherwise {@code null}. */ - public static @Nullable Class getClass(String className) { + public @Nullable Class getClass(String className) { if (CLASSES.containsKey(className)) return CLASSES.get(className); Class c = null; @@ -57,30 +67,63 @@ public static boolean classExists(String className) { * @param params The {@link Class}es used as parameters for the desired method. * @return Whether the method exists. */ - public static boolean methodExists(String className, String methodName, Class @Nullable ... params) { + public boolean methodExists(String className, String methodName, Class @Nullable ... params) { + return methodExists(className, methodName, params, null); + } + + /** + * @param className The full package and class name. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @param returnType The return type of the desired method. + * @return Whether the method exists. + */ + public boolean methodExists(String className, String methodName, Class @Nullable [] params, @Nullable Class returnType) { Class c = getClass(className); if (c == null) return false; - return methodExists(c, methodName, params); + return methodExists(c, methodName, params, returnType); + } + + /** + * @param c The {@link Class} to check the method for. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @return Whether the method exists. + */ + public boolean methodExists(Class c, String methodName, Class @Nullable ... params) { + return methodExists(c, methodName, params, null); } /** * @param c The {@link Class} to check the method for. * @param methodName The name of the method. * @param params The {@link Class}es used as parameters for the desired method. + * @param returnTpe The return type of the desired method. * @return Whether the method exists. */ - public static boolean methodExists(Class c, String methodName, Class @Nullable ... params) { - return getMethod(c, methodName, params) != null; + public boolean methodExists(Class c, String methodName, Class @Nullable [] params, @Nullable Class returnTpe) { + return getMethod(c, methodName, params, returnTpe) != null; + } + + /** + * @param className The full package and class name. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @return The resulting {@link Method} if it exists, otherwise {@code null}. + */ + public @Nullable Method getMethod(String className, String methodName, Class @Nullable ... params) { + return getMethod(className, methodName, params, null); } /** * @param className The full package and class name. * @param methodName The name of the method. * @param params The {@link Class}es used as parameters for the desired method. + * @param returnType The return type of the desired method. * @return The resulting {@link Method} if it exists, otherwise {@code null}. */ - public static @Nullable Method getMethod(String className, String methodName, Class @Nullable ... params) { + public @Nullable Method getMethod(String className, String methodName, Class @Nullable [] params, @Nullable Class returnType) { Class c = getClass(className); if (c == null) return null; @@ -93,16 +136,36 @@ public static boolean methodExists(Class c, String methodName, Class @Null * @param params The {@link Class}es used as parameters for the desired method. * @return The resulting {@link Method} if it exists, otherwise {@code null}. */ - public static @Nullable Method getMethod(Class c, String methodName, Class @Nullable ... params) { + public @Nullable Method getMethod(Class c, String methodName, Class @Nullable ... params) { + return getMethod(c, methodName, params, null); + } + + /** + * @param c The {@link Class} to get the method from. + * @param methodName The name of the method. + * @param params The {@link Class}es used as parameters for the desired method. + * @param returnType The return type of the desired method. + * @return The resulting {@link Method} if it exists, otherwise {@code null}. + */ + public @Nullable Method getMethod(Class c, String methodName, Class @Nullable [] params, @Nullable Class returnType) { MethodID methodID = new MethodID(c, methodName, params); - if (METHODS.contains(c, methodID)) + if (METHODS.contains(c, methodID)) { + Method method = METHODS.get(c, methodID); + if (method != null && returnType != null) { + Class methodType = method.getReturnType(); + if (!returnType.isAssignableFrom(methodType) && !methodType.isAssignableFrom(returnType)) + return null; + } return METHODS.get(c, methodID); + } Method method = null; try { method = c.getDeclaredMethod(methodName, params); } catch (NoSuchMethodException ignored) {} METHODS.put(c, methodID, method); + if (method != null && returnType != null && method.getReturnType() != returnType) + return null; return method; } @@ -111,7 +174,7 @@ public static boolean methodExists(Class c, String methodName, Class @Null * @param fieldName The name of the field. * @return Whether the field exists. */ - public static boolean fieldExists(String className, String fieldName) { + public boolean fieldExists(String className, String fieldName) { Class c = getClass(className); if (c == null) return false; @@ -123,7 +186,7 @@ public static boolean fieldExists(String className, String fieldName) { * @param fieldName The name of the field. * @return Whether the field exists. */ - public static boolean fieldExists(Class c, String fieldName) { + public boolean fieldExists(Class c, String fieldName) { return getField(c, fieldName) != null; } @@ -132,7 +195,7 @@ public static boolean fieldExists(Class c, String fieldName) { * @param fieldName The name of the field. * @return The resulting {@link Field} if it exists, otherwise {@code null}. */ - public static @Nullable Field getField(String className, String fieldName) { + public @Nullable Field getField(String className, String fieldName) { Class c = getClass(className); if (c == null) return null; @@ -144,7 +207,7 @@ public static boolean fieldExists(Class c, String fieldName) { * @param fieldName The name of the field. * @return The resulting {@link Field} if it exists, otherwise {@code null}. */ - public static @Nullable Field getField(Class c, String fieldName) { + public @Nullable Field getField(Class c, String fieldName) { if (FIELDS.contains(c, fieldName)) return FIELDS.get(c, fieldName); Field field = null; @@ -162,7 +225,7 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The result of the invocation if successful, otherwise {@code null}. * @param The expected return type from the invocation. */ - public static @Nullable Type methodInvoke(Method method) { + public @Nullable Type methodInvoke(Method method) { return methodInvoke(method, null); } @@ -174,7 +237,7 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The result of the invocation if successful, otherwise {@code null}. * @param The expected return type from the invocation. */ - public static @Nullable Type methodInvoke(Method method, @Nullable Object holder, Object @Nullable ... params) { + public @Nullable Type methodInvoke(Method method, @Nullable Object holder, Object @Nullable ... params) { method.setAccessible(true); try { //noinspection unchecked @@ -189,7 +252,7 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public static @Nullable Type fieldGet(Field field) { + public @Nullable Type fieldGet(Field field) { return fieldGet(field, null); } @@ -200,7 +263,7 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public static @Nullable Type fieldGet(Field field, @Nullable Object holder) { + public @Nullable Type fieldGet(Field field, @Nullable Object holder) { field.setAccessible(true); try { //noinspection unchecked @@ -215,7 +278,7 @@ public static boolean fieldExists(Class c, String fieldName) { * @param methodName The name of the method. * @param params The types of parameters for the method. */ - private record MethodID(Class c, String methodName, Class @Nullable ... params) {} + private record MethodID(Class c, String methodName, Class @Nullable [] params) {} /** * Custom map for correlating a {@link Class} to a {@link Method}. From eb1fd6da19120781f9cbc735a344635fffa62367 Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Thu, 23 Oct 2025 18:44:42 -0400 Subject: [PATCH 3/5] Additional Changes --- src/main/java/ch/njol/skript/Skript.java | 79 ++------------- .../skript/bukkit/BukkitModule.java | 10 +- .../bukkit/elements/effects/EffWax.java | 2 - .../elements/expressions/ExprCopperState.java | 5 +- .../skript/bukkit/paperutil/CopperState.java | 8 +- .../skriptlang/skript/util/ReflectUtils.java | 99 +++---------------- 6 files changed, 34 insertions(+), 169 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 5c1a315572b..46e7a922260 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -187,7 +187,7 @@ public final class Skript extends JavaPlugin implements Listener { private static org.skriptlang.skript.@UnknownNullability Skript skript = null; private static org.skriptlang.skript.@UnknownNullability Skript unmodifiableSkript = null; - private static final ReflectUtils REFLECT_UTILS = ReflectUtils.getInstance(); + private static ReflectUtils REFLECT_UTILS; private static boolean disabled = false; private static boolean partDisabled = false; @@ -407,6 +407,7 @@ public void onEnable() { handleJvmArguments(); // JVM arguments version = new Version("" + getDescription().getVersion()); // Skript version + REFLECT_UTILS = new ReflectUtils(); // Start the updater // Note: if config prohibits update checks, it will NOT do network connections @@ -2082,27 +2083,6 @@ public static boolean classExists(String className) { return REFLECT_UTILS.getClass(className); } - /** - * @param className The full package and class name. - * @param methodName The name of the method. - * @param params The {@link Class}es used as parameters for the desired method. - * @return Whether the method exists. - */ - public static boolean methodExists(String className, String methodName, Class @Nullable ... params) { - return REFLECT_UTILS.methodExists(className, methodName, params); - } - - /** - * @param className The full package and class name. - * @param methodName The name of the method. - * @param params The {@link Class}es used as parameters for the desired method. - * @param returnType The return type of the desired method. - * @return Whether the method exists. - */ - public static boolean methodExists(String className, String methodName, Class @Nullable [] params, @Nullable Class returnType) { - return REFLECT_UTILS.methodExists(className, methodName, params, returnType); - } - /** * Tests whether a method exists in the given class. * @@ -2130,27 +2110,6 @@ public static boolean methodExists(Class c, String methodName, Class @Null return REFLECT_UTILS.methodExists(c, methodName, parameterTypes, returnType); } - /** - * @param className The full package and class name. - * @param methodName The name of the method. - * @param params The {@link Class}es used as parameters for the desired method. - * @return The resulting {@link Method} if it exists, otherwise {@code null}. - */ - public static @Nullable Method getMethod(String className, String methodName, Class @Nullable ... params) { - return REFLECT_UTILS.getMethod(className, methodName, params); - } - - /** - * @param className The full package and class name. - * @param methodName The name of the method. - * @param params The {@link Class}es used as parameters for the desired method. - * @param returnType The return type of the desired method. - * @return The resulting {@link Method} if it exists, otherwise {@code null}. - */ - public static @Nullable Method getMethod(String className, String methodName, Class @Nullable [] params, @Nullable Class returnType) { - return REFLECT_UTILS.getMethod(className, methodName, params, returnType); - } - /** * @param c The {@link Class} to get the method from. * @param methodName The name of the method. @@ -2172,15 +2131,6 @@ public static boolean methodExists(Class c, String methodName, Class @Null return REFLECT_UTILS.getMethod(c, methodName, params, returnType); } - /** - * @param className The full package and class name. - * @param fieldName The name of the field. - * @return Whether the field exists. - */ - public static boolean fieldExists(String className, String fieldName) { - return REFLECT_UTILS.fieldExists(className, fieldName); - } - /** * Tests whether a field exists in the given class. * @@ -2192,15 +2142,6 @@ public static boolean fieldExists(Class c, String fieldName) { return REFLECT_UTILS.fieldExists(c, fieldName); } - /** - * @param className The full package and class name. - * @param fieldName The name of the field. - * @return The resulting {@link Field} if it exists, otherwise {@code null}. - */ - public static @Nullable Field getField(String className, String fieldName) { - return REFLECT_UTILS.getField(className, fieldName); - } - /** * @param c The {@link Class} to get the field from. * @param fieldName The name of the field. @@ -2216,8 +2157,8 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The result of the invocation if successful, otherwise {@code null}. * @param The expected return type from the invocation. */ - public static @Nullable Type methodInvoke(Method method) { - return REFLECT_UTILS.methodInvoke(method); + public static @Nullable Type invokeMethod(Method method) { + return REFLECT_UTILS.invokeMethod(method); } /** @@ -2228,8 +2169,8 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The result of the invocation if successful, otherwise {@code null}. * @param The expected return type from the invocation. */ - public static @Nullable Type methodInvoke(Method method, @Nullable Object holder, Object @Nullable ... params) { - return REFLECT_UTILS.methodInvoke(method, holder, params); + public static @Nullable Type invokeMethod(Method method, @Nullable Object holder, Object @Nullable ... params) { + return REFLECT_UTILS.invokeMethod(method, holder, params); } /** @@ -2238,8 +2179,8 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public static @Nullable Type fieldGet(Field field) { - return REFLECT_UTILS.fieldGet(field); + public static @Nullable Type invokeField(Field field) { + return REFLECT_UTILS.invokeField(field); } /** @@ -2249,8 +2190,8 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public static @Nullable Type fieldGet(Field field, @Nullable Object holder) { - return REFLECT_UTILS.fieldGet(field, holder); + public static @Nullable Type invokeField(Field field, @Nullable Object holder) { + return REFLECT_UTILS.invokeField(field, holder); } // diff --git a/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java index 86add3965e9..609dbc402a6 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java @@ -27,9 +27,9 @@ public void load(SkriptAddon addon) { new BrewingModule() ); - Set> consumers = new HashSet<>(); + Set> elementsToLoad = new HashSet<>(); - consumers.addAll(Set.of( + elementsToLoad.addAll(Set.of( CondIsWaxed::register, EffWax::register, @@ -38,12 +38,12 @@ public void load(SkriptAddon addon) { )); if (Skript.classExists("org.bukkit.entity.CopperGolem")) - consumers.add(ExprCopperGolemOxidationTime::register); + elementsToLoad.add(ExprCopperGolemOxidationTime::register); if (Skript.classExists("org.bukkit.block.data.type.CopperGolemStatue")) - consumers.add(ExprCopperGolemPose::register); + elementsToLoad.add(ExprCopperGolemPose::register); SyntaxRegistry registry = addon.syntaxRegistry(); - consumers.forEach(consumer -> consumer.accept(registry)); + elementsToLoad.forEach(consumer -> consumer.accept(registry)); } } diff --git a/src/main/java/org/skriptlang/skript/bukkit/elements/effects/EffWax.java b/src/main/java/org/skriptlang/skript/bukkit/elements/effects/EffWax.java index d9640769745..5f0dfa32c83 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/elements/effects/EffWax.java +++ b/src/main/java/org/skriptlang/skript/bukkit/elements/effects/EffWax.java @@ -23,7 +23,6 @@ import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.registration.SyntaxInfo; import org.skriptlang.skript.registration.SyntaxRegistry; -import org.skriptlang.skript.util.ReflectUtils; @Name("Wax") @Description(""" @@ -42,7 +41,6 @@ @Since("INSERT VERSION") public class EffWax extends Effect { - private static final BiMap WAX_CONVERSION; private static final BiMap UNWAX_CONVERSION = HashBiMap.create(); private static final boolean COPPER_GOLEM_EXISTS = Skript.classExists("org.bukkit.entity.CopperGolem"); diff --git a/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperState.java b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperState.java index 9e83519992d..73b26569794 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperState.java +++ b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperState.java @@ -8,7 +8,6 @@ import ch.njol.skript.doc.RequiredPlugins; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import org.skriptlang.skript.bukkit.paperutil.CopperState; import ch.njol.util.coll.CollectionUtils; import com.destroystokyo.paper.MaterialTags; import io.papermc.paper.world.WeatheringCopperState; @@ -17,8 +16,8 @@ import org.bukkit.entity.CopperGolem; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.bukkit.paperutil.CopperState; import org.skriptlang.skript.registration.SyntaxRegistry; -import org.skriptlang.skript.util.ReflectUtils; import java.util.ArrayList; import java.util.HashMap; @@ -41,7 +40,7 @@ @Since("INSERT VERSION") public class ExprCopperState extends SimplePropertyExpression { - // TODO: Remove 'CopperState' and change all instances of 'Enum' to 'WeatheringCopperState' + // TODO: Remove 'CopperState' and change all instances of 'Enum' to 'WeatheringCopperState' when 1.21.9+ is minimum version. private static final CopperStateMaterialMap STATE_MATERIALS = new CopperStateMaterialMap(); private static final List STATE_REPLACEMENTS = new ArrayList<>(); diff --git a/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java b/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java index 70d21c8d2d1..c31ec403da0 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java +++ b/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java @@ -2,11 +2,13 @@ import ch.njol.skript.Skript; import org.jetbrains.annotations.ApiStatus.Internal; +import org.skriptlang.skript.bukkit.elements.expressions.ExprCopperState; import java.lang.reflect.Method; /** - * Temp enum to mimic 'WeatheringCopperState'. + * Temporary enum to mimic 'WeatheringCopperState' to utilizes copper states such as for {@link ExprCopperState}. + * Can be removed when 1.21.9+ is the minimum supported version. */ @Internal public enum CopperState { @@ -21,7 +23,7 @@ public enum CopperState { WEATHERING_CLASS = Skript.getClass("io.papermc.paper.world.WeatheringCopperState"); if (WEATHERING_CLASS != null) { Method valuesMethod = Skript.getMethod(WEATHERING_CLASS, "values"); - WEATHERING_VALUES = Skript.methodInvoke(valuesMethod); + WEATHERING_VALUES = Skript.invokeMethod(valuesMethod); WEATHERING_VALUE_METHOD = Skript.getMethod(WEATHERING_CLASS, "valueOf", String.class); } else { WEATHERING_VALUES = null; @@ -51,7 +53,7 @@ public static Enum[] getValues() { */ public static Enum get(CopperState state) { if (WEATHERING_CLASS != null) - return Skript.methodInvoke(WEATHERING_VALUE_METHOD, null, state.name()); + return Skript.invokeMethod(WEATHERING_VALUE_METHOD, null, state.name()); return state; } diff --git a/src/main/java/org/skriptlang/skript/util/ReflectUtils.java b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java index 323939afdf1..a396d5abf6c 100644 --- a/src/main/java/org/skriptlang/skript/util/ReflectUtils.java +++ b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java @@ -13,15 +13,8 @@ */ public class ReflectUtils { - private static ReflectUtils instance; - private ReflectUtils() {} - - public static ReflectUtils getInstance() { - if (instance == null) - instance = new ReflectUtils(); - return instance; - } + public ReflectUtils() {} /** * Cache for classes. @@ -61,30 +54,6 @@ public boolean classExists(String className) { return c; } - /** - * @param className The full package and class name. - * @param methodName The name of the method. - * @param params The {@link Class}es used as parameters for the desired method. - * @return Whether the method exists. - */ - public boolean methodExists(String className, String methodName, Class @Nullable ... params) { - return methodExists(className, methodName, params, null); - } - - /** - * @param className The full package and class name. - * @param methodName The name of the method. - * @param params The {@link Class}es used as parameters for the desired method. - * @param returnType The return type of the desired method. - * @return Whether the method exists. - */ - public boolean methodExists(String className, String methodName, Class @Nullable [] params, @Nullable Class returnType) { - Class c = getClass(className); - if (c == null) - return false; - return methodExists(c, methodName, params, returnType); - } - /** * @param c The {@link Class} to check the method for. * @param methodName The name of the method. @@ -106,30 +75,6 @@ public boolean methodExists(Class c, String methodName, Class @Nullable [] return getMethod(c, methodName, params, returnTpe) != null; } - /** - * @param className The full package and class name. - * @param methodName The name of the method. - * @param params The {@link Class}es used as parameters for the desired method. - * @return The resulting {@link Method} if it exists, otherwise {@code null}. - */ - public @Nullable Method getMethod(String className, String methodName, Class @Nullable ... params) { - return getMethod(className, methodName, params, null); - } - - /** - * @param className The full package and class name. - * @param methodName The name of the method. - * @param params The {@link Class}es used as parameters for the desired method. - * @param returnType The return type of the desired method. - * @return The resulting {@link Method} if it exists, otherwise {@code null}. - */ - public @Nullable Method getMethod(String className, String methodName, Class @Nullable [] params, @Nullable Class returnType) { - Class c = getClass(className); - if (c == null) - return null; - return getMethod(c, methodName, params); - } - /** * @param c The {@link Class} to get the method from. * @param methodName The name of the method. @@ -164,22 +109,14 @@ public boolean methodExists(Class c, String methodName, Class @Nullable [] } catch (NoSuchMethodException ignored) {} METHODS.put(c, methodID, method); - if (method != null && returnType != null && method.getReturnType() != returnType) - return null; + if (method != null && returnType != null) { + Class methodType = method.getReturnType(); + if (!returnType.isAssignableFrom(methodType) && !methodType.isAssignableFrom(returnType)) + return null; + } return method; } - /** - * @param className The full package and class name. - * @param fieldName The name of the field. - * @return Whether the field exists. - */ - public boolean fieldExists(String className, String fieldName) { - Class c = getClass(className); - if (c == null) - return false; - return fieldExists(c, fieldName); - } /** * @param c The {@link Class} to check the field for. @@ -190,18 +127,6 @@ public boolean fieldExists(Class c, String fieldName) { return getField(c, fieldName) != null; } - /** - * @param className The full package and class name. - * @param fieldName The name of the field. - * @return The resulting {@link Field} if it exists, otherwise {@code null}. - */ - public @Nullable Field getField(String className, String fieldName) { - Class c = getClass(className); - if (c == null) - return null; - return getField(c, fieldName); - } - /** * @param c The {@link Class} to get the field from. * @param fieldName The name of the field. @@ -225,8 +150,8 @@ public boolean fieldExists(Class c, String fieldName) { * @return The result of the invocation if successful, otherwise {@code null}. * @param The expected return type from the invocation. */ - public @Nullable Type methodInvoke(Method method) { - return methodInvoke(method, null); + public @Nullable Type invokeMethod(Method method) { + return invokeMethod(method, null); } /** @@ -237,7 +162,7 @@ public boolean fieldExists(Class c, String fieldName) { * @return The result of the invocation if successful, otherwise {@code null}. * @param The expected return type from the invocation. */ - public @Nullable Type methodInvoke(Method method, @Nullable Object holder, Object @Nullable ... params) { + public @Nullable Type invokeMethod(Method method, @Nullable Object holder, Object @Nullable ... params) { method.setAccessible(true); try { //noinspection unchecked @@ -252,8 +177,8 @@ public boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public @Nullable Type fieldGet(Field field) { - return fieldGet(field, null); + public @Nullable Type invokeField(Field field) { + return invokeField(field, null); } /** @@ -263,7 +188,7 @@ public boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public @Nullable Type fieldGet(Field field, @Nullable Object holder) { + public @Nullable Type invokeField(Field field, @Nullable Object holder) { field.setAccessible(true); try { //noinspection unchecked From f555b8bcdcb8449cc6770dfab70a57493d229c92 Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Thu, 23 Oct 2025 19:21:21 -0400 Subject: [PATCH 4/5] Sovde's Changes --- src/main/java/ch/njol/skript/Skript.java | 11 +++++------ .../org/skriptlang/skript/bukkit/BukkitModule.java | 2 -- .../java/org/skriptlang/skript/util/ReflectUtils.java | 6 +++--- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 46e7a922260..c24e1fad200 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -187,7 +187,7 @@ public final class Skript extends JavaPlugin implements Listener { private static org.skriptlang.skript.@UnknownNullability Skript skript = null; private static org.skriptlang.skript.@UnknownNullability Skript unmodifiableSkript = null; - private static ReflectUtils REFLECT_UTILS; + private static final ReflectUtils REFLECT_UTILS = new ReflectUtils(); private static boolean disabled = false; private static boolean partDisabled = false; @@ -407,7 +407,6 @@ public void onEnable() { handleJvmArguments(); // JVM arguments version = new Version("" + getDescription().getVersion()); // Skript version - REFLECT_UTILS = new ReflectUtils(); // Start the updater // Note: if config prohibits update checks, it will NOT do network connections @@ -2179,8 +2178,8 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public static @Nullable Type invokeField(Field field) { - return REFLECT_UTILS.invokeField(field); + public static @Nullable Type getFieldValue(Field field) { + return REFLECT_UTILS.getFieldValue(field); } /** @@ -2190,8 +2189,8 @@ public static boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public static @Nullable Type invokeField(Field field, @Nullable Object holder) { - return REFLECT_UTILS.invokeField(field, holder); + public static @Nullable Type getFieldValue(Field field, @Nullable Object holder) { + return REFLECT_UTILS.getFieldValue(field, holder); } // diff --git a/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java index 609dbc402a6..b20f85256dd 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java @@ -31,9 +31,7 @@ public void load(SkriptAddon addon) { elementsToLoad.addAll(Set.of( CondIsWaxed::register, - EffWax::register, - ExprCopperState::register )); diff --git a/src/main/java/org/skriptlang/skript/util/ReflectUtils.java b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java index a396d5abf6c..3de3732f921 100644 --- a/src/main/java/org/skriptlang/skript/util/ReflectUtils.java +++ b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java @@ -177,8 +177,8 @@ public boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public @Nullable Type invokeField(Field field) { - return invokeField(field, null); + public @Nullable Type getFieldValue(Field field) { + return getFieldValue(field, null); } /** @@ -188,7 +188,7 @@ public boolean fieldExists(Class c, String fieldName) { * @return The value of the {@link Field}. * @param The expected return type. */ - public @Nullable Type invokeField(Field field, @Nullable Object holder) { + public @Nullable Type getFieldValue(Field field, @Nullable Object holder) { field.setAccessible(true); try { //noinspection unchecked From 9a46b68c06e7e322a944d02d11e46978f51f1b4a Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Sun, 26 Oct 2025 20:29:28 -0400 Subject: [PATCH 5/5] Efy's Changes --- .../ExprCopperGolemOxidationTime.java | 21 +++++++------- .../skript/bukkit/paperutil/CopperState.java | 2 +- .../skriptlang/skript/util/ReflectUtils.java | 28 +++++++++---------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemOxidationTime.java b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemOxidationTime.java index 2f1b4b9065a..4112ce593d6 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemOxidationTime.java +++ b/src/main/java/org/skriptlang/skript/bukkit/elements/expressions/ExprCopperGolemOxidationTime.java @@ -20,7 +20,7 @@ @Name("Time Until Oxidation") @Description(""" - The time until a copper golem oxidizes to its next state. (Normal -> Exposed -> Weathered -> Oxidized) + The time until a copper golem oxidizes to its next state. (Normal -> Exposed -> Weathered -> Oxidized). Copper golems that are waxed do not go through oxidation. Setting or resetting the time until oxidation on a waxed copper golem will remove the waxed state. Resetting the time until oxidation uses vanilla behavior of generating a random time between 7 hours and 7 hours 40 minutes. @@ -48,16 +48,15 @@ public static void register(SyntaxRegistry registry) { @Override public @Nullable Timespan convert(Entity entity) { - if (entity instanceof CopperGolem golem) { - if (!(golem.getOxidizing() instanceof AtTime atTime)) - return null; - long worldTime = golem.getWorld().getGameTime(); - long oxidationTime = atTime.time(); - if (worldTime > oxidationTime) - return null; - return new Timespan(TimePeriod.TICK, oxidationTime - worldTime); - } - return null; + if (!(entity instanceof CopperGolem golem)) + return null; + if (!(golem.getOxidizing() instanceof AtTime atTime)) + return null; + long worldTime = golem.getWorld().getGameTime(); + long oxidationTime = atTime.time(); + if (worldTime > oxidationTime) + return null; + return new Timespan(TimePeriod.TICK, oxidationTime - worldTime); } @Override diff --git a/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java b/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java index c31ec403da0..247158f427b 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java +++ b/src/main/java/org/skriptlang/skript/bukkit/paperutil/CopperState.java @@ -7,7 +7,7 @@ import java.lang.reflect.Method; /** - * Temporary enum to mimic 'WeatheringCopperState' to utilizes copper states such as for {@link ExprCopperState}. + * Temporary enum to mimic `WeatheringCopperState`, which represents copper states, used in elements such as for {@link ExprCopperState}. * Can be removed when 1.21.9+ is the minimum supported version. */ @Internal diff --git a/src/main/java/org/skriptlang/skript/util/ReflectUtils.java b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java index 3de3732f921..f2556a6658f 100644 --- a/src/main/java/org/skriptlang/skript/util/ReflectUtils.java +++ b/src/main/java/org/skriptlang/skript/util/ReflectUtils.java @@ -13,23 +13,22 @@ */ public class ReflectUtils { - public ReflectUtils() {} /** * Cache for classes. */ - private final Map> CLASSES = new HashMap<>(); + private final Map> classes = new HashMap<>(); /** * Cache for methods of classes. */ - private final MethodMap METHODS = new MethodMap(); + private final MethodMap methods = new MethodMap(); /** * Cache for fields of classes. */ - private final FieldMap FIELDS = new FieldMap(); + private final FieldMap fields = new FieldMap(); /** * @param className The full package and class name. @@ -44,13 +43,13 @@ public boolean classExists(String className) { * @return The resulting {@link Class} if found, otherwise {@code null}. */ public @Nullable Class getClass(String className) { - if (CLASSES.containsKey(className)) - return CLASSES.get(className); + if (classes.containsKey(className)) + return classes.get(className); Class c = null; try { c = Class.forName(className); } catch (ClassNotFoundException ignored) {} - CLASSES.put(className, c); + classes.put(className, c); return c; } @@ -94,21 +93,21 @@ public boolean methodExists(Class c, String methodName, Class @Nullable [] */ public @Nullable Method getMethod(Class c, String methodName, Class @Nullable [] params, @Nullable Class returnType) { MethodID methodID = new MethodID(c, methodName, params); - if (METHODS.contains(c, methodID)) { - Method method = METHODS.get(c, methodID); + if (methods.contains(c, methodID)) { + Method method = methods.get(c, methodID); if (method != null && returnType != null) { Class methodType = method.getReturnType(); if (!returnType.isAssignableFrom(methodType) && !methodType.isAssignableFrom(returnType)) return null; } - return METHODS.get(c, methodID); + return methods.get(c, methodID); } Method method = null; try { method = c.getDeclaredMethod(methodName, params); } catch (NoSuchMethodException ignored) {} - METHODS.put(c, methodID, method); + methods.put(c, methodID, method); if (method != null && returnType != null) { Class methodType = method.getReturnType(); if (!returnType.isAssignableFrom(methodType) && !methodType.isAssignableFrom(returnType)) @@ -117,7 +116,6 @@ public boolean methodExists(Class c, String methodName, Class @Nullable [] return method; } - /** * @param c The {@link Class} to check the field for. * @param fieldName The name of the field. @@ -133,14 +131,14 @@ public boolean fieldExists(Class c, String fieldName) { * @return The resulting {@link Field} if it exists, otherwise {@code null}. */ public @Nullable Field getField(Class c, String fieldName) { - if (FIELDS.contains(c, fieldName)) - return FIELDS.get(c, fieldName); + if (fields.contains(c, fieldName)) + return fields.get(c, fieldName); Field field = null; try { field = c.getDeclaredField(fieldName); } catch (NoSuchFieldException ignored) {} - FIELDS.put(c, fieldName, field); + fields.put(c, fieldName, field); return field; }