diff --git a/.gitignore b/.gitignore index fbddca8fd46..47df9cbc0de 100755 --- a/.gitignore +++ b/.gitignore @@ -223,3 +223,6 @@ gradle-app.setting # TODO remove this in the future after some time since https://github.com/SkriptLang/Skript/pull/4979 # This ensures developers don't upload their old existing test_runners/ folder. test_runners/ + +## Mac MetaData +**/.DS_Store diff --git a/build.gradle b/build.gradle index 7ce7614c3b0..8e673cbbb6a 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ allprojects { dependencies { shadow group: 'io.papermc', name: 'paperlib', version: '1.0.8' shadow group: 'org.bstats', name: 'bstats-bukkit', version: '3.0.2' - shadow group: 'net.kyori', name: 'adventure-text-serializer-bungeecord', version: '4.3.0' + shadow group: 'net.kyori', name: 'adventure-text-serializer-bungeecord', version: '4.3.1' implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.20.2-R0.1-SNAPSHOT' implementation group: 'org.eclipse.jdt', name: 'org.eclipse.jdt.annotation', version: '2.2.700' @@ -150,7 +150,7 @@ license { } task releaseJavadoc(type: Javadoc) { - title = project.property('version') + title = project.name + ' ' + project.property('version') source = sourceSets.main.allJava classpath = configurations.compileClasspath options.encoding = 'UTF-8' @@ -223,7 +223,7 @@ void createTestTask(String name, String desc, String environments, int javaVersi main = 'ch.njol.skript.test.platform.PlatformMain' args = [ 'build/test_runners', - junit ? 'src/test/skript/tests/junit' : 'src/test/skript/tests', + junit ? 'src/test/skript/junit' : 'src/test/skript/tests', 'src/test/resources/runner_data', environments, modifiers.contains(Modifiers.DEV_MODE), diff --git a/gradle.properties b/gradle.properties index 1d5c2fb3e73..ff0925c17ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,9 @@ +# Ensure encoding is consistent across systems +org.gradle.jvmargs=-Dfile.encoding=UTF-8 + groupid=ch.njol name=skript -version=2.7.1 +version=2.7.2 jarName=Skript.jar testEnv=java17/paper-1.20.2 testEnvJavaVersion=17 diff --git a/skript-aliases b/skript-aliases index 1ee77d8573a..0884ede0fdf 160000 --- a/skript-aliases +++ b/skript-aliases @@ -1 +1 @@ -Subproject commit 1ee77d8573aa37456f1b49fe12aec7bb410d1dd7 +Subproject commit 0884ede0fdf69e914b944500d9f24f1c000a90a2 diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 9740ee27efa..522a9e8133e 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -591,6 +591,8 @@ public void run() { tainted = true; try { getAddonInstance().loadClasses("ch.njol.skript.test.runner"); + if (TestMode.JUNIT) + getAddonInstance().loadClasses("org.skriptlang.skript.test.junit.registration"); } catch (IOException e) { Skript.exception("Failed to load testing environment."); Bukkit.getServer().shutdown(); @@ -684,7 +686,6 @@ protected void afterErrors() { TestTracker.testFailed("exception was thrown during execution"); } if (TestMode.JUNIT) { - SkriptLogger.setVerbosity(Verbosity.DEBUG); info("Running all JUnit tests..."); long milliseconds = 0, tests = 0, fails = 0, ignored = 0, size = 0; try { @@ -712,7 +713,7 @@ protected void afterErrors() { // If JUnit failures are present, add them to the TestTracker. junit.getFailures().forEach(failure -> { String message = failure.getMessage() == null ? "" : " " + failure.getMessage(); - TestTracker.testFailed("'" + test + "': " + message); + TestTracker.JUnitTestFailed(test, message); Skript.exception(failure.getException(), "JUnit test '" + failure.getTestHeader() + " failed."); }); SkriptJUnitTest.clearJUnitTest(); @@ -734,7 +735,7 @@ protected void afterErrors() { // Delay server shutdown to stop the server from crashing because the current tick takes a long time due to all the tests Bukkit.getScheduler().runTaskLater(Skript.this, () -> { if (TestMode.JUNIT && !EffObjectives.isJUnitComplete()) - TestTracker.testFailed(EffObjectives.getFailedObjectivesString()); + EffObjectives.fail(); info("Collecting results to " + TestMode.RESULTS_FILE); String results = new Gson().toJson(TestTracker.collectResults()); @@ -1261,7 +1262,7 @@ public static boolean isAcceptRegistrations() { } public static void checkAcceptRegistrations() { - if (!isAcceptRegistrations()) + if (!isAcceptRegistrations() && !Skript.testing()) throw new SkriptAPIException("Registration can only be done during plugin initialization"); } diff --git a/src/main/java/ch/njol/skript/aliases/ItemType.java b/src/main/java/ch/njol/skript/aliases/ItemType.java index 7c189f72bf6..bc5b8aa6ca1 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemType.java +++ b/src/main/java/ch/njol/skript/aliases/ItemType.java @@ -18,37 +18,6 @@ */ package ch.njol.skript.aliases; -import java.io.NotSerializableException; -import java.io.StreamCorruptedException; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; -import java.util.Random; -import java.util.RandomAccess; -import java.util.Set; - -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.OfflinePlayer; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; -import org.bukkit.block.Skull; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.SkullMeta; -import org.eclipse.jdt.annotation.Nullable; - import ch.njol.skript.aliases.ItemData.OldItemData; import ch.njol.skript.bukkitutil.BukkitUnsafe; import ch.njol.skript.bukkitutil.ItemUtils; @@ -68,14 +37,44 @@ import ch.njol.yggdrasil.Fields; import ch.njol.yggdrasil.Fields.FieldContext; import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilExtendedSerializable; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.Skull; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.eclipse.jdt.annotation.Nullable; + +import java.io.NotSerializableException; +import java.io.StreamCorruptedException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Random; +import java.util.RandomAccess; +import java.util.Set; @ContainerType(ItemStack.class) public class ItemType implements Unit, Iterable, Container, YggdrasilExtendedSerializable { - + static { // This handles updating ItemType and ItemData variable records Variables.yggdrasil.registerFieldHandler(new FieldHandler() { - + @Override public boolean missingField(Object o, Field field) throws StreamCorruptedException { if (!(o instanceof ItemType || o instanceof ItemData)) @@ -84,12 +83,12 @@ public boolean missingField(Object o, Field field) throws StreamCorruptedExcepti return true; // Just null, no need for updating that data return false; } - + @Override public boolean incompatibleField(Object o, Field f, FieldContext field) throws StreamCorruptedException { return false; } - + @Override public boolean excessiveField(Object o, FieldContext field) throws StreamCorruptedException { if (!(o instanceof ItemType || o instanceof ItemData)) @@ -101,7 +100,7 @@ public boolean excessiveField(Object o, FieldContext field) throws StreamCorrupt } }); } - + /** * DO NOT ADD ItemDatas to this list directly! *

