diff --git a/src/main/java/ch/njol/skript/expressions/ExprVehicle.java b/src/main/java/ch/njol/skript/expressions/ExprVehicle.java
index b45da85eedd..443943b8f44 100644
--- a/src/main/java/ch/njol/skript/expressions/ExprVehicle.java
+++ b/src/main/java/ch/njol/skript/expressions/ExprVehicle.java
@@ -1,9 +1,10 @@
package ch.njol.skript.expressions;
+import java.util.function.Predicate;
+
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.event.entity.EntityDismountEvent;
-import org.bukkit.event.entity.EntityEvent;
import org.bukkit.event.entity.EntityMountEvent;
import org.bukkit.event.vehicle.VehicleEnterEvent;
import org.bukkit.event.vehicle.VehicleExitEvent;
@@ -12,175 +13,139 @@
import ch.njol.skript.Skript;
import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.doc.Description;
-import ch.njol.skript.doc.Examples;
+import ch.njol.skript.doc.Example;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
-import ch.njol.skript.effects.Delay;
import ch.njol.skript.entity.EntityData;
-import ch.njol.skript.expressions.base.SimplePropertyExpression;
-import ch.njol.util.coll.CollectionUtils;
+import ch.njol.skript.expressions.base.PropertyExpression;
+import ch.njol.skript.lang.Expression;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.skript.registrations.EventValues;
+import ch.njol.util.Kleenean;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
+import org.bukkit.entity.Player;
-/**
- * @author Peter Güttinger
- */
@Name("Vehicle")
-@Description({"The vehicle an entity is in, if any. This can actually be any entity, e.g. spider jockeys are skeletons that ride on a spider, so the spider is the 'vehicle' of the skeleton.",
- "See also: passenger"})
-@Examples({"vehicle of the player is a minecart"})
+@Description({
+ "The vehicle an entity is in, if any.",
+ "This can actually be any entity, e.g. spider jockeys are skeletons that ride on a spider, so the spider is the 'vehicle' of the skeleton.",
+ "See also: passenger"
+})
+@Example("""
+ set the vehicle of {game::players::*} to a saddled pig
+ give {game::players::*} a carrot on a stick
+""")
+@Example("""
+ on vehicle enter:
+ vehicle is a horse
+ add 1 to {statistics::horseMounting::%uuid of player%}
+""")
@Since("2.0")
-public class ExprVehicle extends SimplePropertyExpression {
-
- private static final boolean HAS_NEW_MOUNT_EVENTS = Skript.classExists("org.bukkit.event.entity.EntityMountEvent");
-
- private static final boolean HAS_OLD_MOUNT_EVENTS;
- @Nullable
- private static final Class> OLD_MOUNT_EVENT_CLASS;
- @Nullable
- private static final MethodHandle OLD_GETMOUNT_HANDLE;
- @Nullable
- private static final Class> OLD_DISMOUNT_EVENT_CLASS;
- @Nullable
- private static final MethodHandle OLD_GETDISMOUNTED_HANDLE;
+public class ExprVehicle extends PropertyExpression {
static {
- register(ExprVehicle.class, Entity.class, "vehicle[s]", "entities");
-
- // legacy support
- boolean hasOldMountEvents = !HAS_NEW_MOUNT_EVENTS &&
- Skript.classExists("org.spigotmc.event.entity.EntityMountEvent");
- Class> oldMountEventClass = null;
- MethodHandle oldGetMountHandle = null;
- Class> oldDismountEventClass = null;
- MethodHandle oldGetDismountedHandle = null;
- if (hasOldMountEvents) {
- try {
- MethodHandles.Lookup lookup = MethodHandles.lookup();
- MethodType entityReturnType = MethodType.methodType(Entity.class);
- // mount event
- oldMountEventClass = Class.forName("org.spigotmc.event.entity.EntityMountEvent");
- oldGetMountHandle = lookup.findVirtual(oldMountEventClass, "getMount", entityReturnType);
- // dismount event
- oldDismountEventClass = Class.forName("org.spigotmc.event.entity.EntityDismountEvent");
- oldGetDismountedHandle = lookup.findVirtual(oldDismountEventClass, "getDismounted", entityReturnType);
- } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException e) {
- hasOldMountEvents = false;
- oldMountEventClass = null;
- oldGetMountHandle = null;
- oldDismountEventClass = null;
- oldGetDismountedHandle = null;
- Skript.exception(e, "Failed to load old mount event support.");
- }
- }
- HAS_OLD_MOUNT_EVENTS = hasOldMountEvents;
- OLD_MOUNT_EVENT_CLASS = oldMountEventClass;
- OLD_GETMOUNT_HANDLE = oldGetMountHandle;
- OLD_DISMOUNT_EVENT_CLASS = oldDismountEventClass;
- OLD_GETDISMOUNTED_HANDLE = oldGetDismountedHandle;
- }
-
- @Override
- protected Entity[] get(final Event e, final Entity[] source) {
- return get(source, entity -> {
- if (getTime() >= 0 && e instanceof VehicleEnterEvent && entity.equals(((VehicleEnterEvent) e).getEntered()) && !Delay.isDelayed(e)) {
- return ((VehicleEnterEvent) e).getVehicle();
- }
- if (getTime() >= 0 && e instanceof VehicleExitEvent && entity.equals(((VehicleExitEvent) e).getExited()) && !Delay.isDelayed(e)) {
- return ((VehicleExitEvent) e).getVehicle();
- }
- if (
- (HAS_OLD_MOUNT_EVENTS || HAS_NEW_MOUNT_EVENTS)
- && getTime() >= 0 && !Delay.isDelayed(e)
- && e instanceof EntityEvent && entity.equals(((EntityEvent) e).getEntity())
- ) {
- if (HAS_NEW_MOUNT_EVENTS) {
- if (e instanceof EntityMountEvent)
- return ((EntityMountEvent) e).getMount();
- if (e instanceof EntityDismountEvent)
- return ((EntityDismountEvent) e).getDismounted();
- } else { // legacy mount event support
- try {
- assert OLD_MOUNT_EVENT_CLASS != null;
- if (OLD_MOUNT_EVENT_CLASS.isInstance(e)) {
- assert OLD_GETMOUNT_HANDLE != null;
- return (Entity) OLD_GETMOUNT_HANDLE.invoke(e);
- }
- assert OLD_DISMOUNT_EVENT_CLASS != null;
- if (OLD_DISMOUNT_EVENT_CLASS.isInstance(e)) {
- assert OLD_GETDISMOUNTED_HANDLE != null;
- return (Entity) OLD_GETDISMOUNTED_HANDLE.invoke(e);
- }
- } catch (Throwable ex) {
- Skript.exception(ex, "An error occurred while trying to invoke legacy mount event support.");
- }
- }
- }
- return entity.getVehicle();
- });
+ if (Skript.classExists("org.bukkit.event.entity.EntityMountEvent"))
+ registerDefault(ExprVehicle.class, Entity.class, "vehicle[s]", "entities");
}
-
- @Override
- @Nullable
- public Entity convert(final Entity e) {
- assert false;
- return e.getVehicle();
- }
-
+
@Override
- public Class extends Entity> getReturnType() {
- return Entity.class;
+ @SuppressWarnings("unchecked")
+ public boolean init(Expression>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
+ setExpr((Expression) expressions[0]);
+ return true;
}
-
+
@Override
- protected String getPropertyName() {
- return "vehicle";
+ protected Entity[] get(Event event, Entity[] source) {
+ if (event instanceof EntityDismountEvent entityDismountEvent && getTime() != EventValues.TIME_FUTURE) {
+ return get(source, e -> e.equals(entityDismountEvent.getEntity()) ? entityDismountEvent.getDismounted() : e.getVehicle());
+ } else if (event instanceof VehicleEnterEvent vehicleEnterEvent && getTime() != EventValues.TIME_PAST) {
+ return get(source, e -> e.equals(vehicleEnterEvent.getEntered()) ? vehicleEnterEvent.getVehicle() : e.getVehicle());
+ } else if (event instanceof VehicleExitEvent vehicleExitEvent && getTime() != EventValues.TIME_FUTURE) {
+ return get(source, e -> e.equals(vehicleExitEvent.getExited()) ? vehicleExitEvent.getVehicle() : e.getVehicle());
+ } else if (event instanceof EntityMountEvent entityMountEvent && getTime() != EventValues.TIME_PAST) {
+ return get(source, e -> e.equals(entityMountEvent.getEntity()) ? entityMountEvent.getMount() : e.getVehicle());
+ } else {
+ return get(source, Entity::getVehicle);
+ }
}
-
+
@Override
- @Nullable
- public Class>[] acceptChange(final ChangeMode mode) {
+ public Class> @Nullable [] acceptChange(ChangeMode mode) {
if (mode == ChangeMode.SET) {
+ if (isDefault() && getParser().isCurrentEvent(VehicleExitEvent.class, EntityDismountEvent.class)) {
+ Skript.error("Setting the vehicle during a dismount/exit vehicle event will create an infinite mounting loop.");
+ return null;
+ }
return new Class[] {Entity.class, EntityData.class};
}
return super.acceptChange(mode);
}
-
+
@Override
- public void change(final Event e, final @Nullable Object[] delta, final ChangeMode mode) {
+ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
if (mode == ChangeMode.SET) {
- assert delta != null;
- final Entity[] ps = getExpr().getArray(e);
- if (ps.length == 0)
+ // The player can desync if setting an entity as it's currently mounting it.
+ // Remember that there can be other entity types aside from players, so only cancel this for players.
+ Predicate predicate = Player.class::isInstance;
+ if (event instanceof EntityMountEvent entityMountEvent && predicate.test(entityMountEvent.getEntity())) {
+ return;
+ }
+ if (event instanceof VehicleEnterEvent vehicleEnterEvent && predicate.test(vehicleEnterEvent.getEntered())) {
+ return;
+ }
+ Entity[] passengers = getExpr().getArray(event);
+ if (passengers.length == 0)
return;
- final Object o = delta[0];
- if (o instanceof Entity) {
- ((Entity) o).eject();
- final Entity p = CollectionUtils.getRandom(ps);
- assert p != null;
- p.leaveVehicle();
- ((Entity) o).setPassenger(p);
- } else if (o instanceof EntityData) {
- for (final Entity p : ps) {
- final Entity v = ((EntityData>) o).spawn(p.getLocation());
- if (v == null)
+ assert delta != null;
+ Object object = delta[0];
+ if (object instanceof Entity entity) {
+ entity.eject();
+ for (Entity passenger : passengers) {
+ // Avoid infinity mounting
+ if (event instanceof VehicleExitEvent && predicate.test(passenger) && passenger.equals(((VehicleExitEvent) event).getExited()))
+ continue;
+ if (event instanceof EntityDismountEvent && predicate.test(passenger) && passenger.equals(((EntityDismountEvent) event).getEntity()))
+ continue;
+ assert passenger != null;
+ passenger.leaveVehicle();
+ entity.addPassenger(passenger);
+ }
+ } else if (object instanceof EntityData entityData) {
+ VehicleExitEvent vehicleExitEvent = event instanceof VehicleExitEvent ? (VehicleExitEvent) event : null;
+ EntityDismountEvent entityDismountEvent = event instanceof EntityDismountEvent ? (EntityDismountEvent) event : null;
+ for (Entity passenger : passengers) {
+ // Avoid infinity mounting
+ if (vehicleExitEvent != null && predicate.test(passenger) && passenger.equals(vehicleExitEvent.getExited()))
+ continue;
+ if (entityDismountEvent != null && predicate.test(passenger) && passenger.equals(entityDismountEvent.getEntity()))
+ continue;
+ Entity vehicle = entityData.spawn(passenger.getLocation());
+ if (vehicle == null)
continue;
- v.setPassenger(p);
+ vehicle.addPassenger(passenger);
}
} else {
assert false;
}
} else {
- super.change(e, delta, mode);
+ super.change(event, delta, mode);
}
}
-
- @SuppressWarnings("unchecked")
+
@Override
- public boolean setTime(final int time) {
- return super.setTime(time, getExpr(), VehicleEnterEvent.class, VehicleExitEvent.class);
+ public boolean setTime(int time) {
+ return super.setTime(time, getExpr(), VehicleEnterEvent.class, VehicleExitEvent.class, EntityMountEvent.class, EntityDismountEvent.class);
}
-
+
+ @Override
+ public Class extends Entity> getReturnType() {
+ return Entity.class;
+ }
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ return "vehicle of " + getExpr().toString(event, debug);
+ }
+
}