Skip to content
Merged
Changes from 19 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
07025a1
Fix vehicle
TheLimeGlass Mar 11, 2024
5f9001e
Update src/main/java/ch/njol/skript/expressions/ExprVehicle.java
TheLimeGlass Mar 23, 2024
728cb8f
Re-add delays
TheLimeGlass Mar 23, 2024
0873fce
Merge branch 'fix/vehicle-register' of https://github.com/TheLimeGlas…
TheLimeGlass Mar 23, 2024
6949abd
Fix suggestion commit
TheLimeGlass Mar 23, 2024
2e00061
Merge branch 'dev/patch' into fix/vehicle-register
Moderocky Apr 8, 2024
a99038d
Time state changes
TheLimeGlass Jul 17, 2024
d84617b
Merge branch 'fix/vehicle-register' of https://github.com/TheLimeGlas…
TheLimeGlass Jul 17, 2024
a39e1b8
Avoid infinite mounting
TheLimeGlass Jul 17, 2024
e8999ec
Avoid infinite mounting
TheLimeGlass Jul 17, 2024
c404493
Better examples
TheLimeGlass Jul 17, 2024
9935ed1
Merge branch 'dev/feature' into fix/vehicle-register
Moderocky Aug 15, 2024
47b2a99
Update src/main/java/ch/njol/skript/expressions/ExprVehicle.java
TheLimeGlass Aug 18, 2024
306e3b9
Use Java 17
TheLimeGlass Aug 18, 2024
b4a49ee
Update to latest
TheLimeGlass Jul 29, 2025
07111a1
git conflicts
TheLimeGlass Jul 29, 2025
756381e
Update src/main/java/ch/njol/skript/expressions/ExprVehicle.java
TheLimeGlass Aug 11, 2025
aa6679a
Update src/main/java/ch/njol/skript/expressions/ExprVehicle.java
TheLimeGlass Aug 11, 2025
baad7e5
Requested changes
TheLimeGlass Aug 11, 2025
e10e431
Update src/main/java/ch/njol/skript/expressions/ExprVehicle.java
TheLimeGlass Aug 11, 2025
5131dc4
Update src/main/java/ch/njol/skript/expressions/ExprVehicle.java
TheLimeGlass Aug 11, 2025
c9a2355
requested changes
TheLimeGlass Aug 12, 2025
ecefec5
Merge branch 'fix/vehicle-register' of https://github.com/TheLimeGlas…
TheLimeGlass Aug 12, 2025
72f1462
Update src/main/java/ch/njol/skript/expressions/ExprVehicle.java
TheLimeGlass Sep 23, 2025
a21b6ac
Update src/main/java/ch/njol/skript/expressions/ExprVehicle.java
sovdeeth Sep 23, 2025
85df133
Merge branch 'dev/patch' into fix/vehicle-register
sovdeeth Oct 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 100 additions & 133 deletions src/main/java/ch/njol/skript/expressions/ExprVehicle.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,172 +16,138 @@
import ch.njol.skript.doc.Examples;
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.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: <a href='#ExprPassenger'>passenger</a>"})
@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: <a href='#ExprPassenger'>passenger</a>"
})
@Examples({
"set the vehicle of {game::players::*} to a saddled pig",
"give {game::players::*} a carrot on a stick",
"",
"on vehicle enter:",
"\tvehicle is a horse",
"\tadd 1 to {statistics::horseMounting::%uuid of player%}",
})
@Since("2.0")
public class ExprVehicle extends SimplePropertyExpression<Entity, Entity> {

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<Entity, Entity> {

static {
register(ExprVehicle.class, Entity.class, "vehicle[s]", "entities");
if (Skript.classExists("org.bukkit.event.entity.EntityMountEvent"))
registerDefault(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
@SuppressWarnings("unchecked")
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
setExpr((Expression<Entity>) expressions[0]);
return true;
}

@Override
protected Entity[] get(final Event e, final Entity[] source) {
protected Entity[] get(Event event, 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 && event instanceof VehicleEnterEvent vehicleEnterEvent && entity.equals(vehicleEnterEvent.getEntered())) {
return vehicleEnterEvent.getVehicle();
}
if (getTime() >= 0 && e instanceof VehicleExitEvent && entity.equals(((VehicleExitEvent) e).getExited()) && !Delay.isDelayed(e)) {
return ((VehicleExitEvent) e).getVehicle();
if (getTime() <= 0 && event instanceof VehicleExitEvent vehicleExitEvent && entity.equals(vehicleExitEvent.getExited())) {
return vehicleExitEvent.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.");
}
}
if (getTime() >= 0 && event instanceof EntityMountEvent entityMountEvent && entity.equals(entityMountEvent.getEntity())) {
return entityMountEvent.getMount();
}
if (getTime() <= 0 && event instanceof EntityDismountEvent entityDismountEvent && entity.equals(entityDismountEvent.getEntity())) {
return entityDismountEvent.getDismounted();
}
return entity.getVehicle();
});
}

@Override
@Nullable
public Entity convert(final Entity e) {
assert false;
return e.getVehicle();
}

@Override
public Class<? extends Entity> getReturnType() {
return Entity.class;
}

@Override
protected String getPropertyName() {
return "vehicle";
}


@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<Entity> predicate = Player.class::isInstance;
if (event instanceof EntityMountEvent entityMountEvent && predicate.test(entityMountEvent.getEntity())) {
return;
}
if (event instanceof VehicleEnterEvent vehicleEnterEvent && predicate.test(vehicleEnterEvent.getEntered())) {
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)
}
Entity[] passengers = getExpr().getArray(event);
if (passengers.length == 0)
return;
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);
}

}