Skip to content

Commit

Permalink
Merge branch 'dev/patch' into patch/fix-quotes-in-command-args
Browse files Browse the repository at this point in the history
  • Loading branch information
sovdeeth authored Aug 14, 2024
2 parents ff46c2d + 72b17b2 commit b1b0afd
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 107 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ org.gradle.parallel=true

groupid=ch.njol
name=skript
version=2.9.0
version=2.9.1
jarName=Skript.jar
testEnv=java21/paper-1.21.0
testEnvJavaVersion=21
1 change: 1 addition & 0 deletions src/main/java/ch/njol/skript/aliases/AliasesProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ public EntityData<?> getRelatedEntity(ItemData item) {

public void clearAliases() {
aliases.clear();
materials.clear();
variations.clear();
aliasesMap.clear();
}
Expand Down
44 changes: 29 additions & 15 deletions src/main/java/ch/njol/skript/effects/EffPlaySound.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

import java.util.Locale;
import java.util.OptionalLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Name("Play Sound")
Expand All @@ -53,7 +54,7 @@
"<a href=\"https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html\">Spigot sound names</a> " +
"are supported. Playing resource pack sounds are supported too. The sound category is 'master' by default. ",
"",
"Playing a sound from an entity directly will result in the sound coming from said entity, even while moving.",
"When running 1.18+, playing a sound from an entity directly will result in the sound coming from said entity, even while moving.",
"If the sound is custom, a location emitter will follow the entity. Do note that pitch and volume ",
"are reflected based on the entity, and Minecraft may not use the values from this syntax.",
"",
Expand All @@ -68,22 +69,27 @@
"play sound \"custom.music.1\" in jukebox category at {speakerBlock}",
"play sound \"BLOCK_AMETHYST_BLOCK_RESONATE\" with seed 1 on target entity for the player #1.20.1+"
})
@RequiredPlugins("Paper 1.19.4+ or Adventure API 4.12.0+ (sound seed)")
@RequiredPlugins("Minecraft 1.18.1+ (entity emitters), Paper 1.19.4+ or Adventure API 4.12.0+ (sound seed)")
@Since("2.2-dev28, 2.4 (sound categories), 2.9.0 (sound seed & entity emitter)")
public class EffPlaySound extends Effect {

private static final boolean ADVENTURE_API = Skript.classExists("net.kyori.adventure.sound.Sound$Builder");
public static final Pattern KEY_PATTERN = Pattern.compile("([a-z0-9._-]+:)?[a-z0-9/._-]+");
private static final boolean PLAYER_ENTITY_EMITTER = Skript.methodExists(Player.class, "playSound", Entity.class, Sound.class, SoundCategory.class, float.class, float.class);
private static final boolean WORLD_ENTITY_EMITTER = Skript.methodExists(World.class, "playSound", Entity.class, String.class, SoundCategory.class, float.class, float.class);
public static final Pattern KEY_PATTERN = Pattern.compile("([a-z0-9._-]+:)?([a-z0-9/._-]+)");

static {
String additional = "";
if (ADVENTURE_API)
additional = "[[with] seed %-number%] ";
String emitterTypes = "locations";
if (PLAYER_ENTITY_EMITTER)
emitterTypes += "/entities";
Skript.registerEffect(EffPlaySound.class,
"play sound[s] %strings% " + additional + "[(in|from) %-soundcategory%] " +
"[(at|with) volume %-number%] [(and|at|with) pitch %-number%] (at|on|from) %locations/entities% [(to|for) %-players%]",
"[(at|with) volume %-number%] [(and|at|with) pitch %-number%] (at|on|from) %" + emitterTypes + "% [(to|for) %-players%]",
"play sound[s] %strings% " + additional + "[(in|from) %-soundcategory%] " +
"[(at|with) volume %-number%] [(and|at|with) pitch %-number%] [(to|for) %players%] [(at|on|from) %-locations/entities%]"
"[(at|with) volume %-number%] [(and|at|with) pitch %-number%] [(to|for) %players%] [(at|on|from) %-" + emitterTypes + "%]"
);
}

Expand Down Expand Up @@ -148,33 +154,33 @@ protected void execute(Event event) {
if (players != null) {
if (emitters == null) {
for (Player player : players.getArray(event)) {
play(Player::playSound, Player::playSound, ADVENTURE_API ? Player::playSound : null, ADVENTURE_API ? Player::playSound : null,
play(PLAYER_ENTITY_EMITTER ? Player::playSound : null, Player::playSound, ADVENTURE_API ? Player::playSound : null, ADVENTURE_API ? Player::playSound : null,
player, player.getLocation(), sounds.getArray(event), category, volume, pitch, seed);
}
} else {
for (Player player : players.getArray(event)) {
for (Object emitter : emitters.getArray(event)) {
if (emitter instanceof Entity) {
if (emitter instanceof Entity && PLAYER_ENTITY_EMITTER) {
Entity entity = (Entity) emitter;
play(Player::playSound, Player::playSound, ADVENTURE_API ? Player::playSound : null, ADVENTURE_API ? Player::playSound : null,
player, entity, sounds.getArray(event), category, volume, pitch, seed);
} else if (emitter instanceof Location) {
Location location = (Location) emitter;
play(Player::playSound, Player::playSound, ADVENTURE_API ? Player::playSound : null, ADVENTURE_API ? Player::playSound : null,
play(PLAYER_ENTITY_EMITTER ? Player::playSound : null, Player::playSound, ADVENTURE_API ? Player::playSound : null, ADVENTURE_API ? Player::playSound : null,
player, location, sounds.getArray(event), category, volume, pitch, seed);
}
}
}
}
} else if (emitters != null) {
for (Object emitter : emitters.getArray(event)) {
if (emitter instanceof Entity) {
if (emitter instanceof Entity && WORLD_ENTITY_EMITTER) {
Entity entity = (Entity) emitter;
play(World::playSound, World::playSound, ADVENTURE_API ? World::playSound : null, ADVENTURE_API ? World::playSound : null,
entity.getWorld(), entity, sounds.getArray(event), category, volume, pitch, seed);
} else if (emitter instanceof Location) {
Location location = (Location) emitter;
play(World::playSound, World::playSound, ADVENTURE_API ? World::playSound : null, ADVENTURE_API ? World::playSound : null,
play(WORLD_ENTITY_EMITTER ? World::playSound : null, World::playSound, ADVENTURE_API ? World::playSound : null, ADVENTURE_API ? World::playSound : null,
location.getWorld(), location, sounds.getArray(event), category, volume, pitch, seed);
}
}
Expand Down Expand Up @@ -203,7 +209,7 @@ public String toString(@Nullable Event event, boolean debug) {
return builder.toString();
}

private <T, E> void play(@NotNull SoundReceiver<T, Entity> entityReceiver,
private <T, E> void play(@Nullable SoundReceiver<T, Entity> entityReceiver,
@NotNull SoundReceiver<T, Location> locationReceiver,
@Nullable AdventureEmitterSoundReceiver<T> adventureLocationReceiver,
@Nullable AdventureEntitySoundReceiver<T> adventureEmitterReceiver,
Expand All @@ -224,7 +230,7 @@ void play(
);

static <T, E> void play(
@NotNull SoundReceiver<T, Entity> entityReceiver,
@Nullable SoundReceiver<T, Entity> entityReceiver,
@NotNull SoundReceiver<T, Location> locationReceiver,
@NotNull T receiver, @NotNull E emitter, @NotNull String[] sounds,
@NotNull SoundCategory category, float volume, float pitch, OptionalLong seed
Expand All @@ -236,10 +242,18 @@ static <T, E> void play(
key = enumSound.getKey();
} catch (IllegalArgumentException alternative) {
sound = sound.toLowerCase(Locale.ENGLISH);
if (!KEY_PATTERN.matcher(sound).matches())
Matcher keyMatcher = KEY_PATTERN.matcher(sound);
if (!keyMatcher.matches())
continue;
try {
key = NamespacedKey.fromString(sound);
String namespace = keyMatcher.group(1);
String keyValue = keyMatcher.group(2);
if (namespace == null) {
key = NamespacedKey.minecraft(keyValue);
} else {
namespace = namespace.substring(0, namespace.length() - 1);
key = new NamespacedKey(namespace, keyValue);
}
} catch (IllegalArgumentException argument) {
// The user input invalid characters
}
Expand All @@ -249,7 +263,7 @@ static <T, E> void play(
continue;
if (emitter instanceof Location) {
locationReceiver.play(receiver, (Location) emitter, key.getKey(), category, volume, pitch);
} else if (emitter instanceof Entity) {
} else if (emitter instanceof Entity && entityReceiver != null) {
entityReceiver.play(receiver, (Entity) emitter, key.getKey(), category, volume, pitch);
}
return;
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/ch/njol/skript/events/EvtMove.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,14 @@ public boolean check(Event event) {
}

@Override
@Nullable
@SuppressWarnings("unchecked")
public Class<? extends Event> [] getEventClasses() {
if (isPlayer) {
return new Class[] {PlayerMoveEvent.class};
} else if (HAS_ENTITY_MOVE) {
return new Class[] {EntityMoveEvent.class};
}
return null;
throw new IllegalStateException("This event has not yet initialized!");
}

@Override
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/ch/njol/skript/expressions/ExprIndices.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.util.LiteralUtils;
import ch.njol.util.Kleenean;
import ch.njol.util.Pair;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;

Expand Down Expand Up @@ -101,8 +102,14 @@ protected String[] get(Event e) {
if (sort) {
int direction = descending ? -1 : 1;
return variable.entrySet().stream()
.map((entry) -> new Pair<>(
entry.getKey(),
entry.getValue() instanceof Map<?,?>
? ((Map<?,?>) entry.getValue()).get(null)
: entry.getValue()
))
.sorted((a, b) -> ExprSortedList.compare(a.getValue(), b.getValue()) * direction)
.map(Entry::getKey)
.map(Pair::getKey)
.toArray(String[]::new);
}

Expand Down
131 changes: 52 additions & 79 deletions src/main/java/ch/njol/skript/expressions/ExprRemainingAir.java
Original file line number Diff line number Diff line change
@@ -1,108 +1,81 @@
/**
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright Peter Güttinger, SkriptLang team and contributors
*/
package ch.njol.skript.expressions;

import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;

import ch.njol.skript.classes.Changer;
import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.expressions.base.SimplePropertyExpression;
import ch.njol.skript.util.Timespan;
import ch.njol.skript.util.Timespan.TimePeriod;
import ch.njol.util.coll.CollectionUtils;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;

/**
* @author Peter Güttinger
*/
@Name("Remaining Air")
@Description("How much time a player has left underwater before starting to drown.")
@Examples({"player's remaining air is less than 3 seconds:",
" send \"hurry, get to the surface!\" to the player"})
@Since("<i>unknown</i> (before 2.1)")
@Examples({
"if the player's remaining air is less than 3 seconds:",
"\tsend \"hurry, get to the surface!\" to the player"
})
@Since("2.0")
public class ExprRemainingAir extends SimplePropertyExpression<LivingEntity, Timespan> {

static {
register(ExprRemainingAir.class, Timespan.class, "remaining air", "livingentities");
}

@Override
public Class<Timespan> getReturnType() {
return Timespan.class;
}

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

@Override
public Timespan convert(final LivingEntity entity) {
return Timespan.fromTicks(entity.getRemainingAir());
}

@Nullable

@Override
public Class<?>[] acceptChange(Changer.ChangeMode mode) {
return (mode != ChangeMode.REMOVE_ALL) ? CollectionUtils.array(Timespan.class) : null;
public Timespan convert(LivingEntity entity) {
/*
* negative values are allowed, and Minecraft itself may return a negative value from -1 to -20
* these negative values seem to control when the entity actually takes damage
* that is, when it hits -20, the entity takes damage, and it goes back to 0
* for simplicity, we cap it at 0 seconds (as it is still the case that the entity has no air)
*/
return new Timespan(TimePeriod.TICK, Math.max(0, entity.getRemainingAir()));
}

@SuppressWarnings("null")

@Override
public void change(Event event, @Nullable Object[] delta, Changer.ChangeMode mode) {
public Class<?> @Nullable [] acceptChange(ChangeMode mode) {
switch (mode) {
case ADD:
long ticks = ((Timespan)delta[0]).getTicks();
for (LivingEntity entity : getExpr().getArray(event)) {
int newTicks = entity.getRemainingAir() + (int) ticks;

// Sanitize remaining air to avoid client hangs/crashes
if (newTicks > 20000) // 1000 seconds
newTicks = 20000;
entity.setRemainingAir(newTicks);
}
break;
case REMOVE:
ticks = ((Timespan)delta[0]).getTicks();
for (LivingEntity entity : getExpr().getArray(event))
entity.setRemainingAir(entity.getRemainingAir() - (int) ticks);
break;
case SET:
ticks = ((Timespan)delta[0]).getTicks();
// Sanitize remaining air to avoid client hangs/crashes
if (ticks > 20000) // 1000 seconds
ticks = 20000;

for (LivingEntity entity : getExpr().getArray(event))
entity.setRemainingAir((int) ticks);
break;
case REMOVE:
case DELETE:
case REMOVE_ALL:
case RESET:
for (LivingEntity entity : getExpr().getArray(event))
entity.setRemainingAir(20 * 15); // 15 seconds of air
break;
return CollectionUtils.array(Timespan.class);
default:
return null;
}
}


@Override
public void change(Event event, Object @Nullable [] delta, ChangeMode mode) {
// default is 15 seconds of air
long changeValue = delta != null ? ((Timespan) delta[0]).getAs(TimePeriod.TICK) : 20 * 15;
if (mode == ChangeMode.REMOVE) // subtract the change value
changeValue *= -1;
for (LivingEntity entity : getExpr().getArray(event)) {
long newRemainingAir = 0;
if (mode == ChangeMode.ADD || mode == ChangeMode.REMOVE)
newRemainingAir = entity.getRemainingAir();
// while entities have a "maximum air", the value is allowed to go past it
// while negative values are permitted, the behavior is strange
newRemainingAir = Math.max(Math.min(newRemainingAir + changeValue, Integer.MAX_VALUE), 0);
entity.setRemainingAir((int) newRemainingAir);
}
}

@Override
public Class<Timespan> getReturnType() {
return Timespan.class;
}

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

}
17 changes: 11 additions & 6 deletions src/main/java/ch/njol/skript/lang/SkriptEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,27 +85,32 @@ public final boolean init(Literal<?>[] args, int matchedPattern, ParseResult par
throw new IllegalStateException();
skriptEventInfo = (SkriptEventInfo<?>) syntaxElementInfo;

assert entryContainer != null; // cannot be null for non-simple structures
this.source = entryContainer.getSource();

// use default value for now
listeningBehavior = eventData.getListenerBehavior();

// initialize implementation
if (!init(args, matchedPattern, parseResult))
return false;

// evaluate whether this event supports listening to cancelled events
supportsListeningBehavior = false;
for (Class<? extends Event> eventClass : getEventClasses()) {
if (Cancellable.class.isAssignableFrom(eventClass)) {
supportsListeningBehavior = true;
break;
}
}

listeningBehavior = eventData.getListenerBehavior();
// if the behavior is non-null, it was set by the user
if (listeningBehavior != null && !isListeningBehaviorSupported()) {
String eventName = skriptEventInfo.name.toLowerCase(Locale.ENGLISH);
Skript.error(Utils.A(eventName) + " event does not support listening for cancelled or uncancelled events.");
return false;
}

assert entryContainer != null; // cannot be null for non-simple structures
this.source = entryContainer.getSource();

return init(args, matchedPattern, parseResult);
return true;
}

/**
Expand Down
Loading

0 comments on commit b1b0afd

Please sign in to comment.