Skip to content

EffSecShoot Fix Sections/Consumers #7829

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: dev/patch
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
78 changes: 38 additions & 40 deletions src/main/java/ch/njol/skript/sections/EffSecShoot.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import ch.njol.skript.variables.Variables;
import ch.njol.util.Kleenean;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Fireball;
import org.bukkit.entity.LivingEntity;
Expand All @@ -42,12 +43,13 @@
@Since("2.10")
public class EffSecShoot extends EffectSection {

//TODO: Remove reflect method once 1.19 is no longer supported

// '#shootHandlers' should only return an Entity when there is no 'Trigger' / Consumer
// Returning an Entity allows the velocity and 'lastSpawned' to be set
// Consumers set velocity and 'lastSpawned'
private enum CaseUsage {
NOT_PROJECTILE_NO_TRIGGER {
@Override
public @Nullable Entity shootHandler(EntityData<?> entityData, LivingEntity shooter, Location location, Class<? extends Entity> type, Vector vector, Consumer<?> consumer) {
public Entity shootHandler(EntityData<?> entityData, LivingEntity shooter, Location location, Class<? extends Entity> type, Vector vector, Consumer<?> consumer) {
return entityData.spawn(location);
}
},
Expand All @@ -61,7 +63,7 @@ private enum CaseUsage {
},
PROJECTILE_NO_WORLD_NO_TRIGGER {
@Override
public @NotNull Entity shootHandler(EntityData<?> entityData, LivingEntity shooter, Location location, Class<? extends Entity> type, Vector vector, Consumer<?> consumer) {
public Entity shootHandler(EntityData<?> entityData, LivingEntity shooter, Location location, Class<? extends Entity> type, Vector vector, Consumer<?> consumer) {
//noinspection unchecked
Projectile projectile = shooter.launchProjectile((Class<? extends Projectile>) type);
set(projectile, entityData);
Expand All @@ -84,12 +86,18 @@ private enum CaseUsage {
},
PROJECTILE_WORLD_NO_TRIGGER {
@Override
public @NotNull Entity shootHandler(EntityData<?> entityData, LivingEntity shooter, Location location, Class<? extends Entity> type, Vector vector, Consumer<?> consumer) {
public Entity shootHandler(EntityData<?> entityData, LivingEntity shooter, Location location, Class<? extends Entity> type, Vector vector, Consumer<?> consumer) {
Projectile projectile = (Projectile) shooter.getWorld().spawn(location, type);
projectile.setShooter(shooter);
return projectile;
}
},
PROJECTILE_WORLD_TRIGGER_BUKKIT {
@Override
public @Nullable Entity shootHandler(EntityData<?> entityData, LivingEntity shooter, Location location, Class<? extends Entity> type, Vector vector, Consumer<?> consumer) {
return null;
}
},
PROJECTILE_WORLD_TRIGGER {
@Override
public @Nullable Entity shootHandler(EntityData<?> entityData, LivingEntity shooter, Location location, Class<? extends Entity> type, Vector vector, Consumer<?> consumer) {
Expand Down Expand Up @@ -137,22 +145,27 @@ public Entity getProjectile() {
}

private static final boolean RUNNING_PAPER;

// TODO: Remove 'Method's after 1.20.2+ is the minimum version supported
private static Method launchWithBukkitConsumer;
private static Method worldSpawnWithBukkitConsumer;

static {
Skript.registerSection(EffSecShoot.class,
"shoot %entitydatas% [from %livingentities/locations%] [(at|with) (speed|velocity) %-number%] [%-direction%]",
"(make|let) %livingentities/locations% shoot %entitydatas% [(at|with) (speed|velocity) %-number%] [%-direction%]"
);
EventValues.registerEventValue(ShootEvent.class, Entity.class, ShootEvent::getProjectile, EventValues.TIME_NOW);
EventValues.registerEventValue(ShootEvent.class, Projectile.class, shootEvent -> shootEvent.getProjectile()
instanceof Projectile projectile ? projectile : null,
EventValues.TIME_NOW);
EventValues.registerEventValue(ShootEvent.class, Entity.class, ShootEvent::getProjectile);
EventValues.registerEventValue(ShootEvent.class, Projectile.class,
shootEvent -> shootEvent.getProjectile() instanceof Projectile projectile ? projectile : null);

if (!Skript.isRunningMinecraft(1, 20, 3)) {
if (!Skript.isRunningMinecraft(1, 20, 2)) {
try {
launchWithBukkitConsumer = LivingEntity.class.getMethod("launchProjectile", Class.class, Vector.class, org.bukkit.util.Consumer.class);
} catch (NoSuchMethodException ignored) {}
try {
worldSpawnWithBukkitConsumer = World.class.getMethod("spawn", Location.class, Class.class, org.bukkit.util.Consumer.class);
} catch (NoSuchMethodException ignored) {}
}
boolean launchHasJavaConsumer = Skript.methodExists(LivingEntity.class, "launchProjectile", Class.class, Vector.class, Consumer.class);
RUNNING_PAPER = launchWithBukkitConsumer != null || launchHasJavaConsumer;
Expand Down Expand Up @@ -204,7 +217,7 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
if (shooter instanceof LivingEntity livingShooter) {
vector = finalDirection.getDirection(livingShooter.getLocation()).multiply(finalVelocity.doubleValue());
//noinspection rawtypes
Consumer afterSpawn = afterSpawn(event, entityData, livingShooter);
Consumer afterSpawn = afterSpawn(event, entityData, livingShooter, vector);
Class<? extends Entity> type = entityData.getType();
Location shooterLoc = livingShooter.getLocation();
shooterLoc.setY(shooterLoc.getY() + livingShooter.getEyeHeight() / 2);
Expand All @@ -223,7 +236,13 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
CaseUsage caseUsage = getCaseUsage(isProjectile, useWorldSpawn, trigger != null);
if (caseUsage == CaseUsage.PROJECTILE_NO_WORLD_TRIGGER_BUKKIT) {
try {
launchWithBukkitConsumer.invoke(livingShooter, type, vector, afterSpawnBukkit(event, entityData, livingShooter));
//noinspection removal
launchWithBukkitConsumer.invoke(livingShooter, type, vector, (org.bukkit.util.Consumer<? extends Entity>) afterSpawn::accept);
} catch (Exception ignored) {}
} else if (caseUsage == CaseUsage.PROJECTILE_WORLD_TRIGGER_BUKKIT) {
try {
//noinspection removal
worldSpawnWithBukkitConsumer.invoke(livingShooter.getWorld(), type, (org.bukkit.util.Consumer<? extends Entity>) afterSpawn::accept);
} catch (Exception ignored) {}
} else {
finalProjectile = caseUsage.shootHandler(entityData, livingShooter, shooterLoc, type, vector, afterSpawn);
Expand All @@ -232,7 +251,7 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
vector = finalDirection.getDirection((Location) shooter).multiply(finalVelocity.doubleValue());
if (trigger != null) {
//noinspection unchecked,rawtypes
entityData.spawn((Location) shooter, (Consumer) afterSpawn(event, entityData, null));
entityData.spawn((Location) shooter, (Consumer) afterSpawn(event, entityData, null, vector));
} else {
finalProjectile = entityData.spawn((Location) shooter);
}
Expand Down Expand Up @@ -274,44 +293,23 @@ private CaseUsage getCaseUsage(Boolean isProjectile, Boolean useWorldSpawn, Bool
}
if (!hasTrigger)
return CaseUsage.PROJECTILE_WORLD_NO_TRIGGER;
if (worldSpawnWithBukkitConsumer != null)
return CaseUsage.PROJECTILE_WORLD_TRIGGER_BUKKIT;
return CaseUsage.PROJECTILE_WORLD_TRIGGER;
}

private Consumer<? extends Entity> afterSpawn(Event event, EntityData<?> entityData, @Nullable LivingEntity shooter) {
return entity -> {
if (entity instanceof Fireball fireball)
fireball.setShooter(shooter);
else if (entity instanceof Projectile projectile && shooter != null) {
projectile.setShooter(shooter);
set(projectile, entityData);
}
ShootEvent shootEvent = new ShootEvent(entity, shooter);
lastSpawned = entity;
Variables.setLocalVariables(shootEvent, Variables.copyLocalVariables(event));
TriggerItem.walk(trigger, shootEvent);
Variables.setLocalVariables(event, Variables.copyLocalVariables(shootEvent));
Variables.removeLocals(shootEvent);
};
}

/**
* MC 1.19 uses Bukkit Consumer for LivingEntity#launchProjectile instead of Java Consumer
*/
@SuppressWarnings("deprecation")
private org.bukkit.util.Consumer<? extends Entity> afterSpawnBukkit(Event event, EntityData<?> entityData, @Nullable LivingEntity shooter) {
private Consumer<? extends Entity> afterSpawn(Event event, EntityData<?> entityData, @Nullable LivingEntity shooter, Vector vector) {
return entity -> {
entity.setVelocity(vector);
if (entity instanceof Fireball fireball)
fireball.setShooter(shooter);
else if (entity instanceof Projectile projectile && shooter != null) {
else if (entity instanceof Projectile projectile) {
projectile.setShooter(shooter);
set(projectile, entityData);
}
ShootEvent shootEvent = new ShootEvent(entity, shooter);
lastSpawned = entity;
Variables.setLocalVariables(shootEvent, Variables.copyLocalVariables(event));
TriggerItem.walk(trigger, shootEvent);
Variables.setLocalVariables(event, Variables.copyLocalVariables(shootEvent));
Variables.removeLocals(shootEvent);
Variables.withLocalVariables(event, shootEvent, () -> TriggerItem.walk(trigger, shootEvent));
};
}

Expand Down
53 changes: 53 additions & 0 deletions src/test/skript/tests/syntaxes/sections/EffSecShoot.sk
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,56 @@ test "EffSecShoot":

clear entity within {_shooter}
clear entity within {_shooter2}

test "EffSecShoot From Entity - Velocity":
spawn a pig at test-location:
set {_shooter} to entity

make {_shooter} shoot an egg at speed 1:
set {_sectionVelocity} to velocity of event-entity
make {_shooter} shoot an egg at speed 1
assert the velocity of last shot entity is {_sectionVelocity} with "EffSecShoot from entity - egg velocities don't match"

make {_shooter} shoot a pig at speed 2:
set {_sectionVelocity} to velocity of event-entity
make {_shooter} shoot a pig at speed 2
assert the velocity of last shot entity is {_sectionVelocity} with "EffSecShoot from entity - pig velocities don't match"

make {_shooter} shoot an arrow at speed 3:
set {_sectionVelocity} to velocity of event-entity
make {_shooter} shoot an arrow at speed 3
assert the velocity of last shot entity is {_sectionVelocity} with "EffSecShoot from entity - arrow velocities don't match"

if running minecraft "1.20.0":
# 1.19.4 velocity is off by 1 vector-z
make {_shooter} shoot an fireball at speed 4:
set {_sectionVelocity} to velocity of event-entity
make {_shooter} shoot a fireball at speed 4
assert the velocity of last shot entity is {_sectionVelocity} with "EffSecShoot from entity - fireball velocities don't match"

clear all entities

test "EffSecShoot From Location - Velocity":
set {_location} to test-location ~ vector(0,10,0)

make {_location} shoot an egg at speed 1:
set {_sectionVelocity} to velocity of event-entity
make {_location} shoot an egg at speed 1
assert the velocity of last shot entity is {_sectionVelocity} with "EffSecShoot from location - egg velocities don't match"

make {_location} shoot a pig at speed 2:
set {_sectionVelocity} to velocity of event-entity
make {_location} shoot a pig at speed 2
assert the velocity of last shot entity is {_sectionVelocity} with "EffSecShoot from location - pig velocities don't match"

make {_location} shoot an arrow at speed 3:
set {_sectionVelocity} to velocity of event-entity
make {_location} shoot an arrow at speed 3
assert the velocity of last shot entity is {_sectionVelocity} with "EffSecShoot from location - arrow velocities don't match"

make {_location} shoot an fireball at speed 4:
set {_sectionVelocity} to velocity of event-entity
make {_location} shoot a fireball at speed 4
assert the velocity of last shot entity is {_sectionVelocity} with "EffSecShoot from location - fireball velocities don't match"

clear all entities