@@ -109,31 +108,31 @@ public boolean excessiveField(Object o, FieldContext field) throws StreamCorrupt * can have its own ItemMeta. */ final ArrayList types = new ArrayList<>(2); - + /** * Whether this ItemType represents all types or not. */ private boolean all = false; - + /** * Amount determines how many items this type represents. Negative amounts * are treated as their absolute values when adding items to inventories * and otherwise used as "doesn't matter" flags. */ private int amount = -1; - + /** * ItemTypes to use instead of this one if adding to an inventory or setting a block. */ @Nullable private ItemType item = null, block = null; - + /** * Meta that applies for all ItemDatas there. */ @Nullable private ItemMeta globalMeta; - + void setItem(final @Nullable ItemType item) { if (equals(item)) { // can happen if someone defines a 'x' and 'x item/block' alias that have the same value, e.g. 'dirt' and 'dirt block' this.item = null; @@ -149,7 +148,7 @@ void setItem(final @Nullable ItemType item) { this.item = item; } } - + void setBlock(final @Nullable ItemType block) { if (equals(block)) { this.block = null; @@ -167,30 +166,30 @@ void setBlock(final @Nullable ItemType block) { } public ItemType() {} - + public ItemType(Material id) { add_(new ItemData(id)); } - + public ItemType(Material id, String tags) { add_(new ItemData(id, tags)); } - + public ItemType(ItemData d) { add_(d.clone()); } - + public ItemType(ItemStack i) { amount = i.getAmount(); add_(new ItemData(i)); } - + public ItemType(BlockState b) { // amount = 1; add_(new ItemData(b)); // TODO metadata - spawners, skulls, etc. } - + /** * Copy constructor. * @param i Another ItemType. @@ -198,7 +197,7 @@ public ItemType(BlockState b) { private ItemType(ItemType i) { setTo(i); } - + public void setTo(ItemType i) { all = i.all; amount = i.amount; @@ -221,7 +220,7 @@ public ItemType(Block block) { public void modified() { item = block = null; } - + /** * Returns amount of the item in stack that this type represents. * @return amount. @@ -230,22 +229,22 @@ public void modified() { public int getAmount() { return Math.abs(amount); } - + /** * Only use this method if you know what you're doing. - * + * * @return The internal amount, i.e. same as {@link #getAmount()} * or additive inverse number of it. */ public int getInternalAmount() { return amount; } - + @Override public void setAmount(final double amount) { setAmount((int) amount); } - + public void setAmount(final int amount) { this.amount = amount; if (item != null) @@ -253,7 +252,7 @@ public void setAmount(final int amount) { if (block != null) block.amount = amount; } - + /** * Checks if this item type represents one of its items (OR) or all of * them (AND). If this has only one item, it doesn't matter. @@ -262,30 +261,30 @@ public void setAmount(final int amount) { public boolean isAll() { return all; } - + public void setAll(final boolean all) { this.all = all; } - + public boolean isOfType(@Nullable ItemStack item) { if (item == null) return isOfType(Material.AIR, null); return isOfType(new ItemData(item)); } - + public boolean isOfType(@Nullable BlockState block) { if (block == null) return isOfType(Material.AIR, null); - + return isOfType(new ItemData(block)); } - + public boolean isOfType(@Nullable Block block) { if (block == null) return isOfType(Material.AIR, null); return isOfType(block.getState()); } - + public boolean isOfType(ItemData type) { for (final ItemData myType : types) { if (myType.equals(type)) { @@ -294,16 +293,16 @@ public boolean isOfType(ItemData type) { } return false; } - + public boolean isOfType(Material id, @Nullable String tags) { return isOfType(new ItemData(id, tags)); } - + public boolean isOfType(Material id) { // TODO avoid object creation return isOfType(new ItemData(id, null)); } - + /** * Checks if this type represents all the items represented by given * item type. This type may of course also represent other items. @@ -313,17 +312,17 @@ public boolean isOfType(Material id) { public boolean isSupertypeOf(ItemType other) { return types.containsAll(other.types); } - + public ItemType getItem() { final ItemType item = this.item; return item == null ? this : item; } - + public ItemType getBlock() { final ItemType block = this.block; return block == null ? this : block; } - + /** * @return Whether this ItemType has at least one ItemData that represents an item */ @@ -334,7 +333,7 @@ public boolean hasItem() { } return false; } - + /** * @return Whether this ItemType has at least one ItemData that represents a block */ @@ -345,10 +344,10 @@ public boolean hasBlock() { } return false; } - + /** * Sets the given block to this ItemType - * + * * @param block The block to set * @param applyPhysics Whether to run a physics check just after setting the block * @return Whether the block was successfully set @@ -374,7 +373,7 @@ public boolean setBlock(Block block, boolean applyPhysics) { } return false; } - + /** * Send a block change to a player *

This will send a fake block change to the player, and will not change the block on the server.

@@ -391,11 +390,11 @@ public void sendBlockChange(Player player, Location location) { BlockUtils.sendBlockChange(player, location, blockType, d.getBlockValues()); } } - + /** * Intersects all ItemDatas with all ItemDatas of the given ItemType, returning an ItemType with at most n*m ItemDatas, where n = #ItemDatas of this ItemType, and m = * #ItemDatas of the argument. - * + * * @see ItemData#intersection(ItemData) * @param other * @return A new item type which is the intersection of the two item types or null if the intersection is empty. @@ -413,7 +412,7 @@ public ItemType intersection(ItemType other) { return null; return r; } - + /** * @param type Some ItemData. Only a copy of it will be stored. */ @@ -422,7 +421,7 @@ public void add(@Nullable ItemData type) { add_(type.clone()); } } - + /** * @param type A cloned or newly created ItemData */ @@ -433,36 +432,36 @@ private void add_(@Nullable ItemData type) { modified(); } } - + public void addAll(Collection types) { this.types.addAll(types); modified(); } - + public void remove(ItemData type) { if (types.remove(type)) { //numItems -= type.numItems(); modified(); } } - + void remove(int index) { types.remove(index); //numItems -= type.numItems(); modified(); } - + @Override public Iterator containerIterator() { return new Iterator() { @SuppressWarnings("null") Iterator iter = types.iterator(); - + @Override public boolean hasNext() { return iter.hasNext(); } - + @Override public ItemStack next() { if (!hasNext()) @@ -471,17 +470,17 @@ public ItemStack next() { is.setAmount(getAmount()); return is; } - + @Override public void remove() { throw new UnsupportedOperationException(); } }; } - + /** * Gets all ItemStacks this ItemType represents. Only use this if you know what you're doing, as it returns only one element if this is not an 'every' alias. - * + * * @return An Iterable whose iterator will always return the same item(s) */ public Iterable getAll() { @@ -498,7 +497,7 @@ public Iterator iterator() { } }; } - + @Nullable public ItemStack removeAll(@Nullable ItemStack item) { boolean wasAll = all; @@ -512,10 +511,10 @@ public ItemStack removeAll(@Nullable ItemStack item) { amount = oldAmount; } } - + /** * Removes this type from the item stack if appropriate - * + * * @param item * @return The passed ItemStack or null if the resulting amount is <= 0 */ @@ -533,10 +532,10 @@ public ItemStack removeFrom(@Nullable ItemStack item) { item.setAmount(a); return item; } - + /** * Adds this ItemType to the given item stack - * + * * @param item * @return The passed ItemStack or a new one if the passed is null or air */ @@ -548,14 +547,14 @@ public ItemStack addTo(final @Nullable ItemStack item) { item.setAmount(Math.min(item.getAmount() + getAmount(), item.getMaxStackSize())); return item; } - + @Override public ItemType clone() { return new ItemType(this); } - + private final static Random random = new Random(); - + /** * @return One random ItemStack that this ItemType represents. If you have a List or an Inventory, use {@link #addTo(Inventory)} or {@link #addTo(List)} respectively. * @see #addTo(Inventory) @@ -573,13 +572,13 @@ public ItemStack getRandom() { is.setAmount(getAmount()); return is; } - + /** * Test whether this ItemType can be put into the given inventory completely. *

* REMIND If this ItemType represents multiple items with OR, this function will immediately return false.
* CondCanHold currently blocks aliases without 'every'/'all' as temporary solution. - * + * * @param invi * @return Whether this item type can be added to the given inventory */ @@ -590,7 +589,7 @@ public boolean hasSpace(final Inventory invi) { } return addTo(getStorageContents(invi)); } - + public static ItemStack[] getCopiedContents(Inventory invi) { final ItemStack[] buf = invi.getContents(); for (int i = 0; i < buf.length; i++) @@ -598,7 +597,7 @@ public static ItemStack[] getCopiedContents(Inventory invi) { buf[i] = buf[i].clone(); return buf; } - + /** * Gets copy of storage contents, i.e. ignores armor and off hand. This is due to Spigot 1.9 * added armor slots, and off hand to default inventory index. @@ -615,7 +614,7 @@ public static ItemStack[] getStorageContents(final Inventory invi) { return tBuf; } else return getCopiedContents(invi); } - + /** * @return List of ItemDatas. The returned list is not modifiable, use {@link #add(ItemData)} and {@link #remove(ItemData)} if you need to change the list, or use the * {@link #iterator()}. @@ -624,28 +623,28 @@ public static ItemStack[] getStorageContents(final Inventory invi) { public List getTypes() { return Collections.unmodifiableList(types); } - + public int numTypes() { return types.size(); } - + /** * @return How many different items this item type represents */ public int numItems() { return types.size(); } - + @Override public Iterator iterator() { return new Iterator() { private int next = 0; - + @Override public boolean hasNext() { return next < types.size(); } - + @SuppressWarnings("null") @Override public ItemData next() { @@ -653,7 +652,7 @@ public ItemData next() { throw new NoSuchElementException(); return types.get(next++); } - + @Override public void remove() { if (next <= 0) @@ -662,7 +661,7 @@ public void remove() { } }; } - + public boolean isContainedIn(Iterable items) { int needed = getAmount(); int found = 0; @@ -698,7 +697,7 @@ public boolean isContainedIn(ItemStack[] items) { return false; return all; } - + public boolean removeAll(Inventory invi) { final boolean wasAll = all; final int oldAmount = amount; @@ -711,22 +710,22 @@ public boolean removeAll(Inventory invi) { amount = oldAmount; } } - + /** * Removes this type from the given inventory. Does not call updateInventory for players. - * + * * @param invi * @return Whether everything could be removed from the inventory */ public boolean removeFrom(Inventory invi) { ItemStack[] buf = getCopiedContents(invi); - + final boolean ok = removeFrom(Arrays.asList(buf)); - + invi.setContents(buf); return ok; } - + @SafeVarargs public final boolean removeAll(List... lists) { final boolean wasAll = all; @@ -740,16 +739,19 @@ public final boolean removeAll(List... lists) { amount = oldAmount; } } - + /** - * @param lists The lists to remove this type from. Each list should implement {@link RandomAccess}. + * Removes this ItemType from given lists of ItemStacks. + * If an ItemStack is completely removed, that index in the list is set to null, instead of being removed. + * + * @param lists The lists to remove this type from. Each list should implement {@link RandomAccess}. Lists may contain null values after this method. * @return Whether this whole item type could be removed (i.e. returns false if the lists didn't contain this item type completely) */ @SafeVarargs public final boolean removeFrom(final List... lists) { int removed = 0; boolean ok = true; - + for (final ItemData d : types) { if (all) removed = 0; @@ -762,7 +764,7 @@ public final boolean removeFrom(final List... lists) { /* * Do NOT use equals()! It doesn't exactly match items * for historical reasons. This will change in future. - * + * * In Skript 2.3, equals() was used for getting closest * possible aliases for items. It was horribly hacky, and * is not done anymore. Still, some uses of equals() expect @@ -799,15 +801,15 @@ public final boolean removeFrom(final List... lists) { if (all) ok &= removed == getAmount(); } - + if (!all) return false; return ok; } - + /** * Adds this ItemType to the given list, without filling existing stacks. - * + * * @param list */ public void addTo(final List list) { @@ -818,17 +820,17 @@ public void addTo(final List list) { for (final ItemStack is : getItem().getAll()) list.add(is); } - + /** * Tries to add this ItemType to the given inventory. Does not call updateInventory for players. - * + * * @param invi * @return Whether everything could be added to the inventory */ public boolean addTo(final Inventory invi) { // important: don't use inventory.add() - it ignores max stack sizes ItemStack[] buf = invi.getContents(); - + ItemStack[] tBuf = buf.clone(); if (invi instanceof PlayerInventory) { buf = new ItemStack[36]; @@ -836,21 +838,21 @@ public boolean addTo(final Inventory invi) { buf[i] = tBuf[i]; } } - + final boolean b = addTo(buf); - + if (invi instanceof PlayerInventory) { buf = Arrays.copyOf(buf, tBuf.length); for (int i = tBuf.length - 5; i < tBuf.length; ++i) { buf[i] = tBuf[i]; } } - + assert buf != null; invi.setContents(buf); return b; } - + private static boolean addTo(@Nullable ItemStack is, ItemStack[] buf) { if (is == null || is.getType() == Material.AIR) return true; @@ -876,7 +878,7 @@ private static boolean addTo(@Nullable ItemStack is, ItemStack[] buf) { } return false; } - + public boolean addTo(final ItemStack[] buf) { if (!isAll()) { return addTo(getItem().getRandom(), buf); @@ -887,12 +889,12 @@ public boolean addTo(final ItemStack[] buf) { } return ok; } - + /** * Tests whether a given set of ItemTypes is a subset of another set of ItemTypes. *

* This method works differently that normal set operations, as is e.g. returns true if set == {everything}. - * + * * @param set * @param sub * @return Whether all item types in sub have at least one {@link #isSupertypeOf(ItemType) super type} in set @@ -908,7 +910,7 @@ public static boolean isSubset(final ItemType[] set, final ItemType[] sub) { } return true; } - + @Override public boolean equals(final @Nullable Object obj) { if (this == obj) @@ -965,7 +967,7 @@ public boolean isSimilar(ItemType other) { } return false; } - + @Override public int hashCode() { final int prime = 31; @@ -975,21 +977,21 @@ public int hashCode() { result = prime * result + types.hashCode(); return result; } - + @Override public String toString() { return toString(false, 0, null); } - + @Override public String toString(final int flags) { return toString(false, flags, null); } - + public String toString(final int flags, final @Nullable Adjective a) { return toString(false, flags, a); } - + private String toString(final boolean debug, final int flags, final @Nullable Adjective a) { final StringBuilder b = new StringBuilder(); // if (types.size() == 1 && !types.get(0).hasDataRange()) { @@ -1050,33 +1052,33 @@ private String toString(final boolean debug, final int flags, final @Nullable Ad // } return "" + b.toString(); } - + public static String toString(final ItemStack i) { return new ItemType(i).toString(); } - + public static String toString(final ItemStack i, final int flags) { return new ItemType(i).toString(flags); } - + public static String toString(Block b, int flags) { return new ItemType(b).toString(flags); } - + public String getDebugMessage() { return toString(true, 0, null); } - + @Override public Fields serialize() throws NotSerializableException { final Fields f = new Fields(this); return f; } - + @Override public void deserialize(final Fields fields) throws StreamCorruptedException, NotSerializableException { fields.setFields(this); - + // Legacy data (before aliases rework) update if (!types.isEmpty()) { @SuppressWarnings("rawtypes") @@ -1095,7 +1097,7 @@ public void deserialize(final Fields fields) throws StreamCorruptedException, No } } } - + /** * Gets raw item names ("minecraft:some_item"). If they are not available, * empty list will be returned. @@ -1109,10 +1111,10 @@ public List getRawNames() { if (id != null) rawNames.add(id); } - + return rawNames; } - + /** * Gets all enchantments of this item. * @return Enchantments. @@ -1129,7 +1131,7 @@ public Map getEnchantments() { return null; return enchants; } - + /** * Adds enchantments to this item type. * @param enchantments Enchantments. @@ -1144,7 +1146,7 @@ public void addEnchantments(Map enchantments) { globalMeta.addEnchant(entry.getKey(), entry.getValue(), true); } } - + /** * Gets all enchantments of this item. * @return the enchantments of this item type. @@ -1152,7 +1154,7 @@ public void addEnchantments(Map enchantments) { @Nullable public EnchantmentType[] getEnchantmentTypes() { Set> enchants = getItemMeta().getEnchants().entrySet(); - + return enchants.stream() .map(enchant -> new EnchantmentType(enchant.getKey(), enchant.getValue())) .toArray(EnchantmentType[]::new); @@ -1174,14 +1176,14 @@ public EnchantmentType getEnchantmentType(Enchantment enchantment) { .findFirst() .orElse(null); } - + /** * Checks whether this item type has enchantments. */ public boolean hasEnchantments() { return getItemMeta().hasEnchants(); } - + /** * Checks whether this item type has the given enchantments. * @param enchantments the enchantments to be checked. @@ -1190,14 +1192,14 @@ public boolean hasEnchantments(Enchantment... enchantments) { if (!hasEnchantments()) return false; ItemMeta meta = getItemMeta(); - + for (Enchantment enchantment : enchantments) { if (!meta.hasEnchant(enchantment)) return false; } return true; } - + /** * Checks whether this item type contains at most one of the given enchantments. * @param enchantments The enchantments to be checked. @@ -1206,7 +1208,7 @@ public boolean hasAnyEnchantments(Enchantment... enchantments) { if (!hasEnchantments()) return false; ItemMeta meta = getItemMeta(); - + for (Enchantment enchantment : enchantments) { assert enchantment != null; if (meta.hasEnchant(enchantment)) @@ -1214,7 +1216,7 @@ public boolean hasAnyEnchantments(Enchantment... enchantments) { } return false; } - + /** * Checks whether this item type contains the given enchantments. * Also checks the enchantment level. @@ -1234,14 +1236,14 @@ public boolean hasEnchantments(EnchantmentType... enchantments) { } return true; } - + /** * Adds the given enchantments to the item type. * @param enchantments The enchantments to be added. */ public void addEnchantments(EnchantmentType... enchantments) { ItemMeta meta = getItemMeta(); - + for (EnchantmentType enchantment : enchantments) { Enchantment type = enchantment.getType(); assert type != null; // Bukkit working different than we expect @@ -1249,14 +1251,14 @@ public void addEnchantments(EnchantmentType... enchantments) { } setItemMeta(meta); } - + /** * Removes the given enchantments from this item type. * @param enchantments The enchantments to be removed. */ public void removeEnchantments(EnchantmentType... enchantments) { ItemMeta meta = getItemMeta(); - + for (EnchantmentType enchantment : enchantments) { Enchantment type = enchantment.getType(); assert type != null; // Bukkit working different than we expect @@ -1264,14 +1266,14 @@ public void removeEnchantments(EnchantmentType... enchantments) { } setItemMeta(meta); } - + /** * Clears all enchantments from this item type except the ones that are * defined for individual item datas only. */ public void clearEnchantments() { ItemMeta meta = getItemMeta(); - + Set enchants = meta.getEnchants().keySet(); for (Enchantment ench : enchants) { assert ench != null; @@ -1279,7 +1281,7 @@ public void clearEnchantments() { } setItemMeta(meta); } - + /** * Gets item meta that applies to all items represented by this type. * @return Item meta. @@ -1295,13 +1297,13 @@ public ItemMeta getItemMeta() { */ public void setItemMeta(ItemMeta meta) { globalMeta = meta; - + // Apply new meta to all datas for (ItemData data : types) { data.setItemMeta(meta); } } - + /** * Clears item meta from this type. Metas which individual item dates may * have will not be touched. diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index ccf12240d29..f7e67d52610 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -64,6 +64,7 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.PlayerResourcePackStatusEvent.Status; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.BlockInventoryHolder; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; @@ -873,6 +874,10 @@ public String toString(InventoryHolder holder, int flags) { return Classes.toString(((BlockState) holder).getBlock()); } else if (holder instanceof DoubleChest) { return Classes.toString(holder.getInventory().getLocation().getBlock()); + } else if (holder instanceof BlockInventoryHolder) { + return Classes.toString(((BlockInventoryHolder) holder).getBlock()); + } else if (Classes.getSuperClassInfo(holder.getClass()).getC() == InventoryHolder.class) { + return holder.getClass().getSimpleName(); // an inventory holder and only that } else { return Classes.toString(holder); } @@ -1410,7 +1415,7 @@ public String toVariableNameString(FireworkEffect effect) { .user("(panda )?genes?") .name("Gene") .description("Represents a Panda's main or hidden gene. " + - "See genetics for more info.") + "See genetics for more info.") .since("2.4") .requiredPlugins("Minecraft 1.14 or newer")); } @@ -1486,7 +1491,7 @@ public String toVariableNameString(EnchantmentOffer eo) { .user("attribute ?types?") .name("Attribute Type") .description("Represents the type of an attribute. Note that this type does not contain any numerical values." - + "See attribute types for more info.") + + "See attribute types for more info.") .since("2.5")); Classes.registerClass(new EnumClassInfo<>(Environment.class, "environment", "environments") diff --git a/src/main/java/ch/njol/skript/command/CommandHelp.java b/src/main/java/ch/njol/skript/command/CommandHelp.java index 8fcf6f0a3b4..4732ba2cde5 100644 --- a/src/main/java/ch/njol/skript/command/CommandHelp.java +++ b/src/main/java/ch/njol/skript/command/CommandHelp.java @@ -23,6 +23,7 @@ import java.util.LinkedHashMap; import java.util.Locale; +import java.util.Map; import java.util.Map.Entry; import org.bukkit.command.CommandSender; @@ -33,118 +34,119 @@ import ch.njol.skript.localization.Message; import ch.njol.skript.util.SkriptColor; -/** - * @author Peter Güttinger - */ public class CommandHelp { - + private final static String DEFAULTENTRY = "description"; - + private final static ArgsMessage m_invalid_argument = new ArgsMessage("commands.invalid argument"); private final static Message m_usage = new Message("skript command.usage"); - + + private final String actualCommand, actualNode, argsColor; private String command; + private String langNode; @Nullable - private Message description = null; - private final String argsColor; - - @Nullable - private String langNode = null; - - private final LinkedHashMap arguments = new LinkedHashMap<>(); - + private Message description; + + private final Map arguments = new LinkedHashMap<>(); + @Nullable - private Message wildcardArg = null; - - public CommandHelp(final String command, final SkriptColor argsColor, final String langNode) { - this.command = command; - this.argsColor = "" + argsColor.getFormattedChat(); - this.langNode = langNode; - description = new Message(langNode + "." + DEFAULTENTRY); + private ArgumentHolder wildcardArg = null; + + public CommandHelp(String command, SkriptColor argsColor, String langNode) { + this(command, argsColor.getFormattedChat(), langNode, new Message(langNode + "." + DEFAULTENTRY)); } - - public CommandHelp(final String command, final SkriptColor argsColor) { - this.command = command; - this.argsColor = "" + argsColor.getFormattedChat(); + + public CommandHelp(String command, SkriptColor argsColor) { + this(command, argsColor.getFormattedChat(), command, null); } - - public CommandHelp add(final String argument) { - if (langNode == null) { - if (argument.startsWith("<") && argument.endsWith(">")) { - final String carg = GRAY + "<" + argsColor + argument.substring(1, argument.length() - 1) + GRAY + ">"; - arguments.put(carg, argument); - } else { - arguments.put(argument, null); - } - } else { - if (argument.startsWith("<") && argument.endsWith(">")) { - final String carg = GRAY + "<" + argsColor + argument.substring(1, argument.length() - 1) + GRAY + ">"; - wildcardArg = new Message(langNode + "." + argument); - arguments.put(carg, wildcardArg); - } else { - arguments.put(argument, new Message(langNode + "." + argument)); - } + + private CommandHelp(String command, String argsColor, String node, @Nullable Message description) { + this.actualCommand = this.command = command; + this.actualNode = this.langNode = node; + this.argsColor = argsColor; + this.description = description; + } + + public CommandHelp add(String argument) { + ArgumentHolder holder = new ArgumentHolder(argument); + if (argument.startsWith("<") && argument.endsWith(">")) { + argument = GRAY + "<" + argsColor + argument.substring(1, argument.length() - 1) + GRAY + ">"; + wildcardArg = holder; } + arguments.put(argument, holder); return this; } - - public CommandHelp add(final CommandHelp help) { + + public CommandHelp add(CommandHelp help) { arguments.put(help.command, help); help.onAdd(this); return this; } - - protected void onAdd(final CommandHelp parent) { - langNode = parent.langNode + "." + command; + + protected void onAdd(CommandHelp parent) { + langNode = parent.langNode + "." + actualNode; description = new Message(langNode + "." + DEFAULTENTRY); - command = parent.command + " " + parent.argsColor + command; - for (final Entry e : arguments.entrySet()) { - if (e.getValue() instanceof CommandHelp) { - ((CommandHelp) e.getValue()).onAdd(this); - } else { - if (e.getValue() != null) { // wildcard arg - wildcardArg = new Message(langNode + "." + e.getValue()); - e.setValue(wildcardArg); - } else { - e.setValue(new Message(langNode + "." + e.getKey())); - } + command = parent.command + " " + parent.argsColor + actualCommand; + for (Entry entry : arguments.entrySet()) { + if (entry.getValue() instanceof CommandHelp) { + ((CommandHelp) entry.getValue()).onAdd(this); + continue; } + ((ArgumentHolder) entry.getValue()).update(); } } - - public boolean test(final CommandSender sender, final String[] args) { + + public boolean test(CommandSender sender, String[] args) { return test(sender, args, 0); } - - private boolean test(final CommandSender sender, final String[] args, final int index) { + + private boolean test(CommandSender sender, String[] args, int index) { if (index >= args.length) { showHelp(sender); return false; } - final Object help = arguments.get(args[index].toLowerCase(Locale.ENGLISH)); + Object help = arguments.get(args[index].toLowerCase(Locale.ENGLISH)); if (help == null && wildcardArg == null) { showHelp(sender, m_invalid_argument.toString(argsColor + args[index])); return false; } - if (help instanceof CommandHelp) - return ((CommandHelp) help).test(sender, args, index + 1); - return true; + return !(help instanceof CommandHelp) || ((CommandHelp) help).test(sender, args, index + 1); } - - public void showHelp(final CommandSender sender) { + + public void showHelp(CommandSender sender) { showHelp(sender, m_usage.toString()); } - - private void showHelp(final CommandSender sender, final String pre) { + + private void showHelp(CommandSender sender, String pre) { Skript.message(sender, pre + " " + command + " " + argsColor + "..."); - for (final Entry e : arguments.entrySet()) { - Skript.message(sender, " " + argsColor + e.getKey() + " " + GRAY + "-" + RESET + " " + e.getValue()); - } + for (Entry entry : arguments.entrySet()) + Skript.message(sender, " " + argsColor + entry.getKey() + " " + GRAY + "-" + RESET + " " + entry.getValue()); } - + @Override public String toString() { return "" + description; } - + + private class ArgumentHolder { + + private final String argument; + private Message description; + + private ArgumentHolder(String argument) { + this.argument = argument; + this.description = new Message(langNode + "." + argument); + } + + private void update() { + description = new Message(langNode + "." + argument); + } + + @Override + public String toString() { + return description.toString(); + } + + } + } diff --git a/src/main/java/ch/njol/skript/command/Commands.java b/src/main/java/ch/njol/skript/command/Commands.java index 35e12057bb1..e6cb7456be5 100644 --- a/src/main/java/ch/njol/skript/command/Commands.java +++ b/src/main/java/ch/njol/skript/command/Commands.java @@ -41,6 +41,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.server.ServerCommandEvent; import org.bukkit.help.HelpMap; import org.bukkit.help.HelpTopic; @@ -68,7 +69,7 @@ */ @SuppressWarnings("deprecation") public abstract class Commands { - + public final static ArgsMessage m_too_many_arguments = new ArgsMessage("commands.too many arguments"); public final static Message m_internal_error = new Message("commands.internal error"); public final static Message m_correct_usage = new Message("commands.correct usage"); @@ -79,26 +80,26 @@ public abstract class Commands { public static final int CONVERTER_NO_COMMAND_ARGUMENTS = 4; private final static Map commands = new HashMap<>(); - + @Nullable private static SimpleCommandMap commandMap = null; @Nullable private static Map cmKnownCommands; @Nullable private static Set cmAliases; - + static { init(); // separate method for the annotation } public static Set getScriptCommands(){ return commands.keySet(); } - + @Nullable public static SimpleCommandMap getCommandMap(){ return commandMap; } - + @SuppressWarnings("unchecked") private static void init() { try { @@ -106,11 +107,11 @@ private static void init() { Field commandMapField = SimplePluginManager.class.getDeclaredField("commandMap"); commandMapField.setAccessible(true); commandMap = (SimpleCommandMap) commandMapField.get(Bukkit.getPluginManager()); - + Field knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands"); knownCommandsField.setAccessible(true); cmKnownCommands = (Map) knownCommandsField.get(commandMap); - + try { Field aliasesField = SimpleCommandMap.class.getDeclaredField("aliases"); aliasesField.setAccessible(true); @@ -125,25 +126,42 @@ private static void init() { commandMap = null; } } - + @Nullable public static List> currentArguments = null; - + @SuppressWarnings("null") private final static Pattern escape = Pattern.compile("[" + Pattern.quote("(|)<>%\\") + "]"); @SuppressWarnings("null") private final static Pattern unescape = Pattern.compile("\\\\[" + Pattern.quote("(|)<>%\\") + "]"); - + public static String escape(String string) { return "" + escape.matcher(string).replaceAll("\\\\$0"); } - + public static String unescape(String string) { return "" + unescape.matcher(string).replaceAll("$0"); } - + private final static Listener commandListener = new Listener() { - @SuppressWarnings("null") + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerCommand(PlayerCommandPreprocessEvent event) { + // Spigot will simply report that the command doesn't exist if a player does not have permission to use it. + // This is good security but, well, it's a breaking change for Skript. So we need to check for permissions + // ourselves and handle those messages, for every command. + + // parse command, see if it's a skript command + String[] cmd = event.getMessage().substring(1).split("\\s+", 2); + String label = cmd[0].toLowerCase(Locale.ENGLISH); + String arguments = cmd.length == 1 ? "" : "" + cmd[1]; + ScriptCommand command = commands.get(label); + + // if so, check permissions + if (command != null && !command.checkPermissions(event.getPlayer(), label, arguments)) + event.setCancelled(true); + } + @EventHandler(priority = EventPriority.HIGHEST) public void onServerCommand(ServerCommandEvent event) { if (event.getCommand().isEmpty() || event.isCancelled()) @@ -154,7 +172,7 @@ public void onServerCommand(ServerCommandEvent event) { } } }; - + static boolean handleEffectCommand(CommandSender sender, String command) { if (!(sender instanceof ConsoleCommandSender || sender.hasPermission("skript.effectcommands") || SkriptConfig.allowOpsToUseEffectCommands.value() && sender.isOp())) return false; @@ -170,7 +188,7 @@ static boolean handleEffectCommand(CommandSender sender, String command) { parserInstance.setCurrentEvent("effect command", EffectCommandEvent.class); Effect effect = Effect.parse(command, null); parserInstance.deleteCurrentEvent(); - + if (effect != null) { log.clear(); // ignore warnings and stuff log.printLog(); @@ -219,7 +237,7 @@ public static boolean scriptCommandExists(String command) { ScriptCommand scriptCommand = commands.get(command); return scriptCommand != null && scriptCommand.getName().equals(command); } - + public static void registerCommand(ScriptCommand command) { // Validate that there are no duplicates ScriptCommand existingCommand = commands.get(command.getLabel()); @@ -230,7 +248,7 @@ public static void registerCommand(ScriptCommand command) { ); return; } - + if (commandMap != null) { assert cmKnownCommands != null;// && cmAliases != null; command.register(commandMap, cmKnownCommands, cmAliases); @@ -262,9 +280,9 @@ public static void unregisterCommand(ScriptCommand scriptCommand) { } commands.values().removeIf(command -> command == scriptCommand); } - + private static boolean registeredListeners = false; - + public static void registerListeners() { if (!registeredListeners) { Bukkit.getPluginManager().registerEvents(commandListener, Skript.getInstance()); @@ -298,15 +316,15 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { registeredListeners = true; } } - + /** * copied from CraftBukkit (org.bukkit.craftbukkit.help.CommandAliasHelpTopic) */ public static final class CommandAliasHelpTopic extends HelpTopic { - + private final String aliasFor; private final HelpMap helpMap; - + public CommandAliasHelpTopic(String alias, String aliasFor, HelpMap helpMap) { this.aliasFor = aliasFor.startsWith("/") ? aliasFor : "/" + aliasFor; this.helpMap = helpMap; @@ -314,7 +332,7 @@ public CommandAliasHelpTopic(String alias, String aliasFor, HelpMap helpMap) { Validate.isTrue(!name.equals(this.aliasFor), "Command " + name + " cannot be alias for itself"); shortText = ChatColor.YELLOW + "Alias for " + ChatColor.WHITE + this.aliasFor; } - + @Override @NotNull public String getFullText(CommandSender forWho) { @@ -326,7 +344,7 @@ public String getFullText(CommandSender forWho) { } return "" + fullText; } - + @Override public boolean canSee(CommandSender commandSender) { if (amendedPermission != null) @@ -335,5 +353,5 @@ public boolean canSee(CommandSender commandSender) { return aliasForTopic != null && aliasForTopic.canSee(commandSender); } } - + } diff --git a/src/main/java/ch/njol/skript/command/ScriptCommand.java b/src/main/java/ch/njol/skript/command/ScriptCommand.java index 2ce33ac7152..05d01680cc3 100644 --- a/src/main/java/ch/njol/skript/command/ScriptCommand.java +++ b/src/main/java/ch/njol/skript/command/ScriptCommand.java @@ -117,7 +117,7 @@ public class ScriptCommand implements TabExecutor { /** * Creates a new SkriptCommand. - * + * * @param name /name * @param pattern * @param arguments the list of Arguments this command takes @@ -237,16 +237,8 @@ public boolean execute(final CommandSender sender, final String commandLabel, fi final ScriptCommandEvent event = new ScriptCommandEvent(ScriptCommand.this, sender, commandLabel, rest); - if (!permission.isEmpty() && !sender.hasPermission(permission)) { - if (sender instanceof Player) { - List components = - permissionMessage.getMessageComponents(event); - ((Player) sender).spigot().sendMessage(BungeeConverter.convert(components)); - } else { - sender.sendMessage(permissionMessage.getSingle(event)); - } + if (!checkPermissions(sender, event)) return false; - } cooldownCheck : { if (sender instanceof Player && cooldown != null) { @@ -316,19 +308,37 @@ boolean execute2(final ScriptCommandEvent event, final CommandSender sender, fin } finally { log.stop(); } - + if (Skript.log(Verbosity.VERY_HIGH)) Skript.info("# /" + name + " " + rest); final long startTrigger = System.nanoTime(); - + if (!trigger.execute(event)) sender.sendMessage(Commands.m_internal_error.toString()); - + if (Skript.log(Verbosity.VERY_HIGH)) Skript.info("# " + name + " took " + 1. * (System.nanoTime() - startTrigger) / 1000000. + " milliseconds"); return true; } + public boolean checkPermissions(CommandSender sender, String commandLabel, String arguments) { + return checkPermissions(sender, new ScriptCommandEvent(this, sender, commandLabel, arguments)); + } + + public boolean checkPermissions(CommandSender sender, Event event) { + if (!permission.isEmpty() && !sender.hasPermission(permission)) { + if (sender instanceof Player) { + List components = + permissionMessage.getMessageComponents(event); + ((Player) sender).spigot().sendMessage(BungeeConverter.convert(components)); + } else { + sender.sendMessage(permissionMessage.getSingle(event)); + } + return false; + } + return true; + } + public void sendHelp(final CommandSender sender) { if (!description.isEmpty()) sender.sendMessage(description); @@ -337,7 +347,7 @@ public void sendHelp(final CommandSender sender) { /** * Gets the arguments this command takes. - * + * * @return The internal list of arguments. Do not modify it! */ public List> getArguments() { @@ -575,7 +585,7 @@ public List onTabComplete(@Nullable CommandSender sender, @Nullable Comm Class argType = arg.getType(); if (argType.equals(Player.class) || argType.equals(OfflinePlayer.class)) return null; // Default completion - + return Collections.emptyList(); // No tab completion here! } diff --git a/src/main/java/ch/njol/skript/conditions/CondCompare.java b/src/main/java/ch/njol/skript/conditions/CondCompare.java index 707f2626e83..4e0917faa6d 100644 --- a/src/main/java/ch/njol/skript/conditions/CondCompare.java +++ b/src/main/java/ch/njol/skript/conditions/CondCompare.java @@ -18,6 +18,7 @@ */ package ch.njol.skript.conditions; +import ch.njol.skript.util.Time; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; @@ -52,6 +53,7 @@ import ch.njol.skript.util.Utils; import ch.njol.util.Checker; import ch.njol.util.Kleenean; +import org.skriptlang.skript.lang.util.Cyclical; @Name("Comparison") @Description({"A very general condition, it simply compares two values. Usually you can only compare for equality (e.g. block is/isn't of <type>), " + @@ -351,15 +353,29 @@ public boolean check(final Event e) { return third.check(e, (Checker) o3 -> { boolean isBetween; if (comparator != null) { - isBetween = - (Relation.GREATER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o2)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o3))) - // Check OPPOSITE (switching o2 / o3) - || (Relation.GREATER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o3)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o2))); + if (o1 instanceof Cyclical && o2 instanceof Cyclical && o3 instanceof Cyclical) { + if (Relation.GREATER_OR_EQUAL.isImpliedBy(comparator.compare(o2, o3))) + isBetween = Relation.GREATER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o2)) || Relation.SMALLER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o3)); + else + isBetween = Relation.GREATER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o2)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o3)); + } else { + isBetween = + (Relation.GREATER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o2)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o3))) + // Check OPPOSITE (switching o2 / o3) + || (Relation.GREATER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o3)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o2))); + } } else { - isBetween = - (Relation.GREATER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o2)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o3))) - // Check OPPOSITE (switching o2 / o3) - || (Relation.GREATER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o3)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o2))); + if (o1 instanceof Cyclical && o2 instanceof Cyclical && o3 instanceof Cyclical) { + if (Relation.GREATER_OR_EQUAL.isImpliedBy(Comparators.compare(o2, o3))) + isBetween = Relation.GREATER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o2)) || Relation.SMALLER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o3)); + else + isBetween = Relation.GREATER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o2)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o3)); + } else { + isBetween = + (Relation.GREATER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o2)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o3))) + // Check OPPOSITE (switching o2 / o3) + || (Relation.GREATER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o3)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o2))); + } } return relation == Relation.NOT_EQUAL ^ isBetween; }); diff --git a/src/main/java/ch/njol/skript/conditions/CondIsSlimeChunk.java b/src/main/java/ch/njol/skript/conditions/CondIsSlimeChunk.java index 71dbbea2dcc..1b71258482b 100755 --- a/src/main/java/ch/njol/skript/conditions/CondIsSlimeChunk.java +++ b/src/main/java/ch/njol/skript/conditions/CondIsSlimeChunk.java @@ -35,7 +35,7 @@ @Name("Is Slime Chunk") @Description({"Tests whether a chunk is a so-called slime chunk.", "Slimes can generally spawn in the swamp biome and in slime chunks.", - "For more info, see the Minecraft wiki."}) + "For more info, see the Minecraft wiki."}) @Examples({"command /slimey:", "\ttrigger:", "\t\tif chunk at player is a slime chunk:", diff --git a/src/main/java/ch/njol/skript/test/runner/CondMinecraftVersion.java b/src/main/java/ch/njol/skript/conditions/CondMinecraftVersion.java similarity index 98% rename from src/main/java/ch/njol/skript/test/runner/CondMinecraftVersion.java rename to src/main/java/ch/njol/skript/conditions/CondMinecraftVersion.java index bc5f1436349..e2888c0cf80 100644 --- a/src/main/java/ch/njol/skript/test/runner/CondMinecraftVersion.java +++ b/src/main/java/ch/njol/skript/conditions/CondMinecraftVersion.java @@ -16,7 +16,7 @@ * * Copyright Peter Güttinger, SkriptLang team and contributors */ -package ch.njol.skript.test.runner; +package ch.njol.skript.conditions; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; diff --git a/src/main/java/ch/njol/skript/effects/EffExplosion.java b/src/main/java/ch/njol/skript/effects/EffExplosion.java index 4f0de85dd86..192119a54d6 100644 --- a/src/main/java/ch/njol/skript/effects/EffExplosion.java +++ b/src/main/java/ch/njol/skript/effects/EffExplosion.java @@ -37,7 +37,7 @@ * @author Peter Güttinger */ @Name("Explosion") -@Description({"Creates an explosion of a given force. The Minecraft Wiki has an article on explosions " + +@Description({"Creates an explosion of a given force. The Minecraft Wiki has an article on explosions " + "which lists the explosion forces of TNT, creepers, etc.", "Hint: use a force of 0 to create a fake explosion that does no damage whatsoever, or use the explosion effect introduced in Skript 2.0.", "Starting with Bukkit 1.4.5 and Skript 2.0 you can use safe explosions which will damage entities but won't destroy any blocks."}) diff --git a/src/main/java/ch/njol/skript/effects/EffPush.java b/src/main/java/ch/njol/skript/effects/EffPush.java index c248a51acf4..8cda4d62ffe 100644 --- a/src/main/java/ch/njol/skript/effects/EffPush.java +++ b/src/main/java/ch/njol/skript/effects/EffPush.java @@ -77,6 +77,10 @@ protected void execute(final Event e) { final Vector mod = d.getDirection(en); if (v != null) mod.normalize().multiply(v.doubleValue()); + if (!(Double.isFinite(mod.getX()) && Double.isFinite(mod.getY()) && Double.isFinite(mod.getZ()))) { + // Some component of the mod vector is not finite, so just stop + return; + } en.setVelocity(en.getVelocity().add(mod)); // REMIND add NoCheatPlus exception to players } } diff --git a/src/main/java/ch/njol/skript/effects/EffScriptFile.java b/src/main/java/ch/njol/skript/effects/EffScriptFile.java index 0a38111c5ce..ed15f625d2f 100644 --- a/src/main/java/ch/njol/skript/effects/EffScriptFile.java +++ b/src/main/java/ch/njol/skript/effects/EffScriptFile.java @@ -37,6 +37,7 @@ import java.io.File; import java.io.IOException; +import java.util.Set; @Name("Enable/Disable/Reload Script File") @Description("Enables, disables, or reloads a script file.") @@ -102,10 +103,8 @@ protected void execute(Event e) { case RELOAD: { if (ScriptLoader.getDisabledScriptsFilter().accept(scriptFile)) return; - - Script script = ScriptLoader.getScript(scriptFile); - if (script != null) - ScriptLoader.unloadScript(script); + + this.unloadScripts(scriptFile); ScriptLoader.loadScripts(scriptFile, OpenCloseable.EMPTY); break; @@ -114,9 +113,7 @@ protected void execute(Event e) { if (ScriptLoader.getDisabledScriptsFilter().accept(scriptFile)) return; - Script script = ScriptLoader.getScript(scriptFile); - if (script != null) - ScriptLoader.unloadScript(script); + this.unloadScripts(scriptFile); try { FileUtils.move( @@ -135,6 +132,19 @@ protected void execute(Event e) { assert false; } } + + private void unloadScripts(File file) { + if (file.isDirectory()) { + Set