Date: Sun, 28 Jun 2026 13:56:28 -0400
Subject: [PATCH 17/41] Teleportation Cleanup (#8728)
* Initial cleanup
* Add deprecation warnings for certain flags
---
build.gradle | 3 -
.../skript/bukkitutil/SkriptTeleportFlag.java | 33 -----
.../skript/classes/data/BukkitClasses.java | 9 --
src/main/java/ch/njol/skript/util/Utils.java | 59 ---------
.../skript/bukkit/entity/EntityModule.java | 9 ++
.../entity/elements}/effects/EffTeleport.java | 118 ++++++++----------
.../entity/types/TeleportFlagClassInfo.java | 72 +++++++++++
.../effects => bukkit/entity}/EffTeleport.sk | 0
8 files changed, 131 insertions(+), 172 deletions(-)
delete mode 100644 src/main/java/ch/njol/skript/bukkitutil/SkriptTeleportFlag.java
rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/entity/elements}/effects/EffTeleport.java (57%)
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/entity/types/TeleportFlagClassInfo.java
rename src/test/skript/tests/{syntaxes/effects => bukkit/entity}/EffTeleport.sk (100%)
diff --git a/build.gradle b/build.gradle
index 46cedfd31..dbcdf74a9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -25,7 +25,6 @@ allprojects {
}
dependencies {
- shadow group: 'io.papermc', name: 'paperlib', version: '1.0.8'
shadow group: 'org.bstats', name: 'bstats-bukkit', version: '3.2.1'
implementation group: 'io.papermc.paper', name: 'paper-api', version: '26.1.2.build.+'
@@ -101,11 +100,9 @@ tasks.withType(ShadowJar).configureEach {
project.configurations.shadow
]
dependencies {
- include(dependency('io.papermc:paperlib'))
include(dependency('org.bstats:bstats-bukkit'))
include(dependency('org.bstats:bstats-base'))
}
- relocate 'io.papermc.lib', 'ch.njol.skript.paperlib'
relocate 'org.bstats', 'ch.njol.skript.bstats'
manifest {
attributes(
diff --git a/src/main/java/ch/njol/skript/bukkitutil/SkriptTeleportFlag.java b/src/main/java/ch/njol/skript/bukkitutil/SkriptTeleportFlag.java
deleted file mode 100644
index 28d1ba69c..000000000
--- a/src/main/java/ch/njol/skript/bukkitutil/SkriptTeleportFlag.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package ch.njol.skript.bukkitutil;
-
-import io.papermc.paper.entity.TeleportFlag;
-import io.papermc.paper.entity.TeleportFlag.EntityState;
-import io.papermc.paper.entity.TeleportFlag.Relative;
-
-/**
- * A utility enum for accessing Paper's teleport flags (1.19.4+)
- */
-public enum SkriptTeleportFlag {
-
- RETAIN_OPEN_INVENTORY(EntityState.RETAIN_OPEN_INVENTORY),
- RETAIN_PASSENGERS(EntityState.RETAIN_PASSENGERS),
- RETAIN_VEHICLE(EntityState.RETAIN_VEHICLE),
- RETAIN_DIRECTION(Relative.PITCH, Relative.YAW),
- RETAIN_PITCH(Relative.PITCH),
- RETAIN_YAW(Relative.YAW),
- RETAIN_MOVEMENT(Relative.X, Relative.Y, Relative.Z),
- RETAIN_X(Relative.X),
- RETAIN_Y(Relative.Y),
- RETAIN_Z(Relative.Z);
-
- final TeleportFlag[] teleportFlags;
-
- SkriptTeleportFlag(TeleportFlag... teleportFlags) {
- this.teleportFlags = teleportFlags;
- }
-
- public TeleportFlag[] getTeleportFlags() {
- return teleportFlags;
- }
-
-}
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 0e7cb7627..92468ad71 100644
--- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java
+++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java
@@ -1,9 +1,7 @@
package ch.njol.skript.classes.data;
import ch.njol.skript.Skript;
-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.registry.RegistryClassInfo;
import ch.njol.skript.expressions.ExprDamageCause;
@@ -796,13 +794,6 @@ public String toVariableNameString(WorldBorder border) {
.description("Represents the various banner patterns that can be applied to a banner.")
.since("2.10"));
- if (Skript.classExists("io.papermc.paper.entity.TeleportFlag"))
- Classes.registerClass(new EnumClassInfo<>(SkriptTeleportFlag.class, "teleportflag", "teleport flags")
- .user("teleport ?flags?")
- .name("Teleport Flag")
- .description("Teleport Flags are settings to retain during a teleport.")
- .since("2.10"));
-
Classes.registerClass(new ClassInfo<>(Vehicle.class, "vehicle")
.user("vehicles?")
.name("Vehicle")
diff --git a/src/main/java/ch/njol/skript/util/Utils.java b/src/main/java/ch/njol/skript/util/Utils.java
index 334106b4c..c6fd5de2a 100644
--- a/src/main/java/ch/njol/skript/util/Utils.java
+++ b/src/main/java/ch/njol/skript/util/Utils.java
@@ -1,7 +1,6 @@
package ch.njol.skript.util;
import ch.njol.skript.Skript;
-import ch.njol.skript.effects.EffTeleport;
import ch.njol.skript.localization.Language;
import ch.njol.skript.registrations.Classes;
import ch.njol.util.NonNullPair;
@@ -415,64 +414,6 @@ public static String a(final String s, final boolean capA) {
}
}
- /**
- * Gets the collision height of solid or partially-solid blocks at the center of the block.
- * This is mostly for use in the {@link EffTeleport teleport effect}.
- *
- * This version operates on numeric ids, thus only working on
- * Minecraft 1.12 or older.
- *
- * @param type
- * @return The block's height at the center
- */
- public static double getBlockHeight(final int type, final byte data) {
- switch (type) {
- case 26: // bed
- return 9. / 16;
- case 44: // slabs
- case 126:
- return (data & 0x8) == 0 ? 0.5 : 1;
- case 78: // snow layer
- return data == 0 ? 1 : (data % 8) * 2. / 16;
- case 85: // fences & gates
- case 107:
- case 113:
- case 139: // cobblestone wall
- return 1.5;
- case 88: // soul sand
- return 14. / 16;
- case 92: // cake
- return 7. / 16;
- case 93: // redstone repeater
- case 94:
- case 149: // redstone comparator
- case 150:
- return 2. / 16;
- case 96: // trapdoor
- return (data & 0x4) == 0 ? ((data & 0x8) == 0 ? 3. / 16 : 1) : 0;
- case 116: // enchantment table
- return 12. / 16;
- case 117: // brewing stand
- return 14. / 16;
- case 118: // cauldron
- return 5. / 16;
- case 120: // end portal frame
- return (data & 0x4) == 0 ? 13. / 16 : 1;
- case 127: // cocoa plant
- return 12. / 16;
- case 140: // flower pot
- return 6. / 16;
- case 144: // mob head
- return 0.5;
- case 151: // daylight sensor
- return 6. / 16;
- case 154: // hopper
- return 10. / 16;
- default:
- return 1;
- }
- }
-
/**
* Sends a plugin message using the first player from {@link Bukkit#getOnlinePlayers()}.
*
diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java b/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java
index 5e07c604e..7ee808d52 100644
--- a/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java
@@ -2,16 +2,19 @@
import ch.njol.skript.Skript;
import ch.njol.skript.entity.SimpleEntityData;
+import ch.njol.skript.registrations.Classes;
import org.bukkit.entity.AbstractNautilus;
import org.skriptlang.skript.addon.AddonModule;
import org.skriptlang.skript.addon.HierarchicalAddonModule;
import org.skriptlang.skript.addon.SkriptAddon;
import org.skriptlang.skript.bukkit.entity.displays.DisplayModule;
+import org.skriptlang.skript.bukkit.entity.elements.effects.EffTeleport;
import org.skriptlang.skript.bukkit.entity.interactions.InteractionModule;
import org.skriptlang.skript.bukkit.entity.elements.expressions.ExprDeathMessage;
import org.skriptlang.skript.bukkit.entity.entitydata.NautilusData;
import org.skriptlang.skript.bukkit.entity.entitydata.ZombieNautilusData;
import org.skriptlang.skript.bukkit.entity.player.PlayerModule;
+import org.skriptlang.skript.bukkit.entity.types.TeleportFlagClassInfo;
import java.util.List;
@@ -29,6 +32,11 @@ public Iterable children() {
);
}
+ @Override
+ protected void initSelf(SkriptAddon addon) {
+ Classes.registerClass(new TeleportFlagClassInfo());
+ }
+
@Override
protected void loadSelf(SkriptAddon addon) {
if (Skript.classExists("org.bukkit.entity.Nautilus")) {
@@ -38,6 +46,7 @@ protected void loadSelf(SkriptAddon addon) {
}
register(addon,
+ EffTeleport::register,
ExprDeathMessage::register
);
}
diff --git a/src/main/java/ch/njol/skript/effects/EffTeleport.java b/src/main/java/org/skriptlang/skript/bukkit/entity/elements/effects/EffTeleport.java
similarity index 57%
rename from src/main/java/ch/njol/skript/effects/EffTeleport.java
rename to src/main/java/org/skriptlang/skript/bukkit/entity/elements/effects/EffTeleport.java
index 145cc5f57..92e7ef691 100644
--- a/src/main/java/ch/njol/skript/effects/EffTeleport.java
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/elements/effects/EffTeleport.java
@@ -1,8 +1,9 @@
-package ch.njol.skript.effects;
+package org.skriptlang.skript.bukkit.entity.elements.effects;
import ch.njol.skript.Skript;
-import ch.njol.skript.bukkitutil.SkriptTeleportFlag;
+import org.skriptlang.skript.bukkit.entity.types.TeleportFlagClassInfo.SkriptTeleportFlag;
import ch.njol.skript.doc.*;
+import ch.njol.skript.effects.Delay;
import ch.njol.skript.lang.*;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.sections.EffSecSpawn.SpawnEvent;
@@ -10,29 +11,25 @@
import ch.njol.skript.util.Direction;
import ch.njol.skript.variables.Variables;
import ch.njol.util.Kleenean;
-import io.papermc.lib.PaperLib;
-import io.papermc.lib.environments.PaperEnvironment;
import io.papermc.paper.entity.TeleportFlag;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
-import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.skriptlang.skript.registration.SyntaxInfo;
+import org.skriptlang.skript.registration.SyntaxRegistry;
-import java.util.Arrays;
-import java.util.Objects;
import java.util.stream.Stream;
@Name("Teleport")
-@Description({
- "Teleport an entity to a specific location. ",
- "This effect is delayed by default on Paper, meaning certain syntax such as the return effect for functions cannot be used after this effect.",
- "The keyword 'force' indicates this effect will not be delayed, ",
- "which may cause lag spikes or server crashes when using this effect to teleport entities to unloaded chunks.",
- "Teleport flags are settings to retain during a teleport. Such as direction, passengers, x coordinate, etc."
-})
+@Description("""
+ Teleport an entity to a specific location.
+ This effect is delayed by default, as it waits for the chunk of the location to be loaded before teleporting.
+ The keyword 'force' may be used to bypass this behavior (preventing the delay), but note that it may cause lag spikes or other server performance issues when teleporting to unloaded chunks.
+ Teleport flags are properties to retain during a teleport, such as direction, passengers, and velocities.
+ """)
@Example("teleport the player to {home::%uuid of player%}")
@Example("teleport the attacker to the victim")
@Example("""
@@ -43,37 +40,36 @@
@Since("1.0, 2.10 (flags)")
public class EffTeleport extends Effect {
- private static final boolean TELEPORT_FLAGS_SUPPORTED = Skript.classExists("io.papermc.paper.entity.TeleportFlag");
- private static final boolean CAN_RUN_ASYNC = PaperLib.getEnvironment() instanceof PaperEnvironment;
-
- static {
- String extra = "";
- if (TELEPORT_FLAGS_SUPPORTED)
- extra = " [[while] retaining %-teleportflags%]";
- Skript.registerEffect(EffTeleport.class, "[:force] teleport %entities% (to|%direction%) %location%" + extra);
+ public static void register(SyntaxRegistry syntaxRegistry) {
+ syntaxRegistry.register(SyntaxRegistry.EFFECT, SyntaxInfo.builder(EffTeleport.class)
+ .supplier(EffTeleport::new)
+ .addPattern("[:force] teleport %entities% (to|%direction%) %location% [[while] retaining %-teleportflags%]")
+ .build());
}
- private @Nullable Expression teleportFlags;
+ private boolean async;
private Expression entities;
private Expression location;
- private boolean async;
+ private @Nullable Expression teleportFlags;
@Override
@SuppressWarnings("unchecked")
public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
- entities = (Expression) exprs[0];
- location = Direction.combine((Expression extends Direction>) exprs[1], (Expression extends Location>) exprs[2]);
- async = CAN_RUN_ASYNC && !parseResult.hasTag("force");
- if (TELEPORT_FLAGS_SUPPORTED)
- teleportFlags = (Expression) exprs[3];
-
if (getParser().isCurrentEvent(SpawnEvent.class)) {
Skript.error("You cannot teleport an entity that hasn't spawned yet. Ensure you're using the location expression from the spawn section pattern.");
return false;
}
- if (async)
- getParser().setHasDelayBefore(Kleenean.UNKNOWN); // UNKNOWN because it isn't async if the chunk is already loaded.
+ async = !parseResult.hasTag("force");
+ entities = (Expression) exprs[0];
+ location = Direction.combine((Expression extends Direction>) exprs[1], (Expression extends Location>) exprs[2]);
+ teleportFlags = (Expression) exprs[3];
+
+ if (async) {
+ // UNKNOWN because it isn't async if the chunk is already loaded
+ getParser().setHasDelayBefore(Kleenean.UNKNOWN);
+ }
+
return true;
}
@@ -82,21 +78,19 @@ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelaye
debug(event, true);
TriggerItem next = getNext();
- boolean delayed = Delay.isDelayed(event);
Location location = this.location.getSingle(event);
if (location == null)
return next;
boolean unknownWorld = !location.isWorldLoaded();
- Entity[] entityArray = entities.getArray(event); // We have to fetch this before possible async execution to avoid async local variable access.
+ Entity[] entityArray = entities.getArray(event);
if (entityArray.length == 0)
return next;
- if (!delayed) {
+ if (!Delay.isDelayed(event)) { // specific behavior for integration with certain events
if (event instanceof PlayerRespawnEvent playerRespawnEvent && entityArray.length == 1 && entityArray[0].equals(playerRespawnEvent.getPlayer())) {
- if (unknownWorld)
- return next;
- playerRespawnEvent.setRespawnLocation(location);
+ if (!unknownWorld)
+ playerRespawnEvent.setRespawnLocation(location);
return next;
}
@@ -109,6 +103,7 @@ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelaye
return next;
}
}
+
if (unknownWorld) { // we can't fetch the chunk without a world
if (entityArray.length == 1) { // if there's 1 thing we can borrow its world
Entity entity = entityArray[0];
@@ -122,24 +117,28 @@ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelaye
}
}
+ final TeleportFlag[] teleportFlags;
+ if (this.teleportFlags == null) {
+ teleportFlags = new TeleportFlag[0];
+ } else {
+ teleportFlags = this.teleportFlags.stream(event)
+ .flatMap(teleportFlag -> Stream.of(teleportFlag.getTeleportFlags()))
+ .toArray(TeleportFlag[]::new);
+ }
+
if (!async) {
- SkriptTeleportFlag[] teleportFlags = this.teleportFlags == null ? null : this.teleportFlags.getArray(event);
for (Entity entity : entityArray) {
- teleport(entity, location, teleportFlags);
+ entity.teleport(location, teleportFlags);
}
return next;
}
final Location fixed = location;
Object localVars = Variables.removeLocals(event);
-
- // This will either fetch the chunk instantly if on Spigot or already loaded or fetch it async if on Paper.
- PaperLib.getChunkAtAsync(location).thenAccept(chunk -> {
+ fixed.getWorld().getChunkAtAsync(fixed).thenAccept(ignored -> {
Delay.addDelayedEvent(event);
- // The following is now on the main thread
- SkriptTeleportFlag[] teleportFlags = this.teleportFlags == null ? null : this.teleportFlags.getArray(event);
for (Entity entity : entityArray) {
- teleport(entity, fixed, teleportFlags);
+ entity.teleport(fixed, teleportFlags);
}
// Re-set local variables
@@ -161,6 +160,7 @@ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelaye
Variables.removeLocals(event); // Clean up local vars, we may be exiting now
SkriptTimings.stop(timing);
});
+
return null;
}
@@ -171,28 +171,10 @@ protected void execute(Event event) {
@Override
public String toString(@Nullable Event event, boolean debug) {
- SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug)
- .append("teleport", entities, "to", location);
- if (teleportFlags != null)
- builder.append("retaining", teleportFlags);
- return builder.toString();
- }
-
- private void teleport(@NotNull Entity entity, @NotNull Location location, SkriptTeleportFlag... skriptTeleportFlags) {
- if (location.getWorld() == null) {
- location = location.clone();
- location.setWorld(entity.getWorld());
- }
-
- if (!TELEPORT_FLAGS_SUPPORTED || skriptTeleportFlags == null) {
- entity.teleport(location);
- return;
- }
-
- Stream teleportFlags = Arrays.stream(skriptTeleportFlags)
- .flatMap(teleportFlag -> Stream.of(teleportFlag.getTeleportFlags()))
- .filter(Objects::nonNull);
- entity.teleport(location, teleportFlags.toArray(TeleportFlag[]::new));
+ return new SyntaxStringBuilder(event, debug)
+ .append("teleport", entities, "to", location)
+ .appendIf(teleportFlags != null, "retaining", teleportFlags)
+ .toString();
}
}
diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/types/TeleportFlagClassInfo.java b/src/main/java/org/skriptlang/skript/bukkit/entity/types/TeleportFlagClassInfo.java
new file mode 100644
index 000000000..ced03094b
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/types/TeleportFlagClassInfo.java
@@ -0,0 +1,72 @@
+package org.skriptlang.skript.bukkit.entity.types;
+
+import ch.njol.skript.Skript;
+import org.skriptlang.skript.bukkit.entity.types.TeleportFlagClassInfo.SkriptTeleportFlag;
+import ch.njol.skript.classes.EnumClassInfo;
+import io.papermc.paper.entity.TeleportFlag;
+import io.papermc.paper.entity.TeleportFlag.EntityState;
+import io.papermc.paper.entity.TeleportFlag.Relative;
+import org.skriptlang.skript.lang.script.ScriptWarning;
+
+public class TeleportFlagClassInfo extends EnumClassInfo {
+
+ private static final boolean IS_ENTITY_STATE_DEPRECATED = Skript.isRunningMinecraft(1, 21, 10);
+
+ public TeleportFlagClassInfo() {
+ super(SkriptTeleportFlag.class, "teleportflag", "teleport flags", flag -> {
+ switch (flag) {
+ case RETAIN_PITCH, RETAIN_YAW -> ScriptWarning.printDeprecationWarning(
+ "It is no longer possible to individually retain yaw or pitch velocities." +
+ " Use 'yaw and pitch velocity' to retain both instead.");
+ case RETAIN_OPEN_INVENTORY -> {
+ if (IS_ENTITY_STATE_DEPRECATED) {
+ ScriptWarning.printDeprecationWarning(
+ "Inventories are no longer closed on teleportation, meaning this flag is no longer necessary.");
+ }
+ }
+ case RETAIN_PASSENGERS -> {
+ if (IS_ENTITY_STATE_DEPRECATED) {
+ ScriptWarning.printDeprecationWarning(
+ "Passengers are no longer removed on teleportation, meaning this flag is no longer necessary.");
+ }
+ }
+ case RETAIN_VEHICLE -> {
+ if (IS_ENTITY_STATE_DEPRECATED) {
+ ScriptWarning.printDeprecationWarning(
+ "The 'retain vehicle' flag has no functionality on modern versions due to technical limitations with the client.");
+ }
+ }
+ }
+ });
+ this.user("teleport ?flags?")
+ .name("Teleport Flag")
+ .description("Teleport Flags are settings to retain during a teleport.")
+ .since("2.10");
+ }
+
+ public enum SkriptTeleportFlag {
+
+ RETAIN_OPEN_INVENTORY(EntityState.RETAIN_OPEN_INVENTORY),
+ RETAIN_PASSENGERS(EntityState.RETAIN_PASSENGERS),
+ RETAIN_VEHICLE(EntityState.RETAIN_VEHICLE),
+ RETAIN_DIRECTION(Relative.VELOCITY_ROTATION),
+ RETAIN_PITCH(Relative.PITCH),
+ RETAIN_YAW(Relative.YAW),
+ RETAIN_MOVEMENT(Relative.VELOCITY_X, Relative.VELOCITY_Y, Relative.VELOCITY_Z),
+ RETAIN_X(Relative.VELOCITY_X),
+ RETAIN_Y(Relative.VELOCITY_Y),
+ RETAIN_Z(Relative.VELOCITY_Z);
+
+ final TeleportFlag[] teleportFlags;
+
+ SkriptTeleportFlag(TeleportFlag... teleportFlags) {
+ this.teleportFlags = teleportFlags;
+ }
+
+ public TeleportFlag[] getTeleportFlags() {
+ return teleportFlags;
+ }
+
+ }
+
+}
diff --git a/src/test/skript/tests/syntaxes/effects/EffTeleport.sk b/src/test/skript/tests/bukkit/entity/EffTeleport.sk
similarity index 100%
rename from src/test/skript/tests/syntaxes/effects/EffTeleport.sk
rename to src/test/skript/tests/bukkit/entity/EffTeleport.sk
From 50fa1af841fe0f9b201957dcc17d8037d04d467b Mon Sep 17 00:00:00 2001
From: Patrick Miller
Date: Tue, 30 Jun 2026 10:04:21 -0400
Subject: [PATCH 18/41] Fix simple SyntaxInfo return types (#8730)
---
.../skript/bukkit/registration/BukkitSyntaxInfos.java | 5 ++---
.../skriptlang/skript/registration/DefaultSyntaxInfos.java | 4 ++--
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/skriptlang/skript/bukkit/registration/BukkitSyntaxInfos.java b/src/main/java/org/skriptlang/skript/bukkit/registration/BukkitSyntaxInfos.java
index b6abf211c..c27afcec2 100644
--- a/src/main/java/org/skriptlang/skript/bukkit/registration/BukkitSyntaxInfos.java
+++ b/src/main/java/org/skriptlang/skript/bukkit/registration/BukkitSyntaxInfos.java
@@ -2,7 +2,6 @@
import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.SkriptEvent.ListeningBehavior;
-import ch.njol.skript.lang.SyntaxElement;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfosImpl.EventImpl;
@@ -43,7 +42,7 @@ public interface Event extends SyntaxInfo {
* @return A syntax info representing {@code type}.
*/
@Contract("_, _, _, _, _ -> new")
- static SyntaxInfo simple(Class eventClass, Supplier instanceSupplier,
+ static Event simple(Class eventClass, Supplier instanceSupplier,
String name, Class extends org.bukkit.event.Event> bukkitEventClass, String... patterns) {
return builder(eventClass, name)
.supplier(instanceSupplier)
@@ -187,7 +186,7 @@ interface Builder, E extends SkriptEvent> extends Syntax
* @return This builder.
* @see Event#since()
*/
- @Contract("_ -> this")
+ @Contract("-> this")
B clearSince();
/**
diff --git a/src/main/java/org/skriptlang/skript/registration/DefaultSyntaxInfos.java b/src/main/java/org/skriptlang/skript/registration/DefaultSyntaxInfos.java
index 0c09886c2..e161e67de 100644
--- a/src/main/java/org/skriptlang/skript/registration/DefaultSyntaxInfos.java
+++ b/src/main/java/org/skriptlang/skript/registration/DefaultSyntaxInfos.java
@@ -31,7 +31,7 @@ interface Expression, R> extends Syn
* @return A syntax info representing {@code type}.
*/
@Contract("_, _, _ , _-> new")
- static , R> SyntaxInfo simple(Class expressionClass,
+ static , R> Expression simple(Class expressionClass,
Supplier instanceSupplier, Class returnType, String... patterns) {
return builder(expressionClass, returnType)
.supplier(instanceSupplier)
@@ -137,7 +137,7 @@ public boolean canBeSection() {
* @return A syntax info representing {@code type}.
*/
@Contract("_, _, _ -> new")
- static SyntaxInfo simple(Class structureClass,
+ static Structure simple(Class structureClass,
Supplier instanceSupplier, String... patterns) {
return builder(structureClass)
.supplier(instanceSupplier)
From 0b270966bbd3df8e3fb3d9f12fecb921adcd1cde Mon Sep 17 00:00:00 2001
From: OwlBe <258166039+AnOwlBe@users.noreply.github.com>
Date: Tue, 30 Jun 2026 10:55:52 -0400
Subject: [PATCH 19/41] Add expression for Skull Texture (#8500)
---
gradlew.bat | 164 +++++++++---------
.../skript/bukkit/misc/MiscModule.java | 1 +
.../expressions/ExprSkullTexture.java | 87 ++++++++++
.../syntaxes/expressions/ExprSkullTexture.sk | 6 +
4 files changed, 176 insertions(+), 82 deletions(-)
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/misc/elements/expressions/ExprSkullTexture.java
create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprSkullTexture.sk
diff --git a/gradlew.bat b/gradlew.bat
index 8508ef684..a51ec4f58 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,82 +1,82 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-@rem SPDX-License-Identifier: Apache-2.0
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem gradlew startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables, and ensure extensions are enabled
-setlocal EnableExtensions
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-"%COMSPEC%" /c exit 1
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-"%COMSPEC%" /c exit 1
-
-:execute
-@rem Setup the command line
-
-
-
-@rem Execute gradlew
-@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
-@rem which allows us to clear the local environment before executing the java command
-endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
-
-:exitWithErrorLevel
-@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
-"%COMSPEC%" /c exit %ERRORLEVEL%
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem gradlew startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables, and ensure extensions are enabled
+setlocal EnableExtensions
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+"%COMSPEC%" /c exit 1
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+"%COMSPEC%" /c exit 1
+
+:execute
+@rem Setup the command line
+
+
+
+@rem Execute gradlew
+@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
+@rem which allows us to clear the local environment before executing the java command
+endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
+
+:exitWithErrorLevel
+@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
+"%COMSPEC%" /c exit %ERRORLEVEL%
diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/MiscModule.java b/src/main/java/org/skriptlang/skript/bukkit/misc/MiscModule.java
index e778d8681..e30b5e8fa 100644
--- a/src/main/java/org/skriptlang/skript/bukkit/misc/MiscModule.java
+++ b/src/main/java/org/skriptlang/skript/bukkit/misc/MiscModule.java
@@ -21,6 +21,7 @@ protected void loadSelf(SkriptAddon addon) {
ExprMOTD::register,
ExprQuaternionAxisAngle::register,
ExprRotate::register,
+ ExprSkullTexture::register,
ExprTextOf::register,
ExprWithYawPitch::register
);
diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/elements/expressions/ExprSkullTexture.java b/src/main/java/org/skriptlang/skript/bukkit/misc/elements/expressions/ExprSkullTexture.java
new file mode 100644
index 000000000..a2864caf1
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/misc/elements/expressions/ExprSkullTexture.java
@@ -0,0 +1,87 @@
+package org.skriptlang.skript.bukkit.misc.elements.expressions;
+
+import ch.njol.skript.aliases.ItemType;
+import ch.njol.skript.classes.Changer.ChangeMode;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Example;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.expressions.base.SimplePropertyExpression;
+import ch.njol.util.coll.CollectionUtils;
+
+import com.destroystokyo.paper.profile.ProfileProperty;
+import org.bukkit.Bukkit;
+import org.bukkit.event.Event;
+import org.bukkit.inventory.meta.SkullMeta;
+import com.destroystokyo.paper.profile.PlayerProfile;
+import org.jetbrains.annotations.Nullable;
+import org.skriptlang.skript.registration.SyntaxRegistry;
+
+import java.util.UUID;
+
+@Name("Skull Texture")
+@Description("""
+ The skull texture of a player head. This allows you to give a skull a custom texture (e.g. instead of it being a Steve head, it's Notch's head).
+ The texture input is a base64 string containing the texture data to use (https://minecraft-heads.com is one site that provides easy access to base64 texture strings).
+ Resetting the texture of a skull will make it look like a Steve/Alex head.
+ """)
+@Example("set the skull texture of {_i} to \"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTM4NmRmZDc0Y2JhZmJkMWRiZTQ3OWY1ZTAzNzRjMDliZjJlYjRlMzg2NjExZmM0ZmM2OTlmMDJlY2E0ZGQyYyJ9fX0=\"")
+@Since("INSERT VERSION")
+public class ExprSkullTexture extends SimplePropertyExpression {
+
+ public static void register(SyntaxRegistry syntaxRegistry) {
+ syntaxRegistry.register(SyntaxRegistry.EXPRESSION,
+ infoBuilder(ExprSkullTexture.class, String.class, "(skull|head) texture", "itemtypes", false)
+ .supplier(ExprSkullTexture::new)
+ .build());
+ }
+
+ @Override
+ public @Nullable String convert(ItemType item) {
+ if (!(item.getItemMeta() instanceof SkullMeta meta))
+ return null;
+ PlayerProfile profile = meta.getPlayerProfile();
+ if (profile == null)
+ return null;
+ return profile.getProperties().stream()
+ .filter(property -> property.getName().equals("textures"))
+ .findFirst()
+ .map(ProfileProperty::getValue)
+ .orElse(null);
+ }
+
+ @Override
+ public Class> @Nullable [] acceptChange(ChangeMode mode) {
+ return switch (mode) {
+ case SET -> CollectionUtils.array(String.class);
+ case DELETE, RESET -> CollectionUtils.array();
+ default -> null;
+ };
+ }
+
+ @Override
+ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) {
+ PlayerProfile profile = null;
+ if (delta != null) {
+ profile = Bukkit.createProfile(UUID.randomUUID());
+ profile.setProperty(new ProfileProperty("textures", (String) delta[0]));
+ }
+ for (ItemType item : getExpr().getArray(event)) {
+ if (item.getItemMeta() instanceof SkullMeta meta) {
+ meta.setPlayerProfile(profile);
+ item.setItemMeta(meta);
+ }
+ }
+ }
+
+ @Override
+ public Class extends String> getReturnType() {
+ return String.class;
+ }
+
+ @Override
+ protected String getPropertyName() {
+ return "skull texture";
+ }
+
+}
diff --git a/src/test/skript/tests/syntaxes/expressions/ExprSkullTexture.sk b/src/test/skript/tests/syntaxes/expressions/ExprSkullTexture.sk
new file mode 100644
index 000000000..81945125a
--- /dev/null
+++ b/src/test/skript/tests/syntaxes/expressions/ExprSkullTexture.sk
@@ -0,0 +1,6 @@
+test "skull texture":
+ set {_head} to player head
+ set skull texture of {_head} to "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTM4NmRmZDc0Y2JhZmJkMWRiZTQ3OWY1ZTAzNzRjMDliZjJlYjRlMzg2NjExZmM0ZmM2OTlmMDJlY2E0ZGQyYyJ9fX0="
+ assert skull texture of {_head} is "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTM4NmRmZDc0Y2JhZmJkMWRiZTQ3OWY1ZTAzNzRjMDliZjJlYjRlMzg2NjExZmM0ZmM2OTlmMDJlY2E0ZGQyYyJ9fX0=" with "skull texture should be set"
+ reset skull texture of {_head}
+ assert skull texture of {_head} is not set with "skull texture should be reset"
From e900340a83764ba8fb3292d806fcf56a58c9ec88 Mon Sep 17 00:00:00 2001
From: Patrick Miller
Date: Tue, 30 Jun 2026 11:02:26 -0400
Subject: [PATCH 20/41] Fix gradlew.bat line endings again
---
gradlew.bat | 164 ++++++++++++++++++++++++++--------------------------
1 file changed, 82 insertions(+), 82 deletions(-)
diff --git a/gradlew.bat b/gradlew.bat
index a51ec4f58..8508ef684 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,82 +1,82 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-@rem SPDX-License-Identifier: Apache-2.0
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem gradlew startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables, and ensure extensions are enabled
-setlocal EnableExtensions
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-"%COMSPEC%" /c exit 1
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-"%COMSPEC%" /c exit 1
-
-:execute
-@rem Setup the command line
-
-
-
-@rem Execute gradlew
-@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
-@rem which allows us to clear the local environment before executing the java command
-endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
-
-:exitWithErrorLevel
-@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
-"%COMSPEC%" /c exit %ERRORLEVEL%
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem gradlew startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables, and ensure extensions are enabled
+setlocal EnableExtensions
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+"%COMSPEC%" /c exit 1
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+"%COMSPEC%" /c exit 1
+
+:execute
+@rem Setup the command line
+
+
+
+@rem Execute gradlew
+@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
+@rem which allows us to clear the local environment before executing the java command
+endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
+
+:exitWithErrorLevel
+@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
+"%COMSPEC%" /c exit %ERRORLEVEL%
From 12a1fadac89142404745675f810fbd2f538038b1 Mon Sep 17 00:00:00 2001
From: OwlBe <258166039+AnOwlBe@users.noreply.github.com>
Date: Tue, 30 Jun 2026 11:17:39 -0400
Subject: [PATCH 21/41] Entity Pathfind Event & Pathfinding Target Expression
(#8501)
---
.../skript/bukkit/entity/EntityModule.java | 40 ++++++++++
.../expressions/ExprPathfindingLocation.java | 73 +++++++++++++++++++
.../expressions/ExprPathfindingTarget.java | 72 ++++++++++++++++++
3 files changed, 185 insertions(+)
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/entity/elements/expressions/ExprPathfindingLocation.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/entity/elements/expressions/ExprPathfindingTarget.java
diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java b/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java
index 7ee808d52..5632c9c79 100644
--- a/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/EntityModule.java
@@ -2,18 +2,29 @@
import ch.njol.skript.Skript;
import ch.njol.skript.entity.SimpleEntityData;
+import ch.njol.skript.lang.util.SimpleEvent;
+import com.destroystokyo.paper.event.entity.EntityPathfindEvent;
+import io.papermc.paper.event.player.AsyncChatEvent;
+import org.bukkit.Location;
import ch.njol.skript.registrations.Classes;
import org.bukkit.entity.AbstractNautilus;
+import org.bukkit.entity.Entity;
import org.skriptlang.skript.addon.AddonModule;
import org.skriptlang.skript.addon.HierarchicalAddonModule;
import org.skriptlang.skript.addon.SkriptAddon;
import org.skriptlang.skript.bukkit.entity.displays.DisplayModule;
+import org.skriptlang.skript.bukkit.entity.elements.expressions.ExprPathfindingLocation;
+import org.skriptlang.skript.bukkit.entity.elements.expressions.ExprPathfindingTarget;
import org.skriptlang.skript.bukkit.entity.elements.effects.EffTeleport;
import org.skriptlang.skript.bukkit.entity.interactions.InteractionModule;
import org.skriptlang.skript.bukkit.entity.elements.expressions.ExprDeathMessage;
import org.skriptlang.skript.bukkit.entity.entitydata.NautilusData;
import org.skriptlang.skript.bukkit.entity.entitydata.ZombieNautilusData;
import org.skriptlang.skript.bukkit.entity.player.PlayerModule;
+import org.skriptlang.skript.bukkit.lang.eventvalue.EventValue;
+import org.skriptlang.skript.bukkit.lang.eventvalue.EventValueRegistry;
+import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos;
+import org.skriptlang.skript.registration.SyntaxRegistry;
import org.skriptlang.skript.bukkit.entity.types.TeleportFlagClassInfo;
import java.util.List;
@@ -44,8 +55,37 @@ protected void loadSelf(SkriptAddon addon) {
ZombieNautilusData.register();
SimpleEntityData.addSuperEntity("any nautilus", AbstractNautilus.class);
}
+ SyntaxRegistry syntaxRegistry = moduleRegistry(addon);
+ EventValueRegistry registry = addon.registry(EventValueRegistry.class);
+ syntaxRegistry.register(BukkitSyntaxInfos.Event.KEY, BukkitSyntaxInfos.Event.builder(SimpleEvent.class, "Pathfind")
+ .addDescription("Called whenever an entity tries to pathfind to a location or another entity.")
+ .addExample("""
+ on pathfind:
+ broadcast "%event-entity% is about to move to %event-location%!"
+ """)
+ .addSince("INSERT VERSION")
+ .addPattern("[entity] [start[s]] pathfind[ing]")
+ .addEvent(EntityPathfindEvent.class)
+ .build());
+
+ registry.register(EventValue.builder(EntityPathfindEvent.class, Location.class)
+ .getter(EntityPathfindEvent::getLoc)
+ .patterns("target location")
+ .build());
+
+ registry.register(EventValue.builder(EntityPathfindEvent.class, Entity.class)
+ .getter(EntityPathfindEvent::getTargetEntity)
+ .patterns("target entity")
+ .build());
+
+ registry.register(EventValue.builder(EntityPathfindEvent.class, Location.class)
+ .getter(event -> event.getEntity().getLocation())
+ .build());
register(addon,
+ ExprDeathMessage::register,
+ ExprPathfindingLocation::register,
+ ExprPathfindingTarget::register,
EffTeleport::register,
ExprDeathMessage::register
);
diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/elements/expressions/ExprPathfindingLocation.java b/src/main/java/org/skriptlang/skript/bukkit/entity/elements/expressions/ExprPathfindingLocation.java
new file mode 100644
index 000000000..9550ab611
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/elements/expressions/ExprPathfindingLocation.java
@@ -0,0 +1,73 @@
+package org.skriptlang.skript.bukkit.entity.elements.expressions;
+
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Events;
+import ch.njol.skript.doc.Example;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.lang.EventRestrictedSyntax;
+import ch.njol.skript.lang.Expression;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.skript.lang.util.SimpleExpression;
+import ch.njol.util.Kleenean;
+import com.destroystokyo.paper.event.entity.EntityPathfindEvent;
+import org.bukkit.Location;
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.Nullable;
+import org.skriptlang.skript.registration.SyntaxInfo;
+import org.skriptlang.skript.registration.SyntaxRegistry;
+
+@Name("Pathfinding Target Location")
+@Description("The location that the entity is pathfinding towards.")
+@Example("""
+ on pathfind:
+ if the pathfinding target location is within radius 5 of {mylocation}:
+ broadcast "A mob tried to pathfind near a forbidden location!"
+ cancel event
+ """)
+@Since("INSERT VERSION")
+@Events("pathfind")
+public class ExprPathfindingLocation extends SimpleExpression implements EventRestrictedSyntax {
+
+ public static void register(SyntaxRegistry syntaxRegistry) {
+ syntaxRegistry.register(SyntaxRegistry.EXPRESSION, SyntaxInfo.Expression.builder(ExprPathfindingLocation.class, Location.class)
+ .supplier(ExprPathfindingLocation::new)
+ .priority(SyntaxInfo.SIMPLE)
+ .addPattern("[the] path[ ]finding target location")
+ .build());
+ }
+
+ @Override
+ public boolean init(Expression>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
+ return true;
+ }
+
+ @Override
+ public Class extends Event>[] supportedEvents() {
+ return new Class[]{EntityPathfindEvent.class};
+ }
+
+ @Override
+ protected Location[] get(Event event) {
+ if (!(event instanceof EntityPathfindEvent pathfindEvent))
+ return null;
+ Location location = pathfindEvent.getLoc();
+ return new Location[]{location};
+ }
+
+ @Override
+ public boolean isSingle() {
+ return true;
+ }
+
+ @Override
+ public Class extends Location> getReturnType() {
+ return Location.class;
+ }
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ return "the pathfinding location";
+ }
+
+}
diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/elements/expressions/ExprPathfindingTarget.java b/src/main/java/org/skriptlang/skript/bukkit/entity/elements/expressions/ExprPathfindingTarget.java
new file mode 100644
index 000000000..e7b6bb774
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/entity/elements/expressions/ExprPathfindingTarget.java
@@ -0,0 +1,72 @@
+package org.skriptlang.skript.bukkit.entity.elements.expressions;
+
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Events;
+import ch.njol.skript.doc.Example;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.lang.EventRestrictedSyntax;
+import ch.njol.skript.lang.Expression;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.skript.lang.util.SimpleExpression;
+import ch.njol.util.Kleenean;
+import com.destroystokyo.paper.event.entity.EntityPathfindEvent;
+import org.bukkit.entity.Entity;
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.Nullable;
+import org.skriptlang.skript.registration.SyntaxInfo;
+import org.skriptlang.skript.registration.SyntaxRegistry;
+
+@Name("Pathfinding Target Entity")
+@Description("The target entity that another entity is pathfinding towards in a pathfinding event.")
+@Example("""
+ on pathfind:
+ if the pathfinding target entity is a villager:
+ broadcast "I suspect a zombie is going towards a villager right now.."
+ """)
+@Since("INSERT VERSION")
+@Events("pathfind")
+public class ExprPathfindingTarget extends SimpleExpression implements EventRestrictedSyntax {
+
+ public static void register(SyntaxRegistry syntaxRegistry) {
+ syntaxRegistry.register(SyntaxRegistry.EXPRESSION, SyntaxInfo.Expression.builder(ExprPathfindingTarget.class, Entity.class)
+ .supplier(ExprPathfindingTarget::new)
+ .priority(SyntaxInfo.SIMPLE)
+ .addPattern("[the] path[ ]finding target [entity]")
+ .build());
+ }
+
+ @Override
+ public boolean init(Expression>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
+ return true;
+ }
+
+ @Override
+ public Class extends Event>[] supportedEvents() {
+ return new Class[]{EntityPathfindEvent.class};
+ }
+
+ @Override
+ protected Entity[] get(Event event) {
+ if (!(event instanceof EntityPathfindEvent pathfindEvent))
+ return null;
+ Entity target = pathfindEvent.getTargetEntity();
+ return target != null ? new Entity[]{target} : new Entity[0];
+ }
+
+ @Override
+ public boolean isSingle() {
+ return true;
+ }
+
+ @Override
+ public Class extends Entity> getReturnType() {
+ return Entity.class;
+ }
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ return "the pathfinding target";
+ }
+
+}
From cafeb78c247626266ec8a8b0eefaa80b14c41137 Mon Sep 17 00:00:00 2001
From: Ilari Suhonen
Date: Tue, 30 Jun 2026 18:27:54 +0300
Subject: [PATCH 22/41] Modernize enchantments (#8668)
---
.../skript/classes/data/BukkitClasses.java | 38 ----
.../classes/data/DefaultComparators.java | 29 ---
.../classes/data/DefaultConverters.java | 8 -
.../skript/classes/data/SkriptClasses.java | 27 ---
.../expressions/ExprAppliedEnchantments.java | 120 -----------
.../skript/expressions/ExprEnchantItem.java | 116 ----------
.../expressions/ExprEnchantingExpCost.java | 105 ----------
.../expressions/ExprEnchantmentBonus.java | 68 ------
.../expressions/ExprEnchantmentOffer.java | 198 ------------------
src/main/java/ch/njol/util/Math2.java | 26 ++-
.../skript/bukkit/BukkitModule.java | 2 +
.../enchantments/EnchantmentModule.java | 83 ++++++++
.../elements}/conditions/CondIsEnchanted.java | 10 +-
.../conditions/CondItemEnchantmentGlint.java | 49 +++--
.../elements}/effects/EffEnchant.java | 26 ++-
.../effects/EffForceEnchantmentGlint.java | 38 ++--
.../expressions/ExprAppliedEnchantments.java | 120 +++++++++++
.../elements/expressions/ExprEnchantItem.java | 127 +++++++++++
.../expressions/ExprEnchantingExpCost.java | 106 ++++++++++
.../expressions/ExprEnchantmentBonus.java | 71 +++++++
.../expressions/ExprEnchantmentHint.java | 82 ++++++++
.../expressions/ExprEnchantmentLevel.java | 82 ++++----
.../expressions/ExprEnchantmentOffer.java | 192 +++++++++++++++++
.../expressions/ExprEnchantmentOfferCost.java | 64 +++---
.../expressions/ExprEnchantments.java | 62 +++---
.../ExprItemWithEnchantmentGlint.java | 17 +-
.../ExprMinMaxEnchantmentLevel.java | 66 ++++++
.../expressions/ExprStoredEnchantments.java | 146 +++++++++++++
.../types/EnchantmentClassInfo.java | 26 +++
.../types/EnchantmentOfferClassInfo.java | 46 ++++
.../types/EnchantmentTypeClassInfo.java | 46 ++++
.../enchantments/ExprStoredEnchantments.sk | 19 ++
32 files changed, 1329 insertions(+), 886 deletions(-)
delete mode 100644 src/main/java/ch/njol/skript/expressions/ExprAppliedEnchantments.java
delete mode 100644 src/main/java/ch/njol/skript/expressions/ExprEnchantItem.java
delete mode 100644 src/main/java/ch/njol/skript/expressions/ExprEnchantingExpCost.java
delete mode 100644 src/main/java/ch/njol/skript/expressions/ExprEnchantmentBonus.java
delete mode 100644 src/main/java/ch/njol/skript/expressions/ExprEnchantmentOffer.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/EnchantmentModule.java
rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/enchantments/elements}/conditions/CondIsEnchanted.java (84%)
rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/enchantments/elements}/conditions/CondItemEnchantmentGlint.java (53%)
rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/enchantments/elements}/effects/EffEnchant.java (82%)
rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/enchantments/elements}/effects/EffForceEnchantmentGlint.java (64%)
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/elements/expressions/ExprAppliedEnchantments.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/elements/expressions/ExprEnchantItem.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/elements/expressions/ExprEnchantingExpCost.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/elements/expressions/ExprEnchantmentBonus.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/elements/expressions/ExprEnchantmentHint.java
rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/enchantments/elements}/expressions/ExprEnchantmentLevel.java (52%)
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/elements/expressions/ExprEnchantmentOffer.java
rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/enchantments/elements}/expressions/ExprEnchantmentOfferCost.java (56%)
rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/enchantments/elements}/expressions/ExprEnchantments.java (64%)
rename src/main/java/{ch/njol/skript => org/skriptlang/skript/bukkit/enchantments/elements}/expressions/ExprItemWithEnchantmentGlint.java (73%)
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/elements/expressions/ExprMinMaxEnchantmentLevel.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/elements/expressions/ExprStoredEnchantments.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentClassInfo.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentOfferClassInfo.java
create mode 100644 src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentTypeClassInfo.java
create mode 100644 src/test/skript/tests/bukkit/enchantments/ExprStoredEnchantments.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 92468ad71..e45f10841 100644
--- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java
+++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java
@@ -22,8 +22,6 @@
import org.bukkit.block.banner.PatternType;
import org.bukkit.block.data.BlockData;
import org.bukkit.command.CommandSender;
-import org.bukkit.enchantments.Enchantment;
-import org.bukkit.enchantments.EnchantmentOffer;
import org.bukkit.entity.*;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
@@ -422,17 +420,6 @@ public boolean mustSyncDeserialization() {
}
}));
- Classes.registerClass(new RegistryClassInfo<>(Enchantment.class, Registry.ENCHANTMENT, "enchantment", "enchantments")
- .user("enchantments?")
- .name("Enchantment")
- .description("An enchantment, e.g. 'sharpness' or 'fortune'. Unlike enchantment type " +
- "this type has no level, but you usually don't need to use this type anyway.",
- "NOTE: Minecraft namespaces are supported, ex: 'minecraft:basalt_deltas'.",
- "As of Minecraft 1.21 this will also support custom enchantments using namespaces, ex: 'myenchants:explosive'.")
- .examples("")
- .since("1.4.6")
- .before("enchantmenttype"));
-
Material[] allMaterials = Material.values();
Classes.registerClass(new ClassInfo<>(Material.class, "material")
.name(ClassInfo.NO_DOC)
@@ -613,31 +600,6 @@ public String[] getPatterns() {
ExpressionPropertyHandler.of(GameRule::getName, String.class)
));
- Classes.registerClass(new ClassInfo<>(EnchantmentOffer.class, "enchantmentoffer")
- .user("enchant[ment][ ]offers?")
- .name("Enchantment Offer")
- .description("The enchantmentoffer in an enchant prepare event.")
- .examples("on enchant prepare:",
- "\tset enchant offer 1 to sharpness 1",
- "\tset the cost of enchant offer 1 to 10 levels")
- .since("2.5")
- .parser(new Parser<>() {
- @Override
- public boolean canParse(ParseContext context) {
- return false;
- }
-
- @Override
- public String toString(EnchantmentOffer eo, int flags) {
- return Classes.toString(eo.getEnchantment()) + " " + eo.getEnchantmentLevel();
- }
-
- @Override
- public String toVariableNameString(EnchantmentOffer eo) {
- return "offer:" + Classes.toString(eo.getEnchantment()) + "=" + eo.getEnchantmentLevel();
- }
- }));
-
Classes.registerClass(new RegistryClassInfo<>(Attribute.class, Registry.ATTRIBUTE, "attributetype", "attribute types")
.user("attribute ?types?")
.name("Attribute Type")
diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java
index 1fa65336b..9dee9a86b 100644
--- a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java
+++ b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java
@@ -545,35 +545,6 @@ public boolean supportsOrdering() {
}
});
- // EnchantmentOffer Comparators
- // EnchantmentOffer - EnchantmentType
- Comparators.registerComparator(EnchantmentOffer.class, EnchantmentType.class, new Comparator() {
- @Override
- public Relation compare(EnchantmentOffer eo, EnchantmentType et) {
- return Relation.get(eo.getEnchantment() == et.getType() && eo.getEnchantmentLevel() == et.getLevel());
- }
-
- @Override
- public boolean supportsOrdering() {
- return false;
- }
- });
- // EnchantmentOffer - Experience
- Comparators.registerComparator(EnchantmentOffer.class, Experience.class, new Comparator() {
- @Override
- public Relation compare(EnchantmentOffer eo, Experience exp) {
- return Relation.get(eo.getCost() == exp.getXP());
- }
-
- @Override public boolean supportsOrdering() {
- return false;
- }
- });
-
- //EnchantmentType - Enchantment
- Comparators.registerComparator(EnchantmentType.class, Enchantment.class, ((enchantmentType, enchantment) ->
- Relation.get(enchantmentType.getType().equals(enchantment))));
-
Comparators.registerComparator(Inventory.class, InventoryType.class, new Comparator() {
@Override
public Relation compare(Inventory inventory, InventoryType inventoryType) {
diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java
index c32d83fc4..f87f7eb49 100644
--- a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java
+++ b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java
@@ -21,8 +21,6 @@
import org.bukkit.block.DoubleChest;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
-import org.bukkit.enchantments.Enchantment;
-import org.bukkit.enchantments.EnchantmentOffer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntitySnapshot;
import org.bukkit.entity.LivingEntity;
@@ -279,15 +277,9 @@ public void setAmount(Number amount) {
Converter.NO_RIGHT_CHAINING);
}
- // Enchantment - EnchantmentType
- Converters.registerConverter(Enchantment.class, EnchantmentType.class, e -> new EnchantmentType(e, -1));
-
// Vector - Direction
Converters.registerConverter(Vector.class, Direction.class, Direction::new);
- // EnchantmentOffer - EnchantmentType
- Converters.registerConverter(EnchantmentOffer.class, EnchantmentType.class, eo -> new EnchantmentType(eo.getEnchantment(), eo.getEnchantmentLevel()));
-
Converters.registerConverter(String.class, World.class, Bukkit::getWorld);
if (Skript.classExists("org.bukkit.entity.EntitySnapshot"))
diff --git a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java
index 4fd2d2de7..a5e5f69c2 100644
--- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java
+++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java
@@ -382,33 +382,6 @@ public String toVariableNameString(Color color) {
"grow a huge red mushroom above the block")
.since("1.0"));
- Classes.registerClass(new ClassInfo<>(EnchantmentType.class, "enchantmenttype")
- .user("enchant(ing|ment) types?")
- .name("Enchantment Type")
- .description("An enchantment with an optional level, e.g. 'sharpness 2' or 'fortune'.")
- .usage(" []")
- .examples("enchant the player's tool with sharpness 5",
- "helmet is enchanted with waterbreathing")
- .since("1.4.6")
- .parser(new Parser() {
- @Override
- @Nullable
- public EnchantmentType parse(final String s, final ParseContext context) {
- return EnchantmentType.parse(s);
- }
-
- @Override
- public String toString(final EnchantmentType t, final int flags) {
- return t.toString();
- }
-
- @Override
- public String toVariableNameString(final EnchantmentType o) {
- return o.toString();
- }
- })
- .serializer(new YggdrasilSerializer<>()));
-
Classes.registerClass(new ClassInfo<>(Experience.class, "experience")
.user("experience ?(points?)?")
.name("Experience")
diff --git a/src/main/java/ch/njol/skript/expressions/ExprAppliedEnchantments.java b/src/main/java/ch/njol/skript/expressions/ExprAppliedEnchantments.java
deleted file mode 100644
index 85885ddcc..000000000
--- a/src/main/java/ch/njol/skript/expressions/ExprAppliedEnchantments.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package ch.njol.skript.expressions;
-
-import ch.njol.skript.lang.EventRestrictedSyntax;
-import org.bukkit.enchantments.Enchantment;
-import org.bukkit.event.Event;
-import org.bukkit.event.enchantment.EnchantItemEvent;
-import org.jetbrains.annotations.Nullable;
-
-import ch.njol.skript.Skript;
-import ch.njol.skript.classes.Changer.ChangeMode;
-import ch.njol.skript.doc.Description;
-import ch.njol.skript.doc.Events;
-import ch.njol.skript.doc.Example;
-import ch.njol.skript.doc.Name;
-import ch.njol.skript.doc.Since;
-import ch.njol.skript.lang.Expression;
-import ch.njol.skript.lang.ExpressionType;
-import ch.njol.skript.lang.SkriptParser.ParseResult;
-import ch.njol.skript.lang.util.SimpleExpression;
-import ch.njol.skript.log.ErrorQuality;
-import ch.njol.skript.util.EnchantmentType;
-import ch.njol.util.Kleenean;
-import ch.njol.util.coll.CollectionUtils;
-
-@Name("Applied Enchantments")
-@Description({"The applied enchantments in an enchant event.",
- " Deleting or removing the applied enchantments will prevent the item's enchantment."})
-@Example("""
- on enchant:
- set the applied enchantments to sharpness 10 and fire aspect 5
- """)
-@Events("enchant")
-@Since("2.5")
-public class ExprAppliedEnchantments extends SimpleExpression implements EventRestrictedSyntax {
-
- static {
- Skript.registerExpression(ExprAppliedEnchantments.class, EnchantmentType.class, ExpressionType.SIMPLE, "[the] applied enchant[ment]s");
- }
-
- @Override
- public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
- return true;
- }
-
- @Override
- public Class extends Event>[] supportedEvents() {
- return CollectionUtils.array(EnchantItemEvent.class);
- }
-
- @SuppressWarnings("null")
- @Override
- @Nullable
- protected EnchantmentType[] get(Event e) {
- if (!(e instanceof EnchantItemEvent))
- return null;
-
- return ((EnchantItemEvent) e).getEnchantsToAdd().entrySet().stream()
- .map(entry -> new EnchantmentType(entry.getKey(), entry.getValue()))
- .toArray(EnchantmentType[]::new);
- }
-
- @Override
- @Nullable
- public Class>[] acceptChange(ChangeMode mode) {
- if (mode == ChangeMode.REMOVE_ALL || mode == ChangeMode.RESET)
- return null;
- return CollectionUtils.array(Enchantment[].class, EnchantmentType[].class);
- }
-
- @SuppressWarnings("null")
- @Override
- public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
- if (!(event instanceof EnchantItemEvent))
- return;
-
- EnchantmentType[] enchants = new EnchantmentType[delta != null ? delta.length : 0];
- if (delta != null && delta.length != 0) {
- for (int i = 0; i < delta.length; i++) {
- if (delta[i] instanceof EnchantmentType)
- enchants[i] = (EnchantmentType) delta[i];
- else
- enchants[i] = new EnchantmentType((Enchantment) delta[i]);
- }
- }
- EnchantItemEvent e = (EnchantItemEvent) event;
- switch (mode) {
- case SET:
- e.getEnchantsToAdd().clear();
- case ADD:
- for (EnchantmentType enchant : enchants)
- e.getEnchantsToAdd().put(enchant.getType(), enchant.getLevel());
- break;
- case REMOVE:
- for (EnchantmentType enchant : enchants)
- e.getEnchantsToAdd().remove(enchant.getType(), enchant.getLevel());
- break;
- case DELETE:
- e.getEnchantsToAdd().clear();
- case REMOVE_ALL:
- case RESET:
- assert false;
- }
- }
-
- @Override
- public boolean isSingle() {
- return false;
- }
-
- @Override
- public Class extends EnchantmentType> getReturnType() {
- return EnchantmentType.class;
- }
-
- @Override
- public String toString(@Nullable Event e, boolean debug) {
- return "applied enchantments";
- }
-
-}
diff --git a/src/main/java/ch/njol/skript/expressions/ExprEnchantItem.java b/src/main/java/ch/njol/skript/expressions/ExprEnchantItem.java
deleted file mode 100644
index b76aeccad..000000000
--- a/src/main/java/ch/njol/skript/expressions/ExprEnchantItem.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package ch.njol.skript.expressions;
-
-import ch.njol.skript.lang.EventRestrictedSyntax;
-import org.bukkit.event.Event;
-import org.bukkit.event.enchantment.EnchantItemEvent;
-import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
-import org.jetbrains.annotations.Nullable;
-
-import ch.njol.skript.Skript;
-import ch.njol.skript.aliases.ItemType;
-import ch.njol.skript.classes.Changer.ChangeMode;
-import ch.njol.skript.doc.Description;
-import ch.njol.skript.doc.Events;
-import ch.njol.skript.doc.Example;
-import ch.njol.skript.doc.Name;
-import ch.njol.skript.doc.Since;
-import ch.njol.skript.lang.Expression;
-import ch.njol.skript.lang.ExpressionType;
-import ch.njol.skript.lang.SkriptParser.ParseResult;
-import ch.njol.skript.lang.util.SimpleExpression;
-import ch.njol.skript.log.ErrorQuality;
-import ch.njol.util.Kleenean;
-import ch.njol.util.coll.CollectionUtils;
-
-@Name("Enchant Item")
-@Description({"The enchant item in an enchant prepare event or enchant event.",
- "It can be modified, but enchantments will still be applied in the enchant event."})
-@Example("""
- on enchant:
- set the enchanted item to a diamond chestplate
- """)
-@Example("""
- on enchant prepare:
- set the enchant item to a wooden sword
- """)
-@Events({"enchant prepare", "enchant"})
-@Since("2.5")
-public class ExprEnchantItem extends SimpleExpression implements EventRestrictedSyntax {
-
- static {
- Skript.registerExpression(ExprEnchantItem.class, ItemType.class, ExpressionType.SIMPLE, "[the] enchant[ed] item");
- }
-
- @Override
- public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
- return true;
- }
-
- @Override
- public Class extends Event>[] supportedEvents() {
- return CollectionUtils.array(EnchantItemEvent.class, PrepareItemEnchantEvent.class);
- }
-
- @Override
- @Nullable
- protected ItemType[] get(Event e) {
- if (e instanceof PrepareItemEnchantEvent)
- return new ItemType[]{new ItemType(((PrepareItemEnchantEvent) e).getItem())};
- else if (e instanceof EnchantItemEvent)
- return new ItemType[]{new ItemType(((EnchantItemEvent) e).getItem())};
- else
- return null;
- }
-
- @Override
- @Nullable
- public Class>[] acceptChange(ChangeMode mode) {
- if (mode == ChangeMode.SET)
- return CollectionUtils.array(ItemType.class);
- return null;
- }
-
- @Override
- public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
- if (delta == null)
- return;
- ItemType item = ((ItemType) delta[0]);
- switch (mode) {
- case SET:
- if (event instanceof PrepareItemEnchantEvent) {
- PrepareItemEnchantEvent e = (PrepareItemEnchantEvent) event;
- e.getItem().setType(item.getMaterial());
- e.getItem().setItemMeta(item.getItemMeta());
- e.getItem().setAmount(item.getAmount());
- } else if (event instanceof EnchantItemEvent) {
- EnchantItemEvent e = (EnchantItemEvent) event;
- e.getItem().setType(item.getMaterial());
- e.getItem().setItemMeta(item.getItemMeta());
- e.getItem().setAmount(item.getAmount());
- }
- break;
- case ADD:
- case REMOVE:
- case RESET:
- case DELETE:
- case REMOVE_ALL:
- assert false;
- }
- }
-
- @Override
- public boolean isSingle() {
- return true;
- }
-
- @Override
- public Class extends ItemType> getReturnType() {
- return ItemType.class;
- }
-
- @Override
- public String toString(@Nullable Event e, boolean debug) {
- return "enchanted item";
- }
-
-}
diff --git a/src/main/java/ch/njol/skript/expressions/ExprEnchantingExpCost.java b/src/main/java/ch/njol/skript/expressions/ExprEnchantingExpCost.java
deleted file mode 100644
index 97d0d4f2f..000000000
--- a/src/main/java/ch/njol/skript/expressions/ExprEnchantingExpCost.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package ch.njol.skript.expressions;
-
-import ch.njol.skript.lang.EventRestrictedSyntax;
-import org.bukkit.event.Event;
-import org.bukkit.event.enchantment.EnchantItemEvent;
-import org.jetbrains.annotations.Nullable;
-
-import ch.njol.skript.Skript;
-import ch.njol.skript.classes.Changer.ChangeMode;
-import ch.njol.skript.doc.Description;
-import ch.njol.skript.doc.Events;
-import ch.njol.skript.doc.Example;
-import ch.njol.skript.doc.Name;
-import ch.njol.skript.doc.Since;
-import ch.njol.skript.lang.Expression;
-import ch.njol.skript.lang.ExpressionType;
-import ch.njol.skript.lang.SkriptParser.ParseResult;
-import ch.njol.skript.lang.util.SimpleExpression;
-import ch.njol.skript.log.ErrorQuality;
-import ch.njol.skript.util.Experience;
-import ch.njol.util.Kleenean;
-import ch.njol.util.coll.CollectionUtils;
-
-@Name("Enchanting Experience Cost")
-@Description({"The cost of enchanting in an enchant event.",
- "This is number that was displayed in the enchantment table, not the actual number of levels removed."})
-@Example("""
- on enchant:
- send "Cost: %the displayed enchanting cost%" to player
- """)
-@Events("enchant")
-@Since("2.5")
-public class ExprEnchantingExpCost extends SimpleExpression implements EventRestrictedSyntax {
-
- static {
- Skript.registerExpression(ExprEnchantingExpCost.class, Long.class, ExpressionType.SIMPLE,
- "[the] [displayed] ([e]xp[erience]|enchanting) cost");
- }
-
- @Override
- public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
- return true;
- }
-
- @Override
- public Class extends Event>[] supportedEvents() {
- return CollectionUtils.array(EnchantItemEvent.class);
- }
-
- @Override
- @Nullable
- protected Long[] get(Event e) {
- return new Long[]{(long) ((EnchantItemEvent) e).getExpLevelCost()};
- }
-
- @Override
- @Nullable
- public Class>[] acceptChange(ChangeMode mode) {
- if (mode == ChangeMode.RESET || mode == ChangeMode.DELETE || mode == ChangeMode.REMOVE_ALL)
- return null;
- return CollectionUtils.array(Number.class, Experience.class);
- }
-
- @Override
- public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
- if (delta == null)
- return;
- Object c = delta[0];
- int cost = c instanceof Number ? ((Number) c).intValue() : ((Experience) c).getXP();
- EnchantItemEvent e = (EnchantItemEvent) event;
- switch (mode) {
- case SET:
- e.setExpLevelCost(cost);
- break;
- case ADD:
- int add = e.getExpLevelCost() + cost;
- e.setExpLevelCost(add);
- break;
- case REMOVE:
- int subtract = e.getExpLevelCost() - cost;
- e.setExpLevelCost(subtract);
- break;
- case RESET:
- case DELETE:
- case REMOVE_ALL:
- assert false;
- }
- }
-
- @Override
- public boolean isSingle() {
- return true;
- }
-
- @Override
- public Class extends Long> getReturnType() {
- return Long.class;
- }
-
- @Override
- public String toString(@Nullable Event e, boolean debug) {
- return "the displayed cost of enchanting";
- }
-
-}
diff --git a/src/main/java/ch/njol/skript/expressions/ExprEnchantmentBonus.java b/src/main/java/ch/njol/skript/expressions/ExprEnchantmentBonus.java
deleted file mode 100644
index 42b53e166..000000000
--- a/src/main/java/ch/njol/skript/expressions/ExprEnchantmentBonus.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package ch.njol.skript.expressions;
-
-import ch.njol.skript.lang.EventRestrictedSyntax;
-import ch.njol.util.coll.CollectionUtils;
-import org.bukkit.event.Event;
-import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
-import org.jetbrains.annotations.Nullable;
-
-import ch.njol.skript.Skript;
-import ch.njol.skript.doc.Description;
-import ch.njol.skript.doc.Events;
-import ch.njol.skript.doc.Example;
-import ch.njol.skript.doc.Name;
-import ch.njol.skript.doc.Since;
-import ch.njol.skript.lang.Expression;
-import ch.njol.skript.lang.ExpressionType;
-import ch.njol.skript.lang.SkriptParser.ParseResult;
-import ch.njol.skript.lang.util.SimpleExpression;
-import ch.njol.skript.log.ErrorQuality;
-import ch.njol.util.Kleenean;
-
-@Name("Enchantment Bonus")
-@Description("The enchantment bonus in an enchant prepare event. This represents the number of bookshelves affecting/surrounding the enchantment table.")
-@Example("""
- on enchant:
- send "There are %enchantment bonus% bookshelves surrounding this enchantment table!" to player
- """)
-@Events("enchant prepare")
-@Since("2.5")
-public class ExprEnchantmentBonus extends SimpleExpression implements EventRestrictedSyntax {
-
- static {
- Skript.registerExpression(ExprEnchantmentBonus.class, Long.class, ExpressionType.SIMPLE, "[the] enchantment bonus");
- }
-
- @Override
- public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
- return true;
- }
-
- @Override
- public Class extends Event>[] supportedEvents() {
- return CollectionUtils.array(PrepareItemEnchantEvent.class);
- }
-
- @Override
- @Nullable
- protected Long[] get(Event e) {
- return new Long[]{(long) ((PrepareItemEnchantEvent) e).getEnchantmentBonus()};
- }
-
- @Override
- public boolean isSingle() {
- return true;
- }
-
-
- @Override
- public Class extends Long> getReturnType() {
- return Long.class;
- }
-
- @Override
- public String toString(@Nullable Event e, boolean debug) {
- return "enchantment bonus";
- }
-
-}
diff --git a/src/main/java/ch/njol/skript/expressions/ExprEnchantmentOffer.java b/src/main/java/ch/njol/skript/expressions/ExprEnchantmentOffer.java
deleted file mode 100644
index d574d0c8e..000000000
--- a/src/main/java/ch/njol/skript/expressions/ExprEnchantmentOffer.java
+++ /dev/null
@@ -1,198 +0,0 @@
-package ch.njol.skript.expressions;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-
-import ch.njol.skript.lang.EventRestrictedSyntax;
-import org.bukkit.enchantments.EnchantmentOffer;
-import org.bukkit.event.Event;
-import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
-import org.jetbrains.annotations.Nullable;
-
-import ch.njol.skript.Skript;
-import ch.njol.skript.classes.Changer.ChangeMode;
-import ch.njol.skript.doc.Description;
-import ch.njol.skript.doc.Events;
-import ch.njol.skript.doc.Example;
-import ch.njol.skript.doc.Name;
-import ch.njol.skript.doc.RequiredPlugins;
-import ch.njol.skript.doc.Since;
-import ch.njol.skript.lang.Expression;
-import ch.njol.skript.lang.ExpressionType;
-import ch.njol.skript.lang.SkriptParser.ParseResult;
-import ch.njol.skript.lang.util.SimpleExpression;
-import ch.njol.skript.log.ErrorQuality;
-import ch.njol.skript.util.EnchantmentType;
-import ch.njol.util.Kleenean;
-import ch.njol.util.coll.CollectionUtils;
-
-@Name("Enchantment Offer")
-@Description("The enchantment offer in enchant prepare events.")
-@Example("""
- on enchant prepare:
- send "Your enchantment offers are: %the enchantment offers%" to player
- """)
-@Since("2.5")
-@Events("enchant prepare")
-@RequiredPlugins("1.11 or newer")
-public class ExprEnchantmentOffer extends SimpleExpression implements EventRestrictedSyntax {
-
- static {
- if (Skript.classExists("org.bukkit.enchantments.EnchantmentOffer")) {
- Skript.registerExpression(ExprEnchantmentOffer.class, EnchantmentOffer.class, ExpressionType.SIMPLE,
- "[all [of]] [the] enchant[ment] offers",
- "enchant[ment] offer[s] %numbers%",
- "[the] %number%(st|nd|rd|th) enchant[ment] offer");
- }
- }
-
- @SuppressWarnings("null")
- private Expression exprOfferNumber;
-
- private boolean all;
-
- // Used for getCost()
- private final Random rand = new Random();
-
- @SuppressWarnings({"null", "unchecked"})
- @Override
- public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
- if (matchedPattern == 0) {
- all = true;
- } else {
- exprOfferNumber = (Expression) exprs[0];
- all = false;
- }
- return true;
- }
-
- @Override
- public Class extends Event>[] supportedEvents() {
- return CollectionUtils.array(PrepareItemEnchantEvent.class);
- }
-
- @SuppressWarnings({"null", "unused"})
- @Override
- @Nullable
- protected EnchantmentOffer[] get(Event e) {
- if (!(e instanceof PrepareItemEnchantEvent))
- return null;
-
- if (all)
- return ((PrepareItemEnchantEvent) e).getOffers();
- if (exprOfferNumber == null)
- return new EnchantmentOffer[0];
- if (exprOfferNumber.isSingle()) {
- Number offerNumber = exprOfferNumber.getSingle(e);
- if (offerNumber == null)
- return new EnchantmentOffer[0];
- int offer = offerNumber.intValue();
- if (offer < 1 || offer > ((PrepareItemEnchantEvent) e).getOffers().length)
- return new EnchantmentOffer[0];
- return new EnchantmentOffer[]{((PrepareItemEnchantEvent) e).getOffers()[offer - 1]};
- }
- List offers = new ArrayList<>();
- int i;
- for (Number n : exprOfferNumber.getArray(e)) {
- i = n.intValue();
- if (i >= 1 || i <= ((PrepareItemEnchantEvent) e).getOffers().length)
- offers.add(((PrepareItemEnchantEvent) e).getOffers()[i - 1]);
- }
- return offers.toArray(new EnchantmentOffer[0]);
- }
-
- @Override
- @Nullable
- public Class>[] acceptChange(ChangeMode mode) {
- if (mode == ChangeMode.SET || mode == ChangeMode.DELETE)
- return CollectionUtils.array(EnchantmentType.class);
- return null;
- }
-
- @SuppressWarnings("null")
- @Override
- public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
- if (delta == null && mode != ChangeMode.DELETE)
- return;
- EnchantmentType et = mode != ChangeMode.DELETE ? (EnchantmentType) delta[0] : null;
- if (event instanceof PrepareItemEnchantEvent) {
- PrepareItemEnchantEvent e = (PrepareItemEnchantEvent) event;
- switch (mode) {
- case SET:
- if (all) {
- for (int i = 0; i <= 2; i++) {
- EnchantmentOffer eo = e.getOffers()[i];
- if (eo == null) {
- eo = new EnchantmentOffer(et.getType(), et.getLevel(), getCost(i + 1, e.getEnchantmentBonus()));
- e.getOffers()[i] = eo;
- } else {
- eo.setEnchantment(et.getType());
- eo.setEnchantmentLevel(et.getLevel());
- }
- }
- } else {
- for (Number n : exprOfferNumber.getArray(e)) {
- int slot = n.intValue() - 1;
- EnchantmentOffer eo = e.getOffers()[slot];
- if (eo == null) {
- eo = new EnchantmentOffer(et.getType(), et.getLevel(), getCost(slot + 1, e.getEnchantmentBonus()));
- e.getOffers()[slot] = eo;
- } else {
- eo.setEnchantment(et.getType());
- eo.setEnchantmentLevel(et.getLevel());
- }
- }
- }
- break;
- case DELETE:
- if (all) {
- Arrays.fill(e.getOffers(), null);
- } else {
- for (Number n : exprOfferNumber.getArray(e))
- e.getOffers()[n.intValue() - 1] = null;
- }
- break;
- case ADD:
- case REMOVE:
- case RESET:
- case REMOVE_ALL:
- assert false;
- }
- }
- }
-
- @Override
- public boolean isSingle() {
- return !all && exprOfferNumber.isSingle();
- }
-
- @Override
- public Class extends EnchantmentOffer> getReturnType() {
- return EnchantmentOffer.class;
- }
-
- @Override
- public String toString(@Nullable Event e, boolean debug) {
- return all ? "the enchantment offers" : "enchantment offer(s) " + exprOfferNumber.toString(e, debug);
- }
-
- /**
- * Returns an enchantment cost from an enchantment button and number of bookshelves.
- * @param slot The enchantment button slot (1, 2, or 3).
- * @param bookshelves The number of bookshelves around the enchantment table.
- * @return A cost for that enchantment button with the number of bookshelves, or 1 if 'slot' is not an integer from 1 to 3.
- */
- public int getCost(int slot, int bookshelves) {
- // (from 1 to 8) + floor(bookshelves / 2) + (from 0 to bookshelves)
- int base = (int) ((rand.nextInt(7) + 1) + Math.floor(bookshelves / 2) + (rand.nextInt(bookshelves + 1)));
- switch (slot) {
- case 1: return Math.max(base / 3, 1);
- case 2: return (base * 2) / 3 + 1;
- case 3: return Math.max(base, bookshelves * 2);
- default: return 1;
- }
- }
-
-}
diff --git a/src/main/java/ch/njol/util/Math2.java b/src/main/java/ch/njol/util/Math2.java
index 6029be120..82091e4cc 100644
--- a/src/main/java/ch/njol/util/Math2.java
+++ b/src/main/java/ch/njol/util/Math2.java
@@ -1,11 +1,8 @@
package ch.njol.util;
import ch.njol.skript.Skript;
-import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval;
import org.jetbrains.annotations.ApiStatus;
-import java.util.Arrays;
-
/**
* This class is not to be used by addons. In the future methods may
* change signature, contract and/or get removed without warning.
@@ -152,6 +149,29 @@ public static long addClamped(long x, long y) {
return result;
}
+ /**
+ * @param x the first value
+ * @param y the second value
+ * @return the sum of x and y, or one of {@link Long#MIN_VALUE} or {@link Long#MAX_VALUE} in case of an overflow,
+ * whichever is closer to the real result
+ * */
+ public static long addSaturated(long x, long y) {
+ long result = x + y;
+ boolean causedOverflow = ((x ^ result) & (y ^ result)) < 0;
+ if (causedOverflow)
+ return x < 0 && y < 0 ? Long.MIN_VALUE : Long.MAX_VALUE;
+ return result;
+ }
+
+ /**
+ * Clamps a long to fit into the representable range for integers
+ * @return the value if it is between {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}, otherwise
+ * whichever of the two is closest to the value
+ * */
+ public static int clampToInt(long value) {
+ return Math.clamp(value, Integer.MIN_VALUE, Integer.MAX_VALUE);
+ }
+
public static long multiplyClamped(long x, long y) {
long result = x * y;
long ax = Math.abs(x);
diff --git a/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java
index b1e42eadc..63a9617fa 100644
--- a/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java
+++ b/src/main/java/org/skriptlang/skript/bukkit/BukkitModule.java
@@ -9,6 +9,7 @@
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.enchantments.EnchantmentModule;
import org.skriptlang.skript.bukkit.entity.EntityModule;
import org.skriptlang.skript.bukkit.fishing.FishingModule;
import org.skriptlang.skript.bukkit.input.InputModule;
@@ -39,6 +40,7 @@ public Iterable