diff --git a/gradle.properties b/gradle.properties index e31c5a65002..3271da70815 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.parallel=true groupid=ch.njol name=skript -version=2.8.3 +version=2.8.4 jarName=Skript.jar testEnv=java17/paper-1.20.4 testEnvJavaVersion=17 diff --git a/settings.gradle b/settings.gradle index e5432e8214f..78b185aca4c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,6 @@ plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' } rootProject.name = 'Skript' diff --git a/src/main/java/ch/njol/skript/aliases/ItemData.java b/src/main/java/ch/njol/skript/aliases/ItemData.java index b6f90b30f42..32323c57e6f 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemData.java +++ b/src/main/java/ch/njol/skript/aliases/ItemData.java @@ -31,6 +31,7 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemFlag; @@ -188,15 +189,23 @@ public ItemData(ItemStack stack) { this(stack, BlockCompat.INSTANCE.getBlockValues(stack)); this.itemForm = true; } - - public ItemData(BlockState block) { - this.type = ItemUtils.asItem(block.getType()); + + /** + * @deprecated Use {@link ItemData#ItemData(BlockData)} instead + */ + @Deprecated + public ItemData(BlockState blockState) { + this(blockState.getBlockData()); + } + + public ItemData(BlockData blockData) { + this.type = blockData.getMaterial(); this.stack = new ItemStack(type); - this.blockValues = BlockCompat.INSTANCE.getBlockValues(block); + this.blockValues = BlockCompat.INSTANCE.getBlockValues(blockData); } public ItemData(Block block) { - this(block.getState()); + this(block.getBlockData()); } /** diff --git a/src/main/java/ch/njol/skript/aliases/ItemType.java b/src/main/java/ch/njol/skript/aliases/ItemType.java index 6b56e9cee17..7341667fa91 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemType.java +++ b/src/main/java/ch/njol/skript/aliases/ItemType.java @@ -43,6 +43,7 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Skull; +import org.bukkit.block.data.BlockData; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; @@ -184,10 +185,16 @@ public ItemType(ItemStack i) { add_(new ItemData(i)); } - public ItemType(BlockState b) { -// amount = 1; - add_(new ItemData(b)); - // TODO metadata - spawners, skulls, etc. + /** + * @deprecated Use {@link #ItemType(BlockData)} instead + */ + @Deprecated + public ItemType(BlockState blockState) { + this(blockState.getBlockData()); + } + + public ItemType(BlockData blockData) { + add_(new ItemData(blockData)); } /** @@ -211,7 +218,7 @@ public void setTo(ItemType i) { } public ItemType(Block block) { - this(block.getState()); + this(block.getBlockData()); } /** @@ -272,17 +279,25 @@ public boolean isOfType(@Nullable ItemStack item) { return isOfType(new ItemData(item)); } - public boolean isOfType(@Nullable BlockState block) { - if (block == null) + /** + * @deprecated Use {@link #isOfType(BlockData)} instead + */ + @Deprecated + public boolean isOfType(@Nullable BlockState blockState) { + return blockState != null && isOfType(blockState.getBlockData()); + } + + public boolean isOfType(@Nullable BlockData blockData) { + if (blockData == null) return isOfType(Material.AIR, null); - return isOfType(new ItemData(block)); + return isOfType(new ItemData(blockData)); } public boolean isOfType(@Nullable Block block) { if (block == null) return isOfType(Material.AIR, null); - return isOfType(block.getState()); + return isOfType(block.getBlockData()); } public boolean isOfType(ItemData type) { diff --git a/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java b/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java index ca373598eff..c6d5f25870e 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java @@ -74,13 +74,15 @@ public static Material asBlock(Material type) { return null; } } - + /** * Gets an item material corresponding to given block material, which might * be the given material. * @param type Material. * @return Item version of material or null. + * @deprecated This just returns itself and has no use */ + @Deprecated public static Material asItem(Material type) { // Assume (naively) that all types are valid items return type; diff --git a/src/main/java/ch/njol/skript/bukkitutil/block/BlockCompat.java b/src/main/java/ch/njol/skript/bukkitutil/block/BlockCompat.java index 5ffb1063284..eef0866a01b 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/block/BlockCompat.java +++ b/src/main/java/ch/njol/skript/bukkitutil/block/BlockCompat.java @@ -23,11 +23,11 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; import org.bukkit.entity.FallingBlock; import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.Nullable; -import ch.njol.skript.Skript; import ch.njol.skript.aliases.ItemFlags; /** @@ -42,13 +42,15 @@ public interface BlockCompat { BlockCompat INSTANCE = new NewBlockCompat(); static final BlockSetter SETTER = INSTANCE.getSetter(); - + /** * Gets block values from a block state. They can be compared to other * values if needed, but cannot be used to retrieve any other data. * @param block Block state to retrieve value from. * @return Block values. + * @deprecated Use {@link #getBlockValues(BlockData)} instead */ + @Deprecated @Nullable BlockValues getBlockValues(BlockState block); @@ -60,8 +62,11 @@ public interface BlockCompat { */ @Nullable default BlockValues getBlockValues(Block block) { - return getBlockValues(block.getState()); + return getBlockValues(block.getBlockData()); } + + @Nullable + BlockValues getBlockValues(BlockData blockData); /** * Gets block values from a item stack. They can be compared to other values @@ -71,17 +76,19 @@ default BlockValues getBlockValues(Block block) { */ @Nullable BlockValues getBlockValues(ItemStack stack); - + /** * Creates a block state from a falling block. * @param entity Falling block entity * @return Block state. + * @deprecated This shouldn't be used */ + @Deprecated BlockState fallingBlockToState(FallingBlock entity); - + @Nullable default BlockValues getBlockValues(FallingBlock entity) { - return getBlockValues(fallingBlockToState(entity)); + return getBlockValues(entity.getBlockData()); } /** diff --git a/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java b/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java index 0610022d1c9..2a0ecd36da8 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java +++ b/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java @@ -325,16 +325,23 @@ public void sendBlockChange(Player player, Location location, Material type, @Nu } private NewBlockSetter setter = new NewBlockSetter(); - + + /** + * @deprecated Use {@link #getBlockValues(BlockData)} instead + */ + @Deprecated @Nullable @Override - public BlockValues getBlockValues(BlockState block) { - // If block doesn't have useful data, data field of type is MaterialData - if (block.getType().isBlock()) - return new NewBlockValues(block.getType(), block.getBlockData(), false); - return null; + public BlockValues getBlockValues(BlockState blockState) { + return getBlockValues(blockState.getBlockData()); } - + + @Nullable + @Override + public BlockValues getBlockValues(BlockData blockData) { + return new NewBlockValues(blockData.getMaterial(), blockData, false); + } + @Override @Nullable public BlockValues getBlockValues(ItemStack stack) { @@ -345,15 +352,16 @@ public BlockValues getBlockValues(ItemStack stack) { } return null; } - + @Override public BlockSetter getSetter() { return setter; } + @Deprecated @Override public BlockState fallingBlockToState(FallingBlock entity) { - BlockState state = entity.getWorld().getBlockAt(0, 0, 0).getState(); + BlockState state = entity.getLocation().getBlock().getState(); state.setBlockData(entity.getBlockData()); return state; } diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java index 556f396b80b..e2c5392dd39 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java @@ -376,10 +376,28 @@ public Location[] execute(FunctionEvent e, Object[][] params) { } }.description("Creates a location from a world and 3 coordinates, with an optional yaw and pitch.", "If for whatever reason the world is not found, it will fallback to the server's main world.") - .examples("location(0, 128, 0)", - "location(player's x-coordinate, player's y-coordinate + 5, player's z-coordinate, player's world, 0, 90)", - "location(0, 64, 0, world \"world_nether\")", - "location(100, 110, -145, world(\"my_custom_world\"))") + .examples("# TELEPORTING", + "teleport player to location(1,1,1, world \"world\")", + "teleport player to location(1,1,1, world \"world\", 100, 0)", + "teleport player to location(1,1,1, world \"world\", yaw of player, pitch of player)", + "teleport player to location(1,1,1, world of player)", + "teleport player to location(1,1,1, world(\"world\"))", + "teleport player to location({_x}, {_y}, {_z}, {_w}, {_yaw}, {_pitch})", + "# SETTING BLOCKS", + "set block at location(1,1,1, world \"world\") to stone", + "set block at location(1,1,1, world \"world\", 100, 0) to stone", + "set block at location(1,1,1, world of player) to stone", + "set block at location(1,1,1, world(\"world\")) to stone", + "set block at location({_x}, {_y}, {_z}, {_w}) to stone", + "# USING VARIABLES", + "set {_l1} to location(1,1,1)", + "set {_l2} to location(10,10,10)", + "set blocks within {_l1} and {_l2} to stone", + "if player is within {_l1} and {_l2}:", + "# OTHER", + "kill all entities in radius 50 around location(1,65,1, world \"world\")", + "delete all entities in radius 25 around location(50,50,50, world \"world_nether\")", + "ignite all entities in radius 25 around location(1,1,1, world of player)") .since("2.2")); Functions.registerFunction(new SimpleJavaFunction("date", new Parameter[] { diff --git a/src/main/java/ch/njol/skript/conditions/CondIsPathfinding.java b/src/main/java/ch/njol/skript/conditions/CondIsPathfinding.java new file mode 100644 index 00000000000..3fb40549421 --- /dev/null +++ b/src/main/java/ch/njol/skript/conditions/CondIsPathfinding.java @@ -0,0 +1,105 @@ +/** + * 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 . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.conditions; + +import ch.njol.skript.Skript; +import ch.njol.skript.conditions.base.PropertyCondition; +import ch.njol.skript.conditions.base.PropertyCondition.PropertyType; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.RequiredPlugins; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; + +import com.destroystokyo.paper.entity.Pathfinder; +import com.destroystokyo.paper.entity.Pathfinder.PathResult; + +import org.bukkit.Location; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Mob; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; + +@Name("Is Pathfinding") +@Description({ + "Checks whether living entities are pathfinding.", + "Can only be a living entity that is a Mob." +}) +@Examples({ + "make {_entity} pathfind to {_location} at speed 2", + "while {_entity} is pathfinding", + "\twait a second", + "launch flickering trailing burst firework colored red at location of {_entity}", + "subtract 10 from {defence::tower::health}", + "clear entity within {_entity}" +}) +@RequiredPlugins("Paper") +@Since("INSERT VERSION") +public class CondIsPathfinding extends Condition { + + static { + if (Skript.classExists("org.bukkit.entity.Mob") && Skript.methodExists(Mob.class, "getPathfinder")) + PropertyCondition.register(CondIsPathfinding.class, "pathfinding [to[wards] %-livingentity/location%]", "livingentities"); + } + + private Expression entities; + private Expression target; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + entities = (Expression) expressions[0]; + target = expressions[1]; + setNegated(matchedPattern == 1); + return true; + } + + @Override + public boolean check(Event event) { + return entities.check(event, entity -> { + if (!(entity instanceof Mob)) + return false; + Pathfinder pathfind = ((Mob) entity).getPathfinder(); + if (target == null) + return pathfind.hasPath(); + + PathResult current = pathfind.getCurrentPath(); + Object target = this.target.getSingle(event); + if (target == null || current == null) + return false; + Location location = current.getFinalPoint(); + if (target instanceof Location) + return location.equals(target); + assert target instanceof LivingEntity; + LivingEntity entityTarget = (LivingEntity) target; + return location.distance(((Mob) entityTarget).getLocation()) < 1; + }, isNegated()); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return PropertyCondition.toString(this, PropertyType.BE, event, debug, entities, "pathfinding" + + target == null ? "" : " to " + target.toString(event, debug)); + } + +} diff --git a/src/main/java/ch/njol/skript/entity/EntityData.java b/src/main/java/ch/njol/skript/entity/EntityData.java index 435415b8c91..9cf1f427fd7 100644 --- a/src/main/java/ch/njol/skript/entity/EntityData.java +++ b/src/main/java/ch/njol/skript/entity/EntityData.java @@ -37,6 +37,7 @@ import org.bukkit.RegionAccessor; import org.bukkit.World; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.eclipse.jdt.annotation.Nullable; @@ -94,6 +95,7 @@ public abstract class EntityData implements SyntaxElement, Ygg } catch (NoSuchMethodException | SecurityException ignored) { /* We already checked if the method exists */ } } + private static final boolean HAS_ENABLED_BY_FEATURE = Skript.methodExists(EntityType.class, "isEnabledByFeature", World.class); public final static String LANGUAGE_NODE = "entities"; public final static Message m_age_pattern = new Message(LANGUAGE_NODE + ".age pattern"); @@ -463,6 +465,25 @@ private E apply(E entity) { return entity; } + /** + * Check if this entity type can spawn. + *

Some entity types may be restricted by experimental datapacks.

+ * + * @param world World to check if entity can spawn in + * @return True if entity can spawn else false + */ + public boolean canSpawn(@Nullable World world) { + if (world == null) + return false; + if (HAS_ENABLED_BY_FEATURE) { + // Check if the entity can actually be spawned + // Some entity types may be restricted by experimental datapacks + EntityType bukkitEntityType = EntityUtils.toBukkitEntityType(this); + return bukkitEntityType.isEnabledByFeature(world); + } + return true; + } + /** * Spawn this entity data at a location. * @@ -476,7 +497,7 @@ public final E spawn(Location location) { /** * Spawn this entity data at a location. - * The consumer allows for modiciation to the entity before it actually gets spawned. + * The consumer allows for modification to the entity before it actually gets spawned. *

* Bukkit's own {@link org.bukkit.util.Consumer} is deprecated. * Use {@link #spawn(Location, Consumer)} @@ -494,7 +515,7 @@ public E spawn(Location location, org.bukkit.util.@Nullable Consumer consumer /** * Spawn this entity data at a location. - * The consumer allows for modiciation to the entity before it actually gets spawned. + * The consumer allows for modification to the entity before it actually gets spawned. * * @param location The {@link Location} to spawn the entity at. * @param consumer A {@link Consumer} to apply the entity changes to. @@ -503,10 +524,13 @@ public E spawn(Location location, org.bukkit.util.@Nullable Consumer consumer @Nullable public E spawn(Location location, @Nullable Consumer consumer) { assert location != null; + World world = location.getWorld(); + if (!canSpawn(world)) + return null; if (consumer != null) { return EntityData.spawn(location, getType(), e -> consumer.accept(this.apply(e))); } else { - return apply(location.getWorld().spawn(location, getType())); + return apply(world.spawn(location, getType())); } } @@ -646,15 +670,18 @@ public void deserialize(final Fields fields) throws StreamCorruptedException, No protected boolean deserialize(final String s) { return false; } - + @SuppressWarnings({"unchecked", "deprecation"}) protected static @Nullable E spawn(Location location, Class type, Consumer consumer) { + World world = location.getWorld(); + if (world == null) + return null; try { if (WORLD_1_17_CONSUMER) { - return (@Nullable E) WORLD_1_17_CONSUMER_METHOD.invoke(location.getWorld(), location, type, + return (@Nullable E) WORLD_1_17_CONSUMER_METHOD.invoke(world, location, type, (org.bukkit.util.Consumer) consumer::accept); } else if (WORLD_1_13_CONSUMER) { - return (@Nullable E) WORLD_1_13_CONSUMER_METHOD.invoke(location.getWorld(), location, type, + return (@Nullable E) WORLD_1_13_CONSUMER_METHOD.invoke(world, location, type, (org.bukkit.util.Consumer) consumer::accept); } } catch (InvocationTargetException | IllegalAccessException e) { @@ -662,7 +689,7 @@ protected boolean deserialize(final String s) { Skript.exception(e, "Can't spawn " + type.getName()); return null; } - return location.getWorld().spawn(location, type, consumer); + return world.spawn(location, type, consumer); } - + } diff --git a/src/main/java/ch/njol/skript/entity/FallingBlockData.java b/src/main/java/ch/njol/skript/entity/FallingBlockData.java index 2f937148fe9..262b2b564d8 100644 --- a/src/main/java/ch/njol/skript/entity/FallingBlockData.java +++ b/src/main/java/ch/njol/skript/entity/FallingBlockData.java @@ -93,7 +93,7 @@ public ItemType convert(ItemType t) { @Override protected boolean init(final @Nullable Class c, final @Nullable FallingBlock e) { if (e != null) // TODO material data support - types = new ItemType[] {new ItemType(BlockCompat.INSTANCE.fallingBlockToState(e))}; + types = new ItemType[] {new ItemType(e.getBlockData())}; return true; } @@ -101,7 +101,7 @@ protected boolean init(final @Nullable Class c, final @N protected boolean match(final FallingBlock entity) { if (types != null) { for (final ItemType t : types) { - if (t.isOfType(BlockCompat.INSTANCE.fallingBlockToState(entity))) + if (t.isOfType(entity.getBlockData())) return true; } return false; diff --git a/src/main/java/ch/njol/skript/events/EvtBlock.java b/src/main/java/ch/njol/skript/events/EvtBlock.java index 4a393146600..78d5fc1dc59 100644 --- a/src/main/java/ch/njol/skript/events/EvtBlock.java +++ b/src/main/java/ch/njol/skript/events/EvtBlock.java @@ -104,7 +104,7 @@ public boolean check(final Event event) { if (event instanceof BlockFormEvent) { BlockFormEvent blockFormEvent = (BlockFormEvent) event; BlockState newState = blockFormEvent.getNewState(); - item = new ItemType(newState); + item = new ItemType(newState.getBlockData()); blockData = newState.getBlockData(); } else if (event instanceof BlockEvent) { BlockEvent blockEvent = (BlockEvent) event; diff --git a/src/main/java/ch/njol/skript/events/EvtGrow.java b/src/main/java/ch/njol/skript/events/EvtGrow.java index 98f5f281728..ff2deb11a8d 100644 --- a/src/main/java/ch/njol/skript/events/EvtGrow.java +++ b/src/main/java/ch/njol/skript/events/EvtGrow.java @@ -174,7 +174,7 @@ private static boolean checkFrom(Event event, Literal types) { BlockState oldState = ((BlockGrowEvent) event).getBlock().getState(); return types.check(event, type -> { if (type instanceof ItemType) { - return ((ItemType) type).isOfType(oldState); + return ((ItemType) type).isOfType(oldState.getBlockData()); } else if (type instanceof BlockData) { return ((BlockData) type).matches(oldState.getBlockData()); } @@ -201,7 +201,7 @@ private static boolean checkTo(Event event, Literal types) { BlockState newState = ((BlockGrowEvent) event).getNewState(); return types.check(event, type -> { if (type instanceof ItemType) { - return ((ItemType) type).isOfType(newState); + return ((ItemType) type).isOfType(newState.getBlockData()); } else if (type instanceof BlockData) { return ((BlockData) type).matches(newState.getBlockData()); } diff --git a/src/main/java/ch/njol/skript/expressions/ExprAnvilText.java b/src/main/java/ch/njol/skript/expressions/ExprAnvilText.java index acc05ae124c..15490749007 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprAnvilText.java +++ b/src/main/java/ch/njol/skript/expressions/ExprAnvilText.java @@ -35,7 +35,7 @@ @Examples({ "on inventory click:", "\ttype of event-inventory is anvil inventory", - "\tif the anvil input text of the event-inventory is \"FREE OP\":", + "\tif the anvil text input of the event-inventory is \"FREE OP\":", "\t\tban player" }) @Since("2.7") diff --git a/src/main/java/ch/njol/skript/expressions/ExprIndices.java b/src/main/java/ch/njol/skript/expressions/ExprIndices.java index 1f4439273b6..c09bfe81964 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprIndices.java +++ b/src/main/java/ch/njol/skript/expressions/ExprIndices.java @@ -28,7 +28,6 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.Variable; import ch.njol.skript.lang.util.SimpleExpression; -import org.skriptlang.skript.lang.comparator.Comparators; import ch.njol.skript.util.LiteralUtils; import ch.njol.util.Kleenean; import org.bukkit.event.Event; @@ -102,7 +101,7 @@ protected String[] get(Event e) { if (sort) { int direction = descending ? -1 : 1; return variable.entrySet().stream() - .sorted((a, b) -> compare(a, b, direction)) + .sorted((a, b) -> ExprSortedList.compare(a.getValue(), b.getValue()) * direction) .map(Entry::getKey) .toArray(String[]::new); } @@ -130,8 +129,4 @@ public String toString(@Nullable Event e, boolean debug) { return text; } - // Extracted method for better readability - private int compare(Entry a, Entry b, int direction) { - return Comparators.compare(a.getValue(), b.getValue()).getRelation() * direction; - } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprRandom.java b/src/main/java/ch/njol/skript/expressions/ExprRandom.java index c3a87f7e957..5ab7e45996a 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprRandom.java +++ b/src/main/java/ch/njol/skript/expressions/ExprRandom.java @@ -78,9 +78,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye } else { expr = exprs[1].getConvertedExpression((((Literal>) exprs[0]).getSingle()).getC()); } - if (expr == null) - return false; - return true; + return expr != null && LiteralUtils.canInitSafely(expr); } @Override diff --git a/src/main/java/ch/njol/skript/expressions/ExprRandomCharacter.java b/src/main/java/ch/njol/skript/expressions/ExprRandomCharacter.java index 32afa678c42..13660c7ccd8 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprRandomCharacter.java +++ b/src/main/java/ch/njol/skript/expressions/ExprRandomCharacter.java @@ -50,17 +50,17 @@ public class ExprRandomCharacter extends SimpleExpression { static { Skript.registerExpression(ExprRandomCharacter.class, String.class, ExpressionType.COMBINED, - "[a|%-number%] random [:alphanumeric] character[s] (from|between) %string% (to|and) %string%"); + "[a|%-integer%] random [:alphanumeric] character[s] (from|between) %string% (to|and) %string%"); } @Nullable - private Expression amount; + private Expression amount; private Expression from, to; private boolean isAlphanumeric; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - amount = (Expression) exprs[0]; + amount = (Expression) exprs[0]; from = (Expression) exprs[1]; to = (Expression) exprs[2]; isAlphanumeric = parseResult.hasTag("alphanumeric"); @@ -70,7 +70,10 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable protected String[] get(Event event) { - int amount = this.amount == null ? 1 : this.amount.getOptionalSingle(event).orElse(1).intValue(); + Integer amount = this.amount == null ? Integer.valueOf(1) : this.amount.getSingle(event); + if (amount == null || amount <= 0) + return new String[0]; + String from = this.from.getSingle(event); String to = this.to.getSingle(event); if (from == null || to == null) @@ -117,7 +120,7 @@ protected String[] get(Event event) { @Override public boolean isSingle() { if (amount instanceof Literal) - return ((Literal) amount).getSingle().intValue() == 1; + return ((Literal) amount).getSingle() == 1; return amount == null; } diff --git a/src/main/java/ch/njol/skript/expressions/ExprSortedList.java b/src/main/java/ch/njol/skript/expressions/ExprSortedList.java index 3cfed13b0a6..0a4af988476 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprSortedList.java +++ b/src/main/java/ch/njol/skript/expressions/ExprSortedList.java @@ -34,6 +34,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.skriptlang.skript.lang.comparator.Comparator; import org.skriptlang.skript.lang.comparator.Comparators; +import org.skriptlang.skript.lang.comparator.Relation; import java.lang.reflect.Array; @@ -77,12 +78,14 @@ protected Object[] get(Event event) { } @SuppressWarnings("unchecked") - private static int compare(A a, B b) throws IllegalArgumentException, ClassCastException { + public static int compare(A a, B b) throws IllegalArgumentException, ClassCastException { + if (a instanceof String && b instanceof String) + return Relation.get(((String) a).compareToIgnoreCase((String) b)).getRelation(); Comparator comparator = Comparators.getComparator((Class) a.getClass(), (Class) b.getClass()); if (comparator != null && comparator.supportsOrdering()) return comparator.compare(a, b).getRelation(); if (!(a instanceof Comparable)) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Cannot compare " + a.getClass()); return ((Comparable) a).compareTo(b); } diff --git a/src/main/java/ch/njol/skript/expressions/ExprTimespanDetails.java b/src/main/java/ch/njol/skript/expressions/ExprTimespanDetails.java new file mode 100644 index 00000000000..a699d6e6e95 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprTimespanDetails.java @@ -0,0 +1,73 @@ +/** + * 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 . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.expressions; + +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.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.util.Timespan; +import ch.njol.skript.util.Timespan.TimePeriod; +import ch.njol.util.Kleenean; +import org.eclipse.jdt.annotation.Nullable; + +import java.util.Locale; + +@Name("Timespan Details") +@Description("Retrieve specific information of a timespan such as hours/minutes/etc.") +@Examples({ + "set {_t} to difference between now and {Payouts::players::%uuid of player%::last-date}", + "send \"It has been %days of {_t}% day(s) since last payout.\"" +}) +@Since("INSERT VERSION") +public class ExprTimespanDetails extends SimplePropertyExpression { + + static { + register(ExprTimespanDetails.class, Long.class, "(:(tick|second|minute|hour|day|week|month|year))s", "timespans"); + } + + @SuppressWarnings("NotNullFieldNotInitialized") + private TimePeriod type; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + type = TimePeriod.valueOf(parseResult.tags.get(0).toUpperCase(Locale.ENGLISH)); + return super.init(exprs, matchedPattern, isDelayed, parseResult); + } + + @Override + @Nullable + public Long convert(Timespan time) { + return time.getMilliSeconds() / type.getTime(); + } + + @Override + public Class getReturnType() { + return Long.class; + } + + @Override + protected String getPropertyName() { + return type.name().toLowerCase(Locale.ENGLISH); + } + +} diff --git a/src/main/java/ch/njol/skript/lang/SkriptParser.java b/src/main/java/ch/njol/skript/lang/SkriptParser.java index 3d6a16b4eba..d67318f5174 100644 --- a/src/main/java/ch/njol/skript/lang/SkriptParser.java +++ b/src/main/java/ch/njol/skript/lang/SkriptParser.java @@ -531,6 +531,9 @@ private Expression parseSingleExpr(boolean allowUnparsedLiteral, @Nullable Lo Expression parsedExpression = parseExpression(types, expr); if (parsedExpression != null) { // Expression/VariableString parsing success Class returnType = parsedExpression.getReturnType(); // Sometimes getReturnType does non-trivial costly operations + if (returnType == null) + throw new SkriptAPIException("Expression '" + expr + "' returned null for method Expression#getReturnType. Null is not a valid return."); + for (int i = 0; i < types.length; i++) { Class type = types[i]; if (type == null) // Ignore invalid (null) types @@ -980,6 +983,26 @@ public static ParseResult parse(String text, String pattern) { return new SkriptParser(text, PARSE_LITERALS, ParseContext.COMMAND).parse_i(pattern); } + /** + * Parses the text as the given pattern with the given parse context and parse flags. + *

+ * Prints parse errors (i.e. must start a ParseLog before calling this method) + */ + @Nullable + public static ParseResult parse(String text, String pattern, int parseFlags, ParseContext parseContext) { + return new SkriptParser(text, parseFlags, parseContext).parse_i(pattern); + } + + /** + * Parses the text as the given pattern with the given parse context and parse flags. + *

+ * Prints parse errors (i.e. must start a ParseLog before calling this method) + */ + @Nullable + public static ParseResult parse(String text, SkriptPattern pattern, int parseFlags, ParseContext parseContext) { + return parse(text, pattern.toString(), parseFlags, parseContext); + } + /** * Finds the closing bracket of the group at start (i.e. start has to be in a group). * diff --git a/src/main/java/ch/njol/skript/lang/VariableString.java b/src/main/java/ch/njol/skript/lang/VariableString.java index 6f3c11e492a..a81cd600a4e 100644 --- a/src/main/java/ch/njol/skript/lang/VariableString.java +++ b/src/main/java/ch/njol/skript/lang/VariableString.java @@ -224,16 +224,6 @@ public static VariableString newInstance(String original, StringMode mode) { log.printErrors("Can't understand this expression: " + original.substring(exprStart + 1, exprEnd)); return null; } else { - if ( - mode == StringMode.VARIABLE_NAME && - !SkriptConfig.usePlayerUUIDsInVariableNames.value() && - OfflinePlayer.class.isAssignableFrom(expr.getReturnType()) - ) { - Skript.warning( - "In the future, players in variable names will use the player's UUID instead of their name. " + - "For information on how to make sure your scripts won't be impacted by this change, see https://github.com/SkriptLang/Skript/discussions/6270." - ); - } strings.add(expr); } log.printLog(); diff --git a/src/main/java/ch/njol/skript/util/Timespan.java b/src/main/java/ch/njol/skript/util/Timespan.java index 3723fdb192f..a73f1ce6aaf 100644 --- a/src/main/java/ch/njol/skript/util/Timespan.java +++ b/src/main/java/ch/njol/skript/util/Timespan.java @@ -18,10 +18,16 @@ */ package ch.njol.skript.util; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.Locale; import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval; import ch.njol.skript.Skript; @@ -35,91 +41,133 @@ public class Timespan implements YggdrasilSerializable, Comparable { // REMIND unit - private static final Noun m_tick = new Noun("time.tick"); - private static final Noun m_second = new Noun("time.second"); - private static final Noun m_minute = new Noun("time.minute"); - private static final Noun m_hour = new Noun("time.hour"); - private static final Noun m_day = new Noun("time.day"); - private static final Noun m_week = new Noun("time.week"); - private static final Noun m_month = new Noun("time.month"); - private static final Noun m_year = new Noun("time.year"); - static final Noun[] names = {m_tick, m_second, m_minute, m_hour, m_day, m_week, m_month, m_year}; - static final long[] times = {50L, 1000L, 1000L * 60L, 1000L * 60L * 60L, 1000L * 60L * 60L * 24L, 1000L * 60L * 60L * 24L * 7L, 1000L * 60L * 60L * 24L * 30L, 1000L * 60L * 60L * 24L * 365L}; - static final HashMap parseValues = new HashMap<>(); + + public enum TimePeriod { + + TICK(50L), + SECOND(1000L), + MINUTE(SECOND.time * 60L), + HOUR(MINUTE.time * 60L), + DAY(HOUR.time * 24L), + WEEK(DAY.time * 7L), + MONTH(DAY.time * 30L), // Who cares about 28, 29 or 31 days? + YEAR(DAY.time * 365L); + + private final Noun name; + private final long time; + + TimePeriod(long time) { + this.name = new Noun("time." + this.name().toLowerCase(Locale.ENGLISH)); + this.time = time; + } + + public long getTime() { + return time; + } + + } + + private static final List> SIMPLE_VALUES = Arrays.asList( + new NonNullPair<>(TimePeriod.YEAR.name, TimePeriod.YEAR.time), + new NonNullPair<>(TimePeriod.MONTH.name, TimePeriod.MONTH.time), + new NonNullPair<>(TimePeriod.WEEK.name, TimePeriod.WEEK.time), + new NonNullPair<>(TimePeriod.DAY.name, TimePeriod.DAY.time), + new NonNullPair<>(TimePeriod.HOUR.name, TimePeriod.HOUR.time), + new NonNullPair<>(TimePeriod.MINUTE.name, TimePeriod.MINUTE.time), + new NonNullPair<>(TimePeriod.SECOND.name, TimePeriod.SECOND.time) + ); + + private static final Map PARSE_VALUES = new HashMap<>(); + static { Language.addListener(new LanguageChangeListener() { @Override public void onLanguageChange() { - for (int i = 0; i < names.length; i++) { - parseValues.put(names[i].getSingular().toLowerCase(Locale.ENGLISH), times[i]); - parseValues.put(names[i].getPlural().toLowerCase(Locale.ENGLISH), times[i]); + for (TimePeriod time : TimePeriod.values()) { + PARSE_VALUES.put(time.name.getSingular().toLowerCase(Locale.ENGLISH), time.getTime()); + PARSE_VALUES.put(time.name.getPlural().toLowerCase(Locale.ENGLISH), time.getTime()); } } }); } + + private static final Pattern TIMESPAN_PATTERN = Pattern.compile("^(\\d+):(\\d\\d)(:\\d\\d){0,2}(?\\.\\d{1,4})?$"); + private static final Pattern TIMESPAN_NUMBER_PATTERN = Pattern.compile("^\\d+(\\.\\d+)?$"); + private static final Pattern TIMESPAN_SPLIT_PATTERN = Pattern.compile("[:.]"); + + private final long millis; @Nullable - public static Timespan parse(final String s) { - if (s.isEmpty()) + public static Timespan parse(String value) { + if (value.isEmpty()) return null; + long t = 0; boolean minecraftTime = false; boolean isMinecraftTimeSet = false; - if (s.matches("^\\d+:\\d\\d(:\\d\\d)?(\\.\\d{1,4})?$")) { // MM:SS[.ms] or HH:MM:SS[.ms] - final String[] ss = s.split("[:.]"); - final long[] times = {1000L * 60L * 60L, 1000L * 60L, 1000L, 1L}; // h, m, s, ms - - final int offset = ss.length == 3 && !s.contains(".") || ss.length == 4 ? 0 : 1; - for (int i = 0; i < ss.length; i++) { - t += times[offset + i] * Utils.parseLong("" + ss[i]); + + Matcher matcher = TIMESPAN_PATTERN.matcher(value); + if (matcher.matches()) { // MM:SS[.ms] or HH:MM:SS[.ms] or DD:HH:MM:SS[.ms] + String[] substring = TIMESPAN_SPLIT_PATTERN.split(value); + long[] times = {1L, TimePeriod.SECOND.time, TimePeriod.MINUTE.time, TimePeriod.HOUR.time, TimePeriod.DAY.time}; // ms, s, m, h, d + boolean hasMs = value.contains("."); + int length = substring.length; + int offset = 2; // MM:SS[.ms] + + if (length == 4 && !hasMs || length == 5) // DD:HH:MM:SS[.ms] + offset = 0; + else if (length == 3 && !hasMs || length == 4) // HH:MM:SS[.ms] + offset = 1; + + for (int i = 0; i < substring.length; i++) { + t += times[offset + i] * Utils.parseLong("" + substring[i]); } } else { // minutes/seconds/.. etc - final String[] subs = s.toLowerCase(Locale.ENGLISH).split("\\s+"); - for (int i = 0; i < subs.length; i++) { - String sub = subs[i]; + String[] substring = value.toLowerCase(Locale.ENGLISH).split("\\s+"); + for (int i = 0; i < substring.length; i++) { + String sub = substring[i]; if (sub.equals(GeneralWords.and.toString())) { - if (i == 0 || i == subs.length - 1) + if (i == 0 || i == substring.length - 1) return null; continue; } double amount = 1; if (Noun.isIndefiniteArticle(sub)) { - if (i == subs.length - 1) + if (i == substring.length - 1) return null; - amount = 1; - sub = subs[++i]; - } else if (sub.matches("^\\d+(\\.\\d+)?$")) { - if (i == subs.length - 1) + sub = substring[++i]; + } else if (TIMESPAN_NUMBER_PATTERN.matcher(sub).matches()) { + if (i == substring.length - 1) return null; try { amount = Double.parseDouble(sub); } catch (NumberFormatException e) { - throw new IllegalArgumentException("invalid timespan: " + s); + throw new IllegalArgumentException("Invalid timespan: " + value); } - sub = subs[++i]; + sub = substring[++i]; } if (CollectionUtils.contains(Language.getList("time.real"), sub)) { - if (i == subs.length - 1 || isMinecraftTimeSet && minecraftTime) + if (i == substring.length - 1 || isMinecraftTimeSet && minecraftTime) return null; - sub = subs[++i]; + sub = substring[++i]; } else if (CollectionUtils.contains(Language.getList("time.minecraft"), sub)) { - if (i == subs.length - 1 || isMinecraftTimeSet && !minecraftTime) + if (i == substring.length - 1 || isMinecraftTimeSet && !minecraftTime) return null; minecraftTime = true; - sub = subs[++i]; + sub = substring[++i]; } if (sub.endsWith(",")) sub = sub.substring(0, sub.length() - 1); - final Long d = parseValues.get(sub.toLowerCase(Locale.ENGLISH)); + Long d = PARSE_VALUES.get(sub.toLowerCase(Locale.ENGLISH)); if (d == null) return null; - if (minecraftTime && d != times[0]) // times[0] == tick + if (minecraftTime && d != TimePeriod.TICK.time) amount /= 72f; t += Math.round(amount * d); @@ -128,27 +176,47 @@ public static Timespan parse(final String s) { } } + return new Timespan(t); } - private final long millis; - public Timespan() { millis = 0; } - - public Timespan(final long millis) { + + /** + * Builds a Timespan from the given milliseconds. + * + * @param millis The milliseconds of Timespan + */ + public Timespan(long millis) { if (millis < 0) throw new IllegalArgumentException("millis must be >= 0"); this.millis = millis; } + /** + * Builds a Timespan from the given long parameter of a specific {@link TimePeriod}. + * + * @param timePeriod The requested TimePeriod + * @param time The time of the requested TimePeriod + */ + public Timespan(TimePeriod timePeriod, long time) { + if (time < 0) + throw new IllegalArgumentException("time must be >= 0"); + this.millis = time * timePeriod.getTime(); + } + /** * Builds a Timespan from the given long parameter. + * + * @deprecated Use {@link Timespan#Timespan(TimePeriod, long)} * * @param ticks The amount of Minecraft ticks to convert to a timespan. * @return Timespan based on the provided long. */ + @Deprecated + @ApiStatus.ScheduledForRemoval public static Timespan fromTicks(long ticks) { return new Timespan(ticks * 50L); } @@ -162,17 +230,35 @@ public static Timespan fromTicks_i(long ticks) { return new Timespan(ticks * 50L); } + /** + * @deprecated Use {@link Timespan#getAs(TimePeriod)} + * + * @return the amount of milliseconds this timespan represents. + */ + @Deprecated + @ScheduledForRemoval public long getMilliSeconds() { return millis; } /** + * @deprecated Use {@link Timespan#getAs(TimePeriod)} + * * @return the amount of Minecraft ticks this timespan represents. */ + @Deprecated + @ScheduledForRemoval public long getTicks() { return Math.round((millis / 50.0)); } + /** + * @return the amount of TimePeriod this timespan represents. + */ + public long getAs(TimePeriod timePeriod) { + return Math.round(millis * timePeriod.getTime()); + } + /** * @deprecated Use getTicks() instead. Old API naming changes. */ @@ -187,66 +273,62 @@ public String toString() { return toString(millis); } - public String toString(final int flags) { + public String toString(int flags) { return toString(millis, flags); } - @SuppressWarnings("unchecked") - static final NonNullPair[] simpleValues = new NonNullPair[] { - new NonNullPair<>(m_day, 1000L * 60 * 60 * 24), - new NonNullPair<>(m_hour, 1000L * 60 * 60), - new NonNullPair<>(m_minute, 1000L * 60), - new NonNullPair<>(m_second, 1000L) - }; - - public static String toString(final long millis) { + public static String toString(long millis) { return toString(millis, 0); } - public static String toString(final long millis, final int flags) { - for (int i = 0; i < simpleValues.length - 1; i++) { - if (millis >= simpleValues[i].getSecond()) { - final double second = 1. * (millis % simpleValues[i].getSecond()) / simpleValues[i + 1].getSecond(); + @SuppressWarnings("null") + public static String toString(long millis, int flags) { + for (int i = 0; i < SIMPLE_VALUES.size() - 1; i++) { + if (millis >= SIMPLE_VALUES.get(i).getSecond()) { + long remainder = millis % SIMPLE_VALUES.get(i).getSecond(); + double second = 1. * remainder / SIMPLE_VALUES.get(i + 1).getSecond(); if (!"0".equals(Skript.toString(second))) { // bad style but who cares... - return toString(Math.floor(1. * millis / simpleValues[i].getSecond()), simpleValues[i], flags) + " " + GeneralWords.and + " " + toString(second, simpleValues[i + 1], flags); + return toString(Math.floor(1. * millis / SIMPLE_VALUES.get(i).getSecond()), SIMPLE_VALUES.get(i), flags) + " " + GeneralWords.and + " " + toString(remainder, flags); } else { - return toString(1. * millis / simpleValues[i].getSecond(), simpleValues[i], flags); + return toString(1. * millis / SIMPLE_VALUES.get(i).getSecond(), SIMPLE_VALUES.get(i), flags); } } } - return toString(1. * millis / simpleValues[simpleValues.length - 1].getSecond(), simpleValues[simpleValues.length - 1], flags); + return toString(1. * millis / SIMPLE_VALUES.get(SIMPLE_VALUES.size() - 1).getSecond(), SIMPLE_VALUES.get(SIMPLE_VALUES.size() - 1), flags); } - private static String toString(final double amount, final NonNullPair p, final int flags) { - return p.getFirst().withAmount(amount, flags); + private static String toString(double amount, NonNullPair pair, int flags) { + return pair.getFirst().withAmount(amount, flags); } - + + /** + * Compare this Timespan with another + * @param time the Timespan to be compared. + * @return -1 if this Timespan is less than argument Timespan, 0 if equals and 1 if greater than + */ @Override - public int compareTo(final @Nullable Timespan o) { - final long d = o == null ? millis : millis - o.millis; - return d > 0 ? 1 : d < 0 ? -1 : 0; + public int compareTo(@Nullable Timespan time) { + return Long.compare(millis, time == null ? millis : time.millis); } @Override public int hashCode() { - final int prime = 31; + int prime = 31; int result = 1; - result = prime * result + (int) (millis/Integer.MAX_VALUE); + result = prime * result + (int) (millis / Integer.MAX_VALUE); return result; } @Override - public boolean equals(final @Nullable Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof Timespan)) return false; - final Timespan other = (Timespan) obj; - if (millis != other.millis) - return false; - return true; + + return millis == ((Timespan) obj).millis; } } diff --git a/src/main/resources/config.sk b/src/main/resources/config.sk index d15284a06eb..7d7979e2d45 100644 --- a/src/main/resources/config.sk +++ b/src/main/resources/config.sk @@ -88,7 +88,7 @@ player variable fix: true # This fix should work around that and whenever a invalid(old) player object is attempted to be get through a variable # It will check if the player is online and then get the valid(new) player object and update the variable object to that one. -use player UUIDs in variable names: false +use player UUIDs in variable names: true # Whether to use a player's UUID instead of their name in variables, e.g. {home::%player%} will look like # {home::e5240337-a4a2-39dd-8ed9-e5ce729a8522} instead of {home::njol}. # Please note that if this setting is changed old variables WILL NOT be renamed automatically. diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index efd0651babf..2ac67709dc4 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -1934,12 +1934,21 @@ attribute types: generic_attack_speed: generic attack speed, attack speed generic_flying_speed: generic flying speed, flying speed generic_follow_range: generic follow range, follow range + generic_gravity: generic gravity, gravity + generic_jump_strength: generic jump strength, jump strength generic_knockback_resistance: generic knockback resistance, knockback resistance generic_luck: generic luck, luck generic_max_absorption: generic max absorption, max absorption generic_max_health: generic max health, max health generic_movement_speed: generic movement speed, movement speed + generic_safe_fall_distance: generic safe fall distance, safe fall distance + generic_fall_damage_multiplier: generic fall damage multiplier, fall damage multiplier + generic_scale: generic scale, scale + generic_step_height: generic step height, step height horse_jump_strength: horse jump strength + player_block_break_speed: player block break speed, block break speed + player_block_interaction_range: player block interaction range, block interaction range + player_entity_interaction_range: player entity interaction range, entity interaction range zombie_spawn_reinforcements: zombie spawn reinforcements # -- Environments -- diff --git a/src/test/java/org/skriptlang/skript/test/tests/parsing/StaticParseTest.java b/src/test/java/org/skriptlang/skript/test/tests/parsing/StaticParseTest.java new file mode 100644 index 00000000000..b4589f63ccf --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/parsing/StaticParseTest.java @@ -0,0 +1,90 @@ +/** + * 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 . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package org.skriptlang.skript.test.tests.parsing; + +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.patterns.PatternCompiler; +import ch.njol.skript.patterns.SkriptPattern; +import org.junit.Assert; +import org.junit.Test; + +import java.util.stream.Stream; + +public class StaticParseTest { + + private Stream computeParseResults(String text, String pattern) { + ParseResult stringParseResult = SkriptParser.parse(text, pattern, SkriptParser.ALL_FLAGS, ParseContext.COMMAND); + SkriptPattern compiledPattern = PatternCompiler.compile(pattern); + ParseResult compiledParseResult = SkriptParser.parse(text, compiledPattern, SkriptParser.ALL_FLAGS, ParseContext.COMMAND); + return Stream.of(stringParseResult, compiledParseResult); + } + + @Test + public void testMultipleSingleExpressions() { + computeParseResults("1 test 2 test 3", "%number% test %number% test %number%").forEach(result -> { + Assert.assertNotNull("parse method returned null", result); + Assert.assertEquals("parse method returned wrong number of expressions", 3, result.exprs.length); + Assert.assertEquals("parse method returned wrong first expression", 1L, result.exprs[0].getSingle(null)); + Assert.assertEquals("parse method returned wrong second expression", 2L, result.exprs[1].getSingle(null)); + Assert.assertEquals("parse method returned wrong first expression", 3L, result.exprs[2].getSingle(null)); + }); + } + + @Test + public void testMultipleMultipleExpressions() { + computeParseResults("1, 2 and 3 test 4, 5 and 6", "%numbers% test %numbers%").forEach(result -> { + Assert.assertNotNull("parse method returned null", result); + Assert.assertEquals("parse method returned wrong number of expressions", 2, result.exprs.length); + Assert.assertArrayEquals("parse method returned wrong first expression", new Long[]{1L,2L,3L}, result.exprs[0].getArray(null)); + Assert.assertArrayEquals("parse method returned wrong second expression", new Long[]{4L,5L,6L}, result.exprs[1].getArray(null)); + }); + } + + @Test + public void testMultipleMixedExpressions() { + computeParseResults("1 test 2, 3 and 4", "%number% test %numbers%").forEach(result -> { + Assert.assertNotNull("parse method returned null", result); + Assert.assertEquals("parse method returned wrong number of expressions", 2, result.exprs.length); + Assert.assertEquals("parse method returned wrong first expression", 1L, result.exprs[0].getSingle(null)); + Assert.assertArrayEquals("parse method returned wrong second expression", new Long[]{2L,3L,4L}, result.exprs[1].getArray(null)); + }); + } + + @Test + public void testMultipleExpression() { + computeParseResults("1, 2 and 3", "%numbers%").forEach(result -> { + Assert.assertNotNull("parse method returned null", result); + Assert.assertEquals("parse method returned wrong number of expressions", 1, result.exprs.length); + Assert.assertArrayEquals("parse method returned wrong first expression", new Long[]{1L,2L,3L}, result.exprs[0].getArray(null)); + }); + + } + + @Test + public void testSingleExpression() { + computeParseResults("1", "%number%").forEach(result -> { + Assert.assertNotNull("parse method returned null", result); + Assert.assertEquals("parse method returned wrong number of expressions", 1, result.exprs.length); + Assert.assertEquals("parse method returned wrong first expression", 1L, result.exprs[0].getSingle(null)); + }); + } + +} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprCharacters.sk b/src/test/skript/tests/syntaxes/expressions/ExprCharacters.sk index 5c57a474378..0805fb13d85 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprCharacters.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprCharacters.sk @@ -1,5 +1,13 @@ test "characters between": set {_a} to 1 random character between "a" and "z" + assert {_a} is set with "failed to generate random character" + + set {_a::*} to -10 random character between "a" and "z" + assert {_a::*} is not set with "-10 random characters returned non-null value: %{_a::*}%" + + set {_a::*} to {_null} random character between "a" and "z" + assert {_a::*} is not set with "null random characters returned non-null value: %{_a::*}%" + assert join characters between "a" and "d" is "abcd" with "Invalid characters between a and d" assert join characters between "d" and "a" is "dcba" with "Invalid characters between d and a" assert join characters between "Z" and "a" is "Z[\]^_`a" with "Invalid characters between Z and a" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprIndices.sk b/src/test/skript/tests/syntaxes/expressions/ExprIndices.sk index fccca1d6a6d..855e4129b78 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprIndices.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprIndices.sk @@ -23,3 +23,17 @@ test "sorted indices": set {_a} to {_leader-board::%loop-value%} set {_b} to {_descending-values::%loop-index%} assert {_a} is equal to {_b} with "Sorting indices in descending order is incorrect" + +test "alphabetically sorted indices": + set {_test::a} to "Bob" + set {_test::b} to "Donald" + set {_test::c} to "Zeffer" + set {_test::d} to "Angus" + set {_test::e} to "Kevin" + set {_test::f} to "anderson" + set {_test::g} to "robertson" + + set {_indexes::*} to sorted indices of {_test::*} in ascending order + + assert {_test::%{_indexes::1}%} = "anderson" with "First element of sorted strings should be 'anderson'" + assert {_test::%{_indexes::7}%} = "Zeffer" with "Last element of sorted strings should be 'Zeffer'"