From 2daaa6fa326c8748b1df8ec41f5cab96c45737d4 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Wed, 20 Dec 2023 20:36:28 -0500 Subject: [PATCH 01/67] (Patch) Fix NPE issue with drops in 1.20.2 (#6239) Fix NPE issue with drops in 1.20.2 Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/aliases/ItemType.java | 50 +++++++++++++++---- .../ch/njol/skript/expressions/ExprDrops.java | 13 ++--- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/main/java/ch/njol/skript/aliases/ItemType.java b/src/main/java/ch/njol/skript/aliases/ItemType.java index bc5b8aa6ca1..96f4f7adfb4 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemType.java +++ b/src/main/java/ch/njol/skript/aliases/ItemType.java @@ -728,12 +728,18 @@ public boolean removeFrom(Inventory invi) { @SafeVarargs public final boolean removeAll(List... lists) { + return removeAll(true, lists); + } + + + @SafeVarargs + public final boolean removeAll(boolean replaceWithNull, List...lists) { final boolean wasAll = all; final int oldAmount = amount; all = true; amount = -1; try { - return removeFrom(lists); + return removeFrom(replaceWithNull, lists); } finally { all = wasAll; amount = oldAmount; @@ -749,18 +755,37 @@ public final boolean removeAll(List... lists) { */ @SafeVarargs public final boolean removeFrom(final List... lists) { + return removeFrom(true, lists); + } + + /** + * Removes this ItemType from given lists of ItemStacks. + * If replaceWithNull is true, then if an ItemStack is completely removed, that index in the list is set to null, instead of being removed. + * + * @param replaceWithNull Whether to replace removed ItemStacks with null, or to remove them completely + * @param lists The lists to remove this type from. Each list should implement {@link RandomAccess}. Lists may contain null values after this method if replaceWithNull is true. + * @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(boolean replaceWithNull, List... lists) { int removed = 0; boolean ok = true; - for (final ItemData d : types) { + for (ItemData d : types) { if (all) removed = 0; - for (final List list : lists) { + for (List list : lists) { if (list == null) continue; assert list instanceof RandomAccess; - for (int i = 0; i < list.size(); i++) { - final ItemStack is = list.get(i); + + Iterator listIterator = list.iterator(); + int index = -1; // only reliable if replaceWithNull is true. Will be -1 if replaceWithNull is false. + while (listIterator.hasNext()) { + ItemStack is = listIterator.next(); + // index is only reliable if replaceWithNull is true + if (replaceWithNull) + index++; /* * Do NOT use equals()! It doesn't exactly match items * for historical reasons. This will change in future. @@ -778,15 +803,22 @@ public final boolean removeFrom(final List... lists) { boolean plain = d.isPlain() != other.isPlain(); if (d.matchPlain(other) || other.matchAlias(d).isAtLeast(plain ? MatchQuality.EXACT : (d.isAlias() && !other.isAlias() ? MatchQuality.SAME_MATERIAL : MatchQuality.SAME_ITEM))) { if (all && amount == -1) { - list.set(i, null); + if (replaceWithNull) { + list.set(index, null); + } else { + listIterator.remove(); + } removed = 1; continue; } - assert is != null; - final int toRemove = Math.min(is.getAmount(), getAmount() - removed); + int toRemove = Math.min(is.getAmount(), getAmount() - removed); removed += toRemove; if (toRemove == is.getAmount()) { - list.set(i, null); + if (replaceWithNull) { + list.set(index, null); + } else { + listIterator.remove(); + } } else { is.setAmount(is.getAmount() - toRemove); } diff --git a/src/main/java/ch/njol/skript/expressions/ExprDrops.java b/src/main/java/ch/njol/skript/expressions/ExprDrops.java index 2d57b4e9277..85f3f423a22 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprDrops.java +++ b/src/main/java/ch/njol/skript/expressions/ExprDrops.java @@ -18,10 +18,6 @@ */ package ch.njol.skript.expressions; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - import ch.njol.skript.Skript; import ch.njol.skript.aliases.ItemType; import ch.njol.skript.classes.Changer.ChangeMode; @@ -44,6 +40,9 @@ import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + /** * @author Peter Güttinger */ @@ -179,20 +178,18 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { break; case REMOVE: for (ItemType item : deltaDrops) { - item.removeFrom(drops); + item.removeFrom(false, drops); } break; case REMOVE_ALL: for (ItemType item : deltaDrops) { - item.removeAll(drops); + item.removeAll(false, drops); } break; case DELETE: case RESET: assert false; } - // remove stray nulls caused by ItemType#removeFrom() and ItemType#removeAll() - drops.removeIf(Objects::isNull); } } From 48e18f95bdd959d551fd297e9b3c756cb7327a2e Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Sat, 30 Dec 2023 17:03:44 -0500 Subject: [PATCH 02/67] Fix CondCompare Logging (#6266) --- .../ch/njol/skript/conditions/CondCompare.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/ch/njol/skript/conditions/CondCompare.java b/src/main/java/ch/njol/skript/conditions/CondCompare.java index 4e0917faa6d..f725c1bd6f8 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.log.ParseLogHandler; import ch.njol.skript.util.Time; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; @@ -166,9 +167,8 @@ public static String f(final Expression e) { @SuppressWarnings("unchecked") private boolean init(String expr) { - RetainingLogHandler log = SkriptLogger.startRetainingLog(); Expression third = this.third; - try { + try (ParseLogHandler log = SkriptLogger.startParseLogHandler()) { if (first.getReturnType() == Object.class) { Expression expression = null; if (first instanceof UnparsedLiteral) @@ -176,7 +176,7 @@ private boolean init(String expr) { if (expression == null) expression = first.getConvertedExpression(Object.class); if (expression == null) { - log.printErrors(); + log.printError(); return false; } first = expression; @@ -188,7 +188,7 @@ private boolean init(String expr) { if (expression == null) expression = second.getConvertedExpression(Object.class); if (expression == null) { - log.printErrors(); + log.printError(); return false; } second = expression; @@ -200,14 +200,13 @@ private boolean init(String expr) { if (expression == null) expression = third.getConvertedExpression(Object.class); if (expression == null) { - log.printErrors(); + log.printError(); return false; } this.third = third = expression; } - log.printLog(); - } finally { - log.stop(); + // we do not want to print any errors as they are not applicable + log.printLog(false); } Class firstReturnType = first.getReturnType(); Class secondReturnType = third == null ? second.getReturnType() : Utils.getSuperType(second.getReturnType(), third.getReturnType()); From c3b0f44d4777416d3d5f99dcc3bcb65391bf5cb1 Mon Sep 17 00:00:00 2001 From: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Date: Sat, 30 Dec 2023 15:16:35 -0700 Subject: [PATCH 03/67] Remove final on 'cleanup' method and provide JUnit implementation warnings. (#6261) --- src/main/java/ch/njol/skript/Skript.java | 20 ++++++++++++++++++- .../skript/test/runner/SkriptJUnitTest.java | 5 ++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 878dfe12666..7b1ac6c0eab 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -82,6 +82,7 @@ import ch.njol.util.Kleenean; import ch.njol.util.NullableChecker; import ch.njol.util.StringUtils; +import ch.njol.util.coll.CollectionUtils; import ch.njol.util.coll.iterator.CheckedIterator; import ch.njol.util.coll.iterator.EnumerationIterable; import com.google.common.collect.Lists; @@ -106,6 +107,7 @@ import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.java.JavaPlugin; import org.eclipse.jdt.annotation.Nullable; +import org.junit.After; import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.skriptlang.skript.lang.comparator.Comparator; @@ -147,7 +149,6 @@ import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; @@ -703,6 +704,23 @@ protected void afterErrors() { Result junit = JUnitCore.runClasses(clazz); TestTracker.testStarted("JUnit: '" + test + "'"); + /** + * Usage of @After is pointless if the JUnit class requires delay. As the @After will happen instantly. + * The JUnit must override the 'cleanup' method to avoid Skript automatically cleaning up the test data. + */ + boolean overrides = false; + for (Method method : clazz.getDeclaredMethods()) { + if (!method.isAnnotationPresent(After.class)) + continue; + if (SkriptJUnitTest.getShutdownDelay() > 1) + warning("Using @After in JUnit classes, happens instantaneously, and JUnit class '" + test + "' requires a delay. Do your test cleanup in the script junit file or 'cleanup' method."); + if (method.getName().equals("cleanup")) + overrides = true; + } + if (SkriptJUnitTest.getShutdownDelay() > 1 && !overrides) + error("The JUnit class '" + test + "' does not override the method 'cleanup' thus the test data will instantly be cleaned up. " + + "This JUnit test requires longer shutdown time: " + SkriptJUnitTest.getShutdownDelay()); + // Collect all data from the current JUnit test. shutdownDelay = Math.max(shutdownDelay, SkriptJUnitTest.getShutdownDelay()); tests += junit.getRunCount(); diff --git a/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java b/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java index 8431dc9a6a3..38ec51d116c 100644 --- a/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java +++ b/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java @@ -76,9 +76,12 @@ public static void setShutdownDelay(long delay) { SkriptJUnitTest.delay = delay; } + /** + * Override this method if your JUnit test requires block modification with delay over 1 tick. + */ @Before @After - public final void cleanup() { + public void cleanup() { getTestWorld().getEntities().forEach(Entity::remove); setBlock(Material.AIR); } From 967f9ee816b8ea296013f5d939a84a1d0675ce76 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Sat, 30 Dec 2023 22:23:03 +0000 Subject: [PATCH 04/67] Allow multiple hashtags to start comments at the beginning of a line. (#6146) --- src/main/java/ch/njol/skript/config/Node.java | 2 ++ .../skript/test/tests/config/NodeTest.java | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/njol/skript/config/Node.java b/src/main/java/ch/njol/skript/config/Node.java index 170fd38cf42..afb3ea3d038 100644 --- a/src/main/java/ch/njol/skript/config/Node.java +++ b/src/main/java/ch/njol/skript/config/Node.java @@ -128,6 +128,8 @@ public void move(final SectionNode newParent) { * @return A pair (value, comment). */ public static NonNullPair splitLine(final String line) { + if (line.trim().startsWith("#")) + return new NonNullPair<>("", line.substring(line.indexOf('#'))); final Matcher m = linePattern.matcher(line); boolean matches = false; try { diff --git a/src/test/java/org/skriptlang/skript/test/tests/config/NodeTest.java b/src/test/java/org/skriptlang/skript/test/tests/config/NodeTest.java index e24d28405f2..c7e0eb3b29f 100644 --- a/src/test/java/org/skriptlang/skript/test/tests/config/NodeTest.java +++ b/src/test/java/org/skriptlang/skript/test/tests/config/NodeTest.java @@ -39,12 +39,12 @@ public void splitLineTest() { {"ab#cd", "ab", "#cd"}, {"ab##cd", "ab#cd", ""}, {"ab###cd", "ab#", "#cd"}, - {"######", "###", ""}, - {"#######", "###", "#"}, - {"#### # ####", "## ", "# ####"}, - {"##### ####", "##", "# ####"}, - {"#### #####", "## ##", "#"}, - {"#########", "####", "#"}, + {"######", "", "######"}, + {"#######", "", "#######"}, + {"#### # ####", "", "#### # ####"}, + {"##### ####", "", "##### ####"}, + {"#### #####", "", "#### #####"}, + {"#########", "", "#########"}, {"a##b#c##d#e", "a#b", "#c##d#e"}, {" a ## b # c ## d # e ", " a # b ", "# c ## d # e "}, }; From 58ca4695e24ba0330feaceb41f1440b36d4f6082 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 31 Dec 2023 16:47:20 -0800 Subject: [PATCH 05/67] Bump io.papermc.paper:paper-api from 1.20.2-R0.1-SNAPSHOT to 1.20.4-R0.1-SNAPSHOT (#6227) * Start working towards 1.20.4 * Update aliases to master * Add missing enums for 1.20.3 * Update default.lang --------- Co-authored-by: TheLimeGlass Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- build.gradle | 6 +++--- gradle.properties | 2 +- src/main/resources/lang/default.lang | 6 ++++++ .../java17/{paper-1.20.2.json => paper-1.20.4.json} | 4 ++-- .../skript/tests/syntaxes/conditions/CondIsPreferredTool.sk | 4 ++-- 5 files changed, 14 insertions(+), 8 deletions(-) rename src/test/skript/environments/java17/{paper-1.20.2.json => paper-1.20.4.json} (85%) diff --git a/build.gradle b/build.gradle index 708f7274a36..dfe1f3284b7 100644 --- a/build.gradle +++ b/build.gradle @@ -29,8 +29,8 @@ dependencies { shadow group: 'org.bstats', name: 'bstats-bukkit', version: '3.0.2' shadow group: 'net.kyori', name: 'adventure-text-serializer-bungeecord', version: '4.3.2' - 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.800' + implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.20.4-R0.1-SNAPSHOT' + implementation group: 'org.eclipse.jdt', name: 'org.eclipse.jdt.annotation', version: '2.2.700' implementation group: 'com.google.code.findbugs', name: 'findbugs', version: '3.0.1' implementation group: 'com.sk89q.worldguard', name: 'worldguard-legacy', version: '7.0.0-SNAPSHOT' implementation group: 'net.milkbowl.vault', name: 'Vault', version: '1.7.1', { @@ -250,7 +250,7 @@ void createTestTask(String name, String desc, String environments, int javaVersi } } -def latestEnv = 'java17/paper-1.20.2.json' +def latestEnv = 'java17/paper-1.20.4.json' def latestJava = 17 def oldestJava = 8 diff --git a/gradle.properties b/gradle.properties index 7af67b25858..4542c647233 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,5 +7,5 @@ groupid=ch.njol name=skript version=2.8.0-dev jarName=Skript.jar -testEnv=java17/paper-1.20.2 +testEnv=java17/paper-1.20.4 testEnvJavaVersion=17 diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 176cc9b94ce..e1a2dccc4ac 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -1819,6 +1819,8 @@ inventory types: smithing: smithing inventory @a composter: composter inventory @a chiseled_bookshelf: chiseled bookshelf @a, bookshelf @a + decorated_pot: decorated pot @a + crafter: crafter inventory @a jukebox: jukebox @a # The smithing new table is required for Skript to work, but this is a draft API for 1.20 @@ -1888,6 +1890,10 @@ resource pack states: declined: decline, refuse, reject, declined, refused, rejected failed_download: download fail, fail, failed to download, failed successfully_loaded: successfully load, successfully install, success, successfully loaded, successfully installed + downloaded: downloaded + invalid_url: invalid url + failed_reload: failed reload, failed to reload + discarded: discarded # -- Sound Categories -- sound categories: diff --git a/src/test/skript/environments/java17/paper-1.20.2.json b/src/test/skript/environments/java17/paper-1.20.4.json similarity index 85% rename from src/test/skript/environments/java17/paper-1.20.2.json rename to src/test/skript/environments/java17/paper-1.20.4.json index 0512ae142b0..02be65e56ac 100644 --- a/src/test/skript/environments/java17/paper-1.20.2.json +++ b/src/test/skript/environments/java17/paper-1.20.4.json @@ -1,11 +1,11 @@ { - "name": "paper-1.20.2", + "name": "paper-1.20.4", "resources": [ {"source": "server.properties.generic", "target": "server.properties"} ], "paperDownloads": [ { - "version": "1.20.2", + "version": "1.20.4", "target": "paperclip.jar" } ], diff --git a/src/test/skript/tests/syntaxes/conditions/CondIsPreferredTool.sk b/src/test/skript/tests/syntaxes/conditions/CondIsPreferredTool.sk index 3a3e9f5149a..585055cdf88 100644 --- a/src/test/skript/tests/syntaxes/conditions/CondIsPreferredTool.sk +++ b/src/test/skript/tests/syntaxes/conditions/CondIsPreferredTool.sk @@ -1,12 +1,12 @@ test "CondIsPreferredTool - BlockData" when running minecraft "1.19.2": assert wooden pickaxe is preferred tool for minecraft:stone[] with "failed wooden pickaxe for stone blockdata" - assert wooden pickaxe is preferred tool for minecraft:grass[] with "failed wooden pickaxe for grass blockdata" + assert wooden pickaxe is preferred tool for minecraft:dirt[] with "failed wooden pickaxe for dirt blockdata" assert wooden pickaxe is not preferred tool for minecraft:obsidian[] with "failed wooden pickaxe for obsidian blockdata" assert diamond pickaxe is preferred tool for minecraft:obsidian[] with "failed diamond pickaxe for obsidian blockdata" assert wooden axe is not preferred tool for minecraft:stone[] with "failed wooden axe for stone blockdata" - assert wooden axe is preferred tool for minecraft:grass[] with "failed wooden axe for grass blockdata" + assert wooden axe is preferred tool for minecraft:dirt[] with "failed wooden axe for dirt blockdata" test "CondIsPreferredTool - Block" when running minecraft "1.16.5": set {_block} to block at location(0,0,0, "world") From 5843ec835fd22126d335aad122fb6650df68ecfb Mon Sep 17 00:00:00 2001 From: Pikachu920 <28607612+Pikachu920@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:53:50 -0600 Subject: [PATCH 06/67] Add tests for effects (#6204) * Add tests for EffActionBar * Fix order of operations issue in EffApplyBoneMeal * Add tests for EffApplyBoneMeal * Add tests for EffDoIf * Add tests for EffSwingHand * Replace static imports * Add tests for EffFeed * Fix EffSwingHandTest on unsupported versions * Add tests for EffMakeFly * Add tests for EffPvP * Remove double whitespace * Add tests for EffOp --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- build.gradle | 2 +- src/main/java/ch/njol/skript/Skript.java | 3 + .../njol/skript/effects/EffApplyBoneMeal.java | 2 +- .../ch/njol/skript/effects/EffSwingHand.java | 2 +- .../skript/test/runner/SkriptJUnitTest.java | 2 +- .../syntaxes/effects/EffActionBarTest.java | 97 +++++++++++++++++++ .../effects/EffApplyBoneMealTest.java | 96 ++++++++++++++++++ .../tests/syntaxes/effects/EffFeedTest.java | 84 ++++++++++++++++ .../syntaxes/effects/EffMakeFlyTest.java | 73 ++++++++++++++ .../tests/syntaxes/effects/EffOpTest.java | 69 +++++++++++++ .../syntaxes/effects/EffSwingHandTest.java | 75 ++++++++++++++ .../syntaxes/{ => events}/EvtGrowTest.java | 2 +- src/test/skript/junit/EvtGrow.sk | 80 +++++++-------- .../skript/tests/syntaxes/effects/EffDoIf.sk | 6 ++ .../skript/tests/syntaxes/effects/EffPvP.sk | 6 ++ 15 files changed, 554 insertions(+), 45 deletions(-) create mode 100644 src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffActionBarTest.java create mode 100644 src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffApplyBoneMealTest.java create mode 100644 src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffFeedTest.java create mode 100644 src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffMakeFlyTest.java create mode 100644 src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffOpTest.java create mode 100644 src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffSwingHandTest.java rename src/test/java/org/skriptlang/skript/test/tests/syntaxes/{ => events}/EvtGrowTest.java (97%) create mode 100644 src/test/skript/tests/syntaxes/effects/EffDoIf.sk create mode 100644 src/test/skript/tests/syntaxes/effects/EffPvP.sk diff --git a/build.gradle b/build.gradle index dfe1f3284b7..5ab26a2e631 100644 --- a/build.gradle +++ b/build.gradle @@ -40,7 +40,7 @@ dependencies { implementation fileTree(dir: 'lib', include: '*.jar') testShadow group: 'junit', name: 'junit', version: '4.13.2' - testShadow group: 'org.easymock', name: 'easymock', version: '5.2.0' + testShadow group: 'org.easymock', name: 'easymock', version: '5.0.1' } task checkAliases { diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 7b1ac6c0eab..291ebff5ae4 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -691,6 +691,9 @@ protected void afterErrors() { long milliseconds = 0, tests = 0, fails = 0, ignored = 0, size = 0; try { List> classes = Lists.newArrayList(Utils.getClasses(Skript.getInstance(), "org.skriptlang.skript.test", "tests")); + // Don't attempt to run inner/anonymous classes as tests + classes.removeIf(Class::isAnonymousClass); + classes.removeIf(Class::isLocalClass); // Test that requires package access. This is only present when compiling with src/test. classes.add(Class.forName("ch.njol.skript.variables.FlatFileStorageTest")); size = classes.size(); diff --git a/src/main/java/ch/njol/skript/effects/EffApplyBoneMeal.java b/src/main/java/ch/njol/skript/effects/EffApplyBoneMeal.java index 484654a14f6..080f1cd3982 100644 --- a/src/main/java/ch/njol/skript/effects/EffApplyBoneMeal.java +++ b/src/main/java/ch/njol/skript/effects/EffApplyBoneMeal.java @@ -71,7 +71,7 @@ protected void execute(Event event) { @Override public String toString(@Nullable Event event, boolean debug) { - return "apply " + amount != null ? amount.toString(event, debug) + " " : "" + "bone meal to " + blocks.toString(event, debug); + return "apply " + (amount != null ? amount.toString(event, debug) + " " : "" + "bone meal to " + blocks.toString(event, debug)); } } diff --git a/src/main/java/ch/njol/skript/effects/EffSwingHand.java b/src/main/java/ch/njol/skript/effects/EffSwingHand.java index a129254de9f..826cce82978 100644 --- a/src/main/java/ch/njol/skript/effects/EffSwingHand.java +++ b/src/main/java/ch/njol/skript/effects/EffSwingHand.java @@ -46,7 +46,7 @@ public class EffSwingHand extends Effect { "make %livingentities% swing [their] off[ ]hand"); } - private static final boolean SWINGING_IS_SUPPORTED = Skript.methodExists(LivingEntity.class, "swingMainHand"); + public static final boolean SWINGING_IS_SUPPORTED = Skript.methodExists(LivingEntity.class, "swingMainHand"); @SuppressWarnings("null") private Expression entities; diff --git a/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java b/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java index 38ec51d116c..65a52f0ac1a 100644 --- a/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java +++ b/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java @@ -38,7 +38,7 @@ public abstract class SkriptJUnitTest { static { - World world = Bukkit.getWorlds().get(0); + World world = getTestWorld(); world.setGameRule(GameRule.MAX_ENTITY_CRAMMING, 1000); world.setGameRule(GameRule.DO_WEATHER_CYCLE, false); // Natural entity spawning diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffActionBarTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffActionBarTest.java new file mode 100644 index 00000000000..125f22e86e8 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffActionBarTest.java @@ -0,0 +1,97 @@ +/** + * 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.syntaxes.effects; + + +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.ContextlessEvent; +import ch.njol.skript.test.runner.SkriptJUnitTest; +import ch.njol.skript.variables.Variables; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.entity.Player; +import org.easymock.EasyMock; +import org.easymock.IArgumentMatcher; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + + +@SuppressWarnings("deprecation") +public class EffActionBarTest extends SkriptJUnitTest { + + private Player testPlayer; + private Player.Spigot testSpigotPlayer; + private Effect actionBarEffect; + + @Before + public void setup() { + testPlayer = EasyMock.niceMock(Player.class); + testSpigotPlayer = EasyMock.niceMock(Player.Spigot.class); + actionBarEffect = Effect.parse("send actionbar {_content} to {_player}", null); + } + + @Test + public void test() { + if (actionBarEffect == null) + Assert.fail("Effect is null"); + + String expectedActionBarContent = "hello world"; + + EasyMock.expect(testPlayer.spigot()).andAnswer(() -> testSpigotPlayer); + + testSpigotPlayer.sendMessage( + EasyMock.eq(ChatMessageType.ACTION_BAR), + (BaseComponent[]) componentMatcher(expectedActionBarContent) + ); + + EasyMock.expectLastCall(); + + EasyMock.replay(testPlayer, testSpigotPlayer); + + ContextlessEvent event = ContextlessEvent.get(); + Variables.setVariable("content", expectedActionBarContent, event, true); + Variables.setVariable("player", testPlayer, event, true); + TriggerItem.walk(actionBarEffect, event); + + EasyMock.verify(testPlayer, testSpigotPlayer); + } + + private T componentMatcher(String expectedContent) { + EasyMock.reportMatcher(new IArgumentMatcher() { + @Override + public boolean matches(Object argument) { + if (argument instanceof TextComponent) { + return ((TextComponent) argument).getText().equals(expectedContent); + } + return false; + } + + @Override + public void appendTo(StringBuffer buffer) { + buffer.append("[component matcher]"); + } + }); + + return null; + } + +} diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffApplyBoneMealTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffApplyBoneMealTest.java new file mode 100644 index 00000000000..5b7a5b8aa1f --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffApplyBoneMealTest.java @@ -0,0 +1,96 @@ +/** + * 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.syntaxes.effects; + + +import ch.njol.skript.Skript; +import ch.njol.skript.effects.EffApplyBoneMeal; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.SyntaxElementInfo; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.ContextlessEvent; +import ch.njol.skript.test.runner.SkriptJUnitTest; +import ch.njol.skript.variables.Variables; +import org.bukkit.block.Block; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +public class EffApplyBoneMealTest { + + private Block stubTestBlock; + private Effect applyBonemealEffect; + private Effect applyMultipleBonemealEffect; + + @Before + public void setup() { + stubTestBlock = EasyMock.niceMock(Block.class); + applyBonemealEffect = Effect.parse("apply bonemeal to {_block}", null); + applyMultipleBonemealEffect = Effect.parse("apply {_times} bonemeal to {_block}", null); + } + + @Test + public void test() { + boolean bonemealEffectRegistered = Skript.getEffects().stream() + .map(SyntaxElementInfo::getElementClass) + .anyMatch(EffApplyBoneMeal.class::equals); + if (!bonemealEffectRegistered) + return; + if (applyBonemealEffect == null) + Assert.fail("Effect is null"); + if (applyMultipleBonemealEffect == null) + Assert.fail("Multiple effect is null"); + + int countOfBonemealToApply = 5; + ContextlessEvent event = ContextlessEvent.get(); + Variables.setVariable("block", getMockBlock(), event, true); + Variables.setVariable("times", countOfBonemealToApply, event, true); + + EasyMock.expect(stubTestBlock.applyBoneMeal(EasyMock.notNull())).andReturn(true).times(1); + EasyMock.replay(stubTestBlock); + TriggerItem.walk(applyBonemealEffect, event); + EasyMock.verify(stubTestBlock); + + EasyMock.resetToNice(stubTestBlock); + EasyMock.expect(stubTestBlock.applyBoneMeal(EasyMock.notNull())).andReturn(true).times(2); + EasyMock.replay(stubTestBlock); + TriggerItem.walk(applyMultipleBonemealEffect, event); + EasyMock.verify(stubTestBlock); + } + + private Block getMockBlock() { + Block realBlock = SkriptJUnitTest.getBlock(); + + // we need to intercept applyBoneMeal calls so that easymock can detect them + // but we need to pass the other calls to a real block so that a real blockdata, + // material, location, etc are available + InvocationHandler handler = (proxy, method, args) -> { + if (method.getName().equals("applyBoneMeal")) { + return method.invoke(stubTestBlock, args); + } + return method.invoke(realBlock, args); + }; + + return (Block) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Block.class }, handler); + } +} diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffFeedTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffFeedTest.java new file mode 100644 index 00000000000..1235eea38ab --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffFeedTest.java @@ -0,0 +1,84 @@ +/** + * 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.syntaxes.effects; + +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.ContextlessEvent; +import ch.njol.skript.test.runner.SkriptJUnitTest; +import ch.njol.skript.variables.Variables; +import org.bukkit.entity.Player; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +public class EffFeedTest extends SkriptJUnitTest { + + private Player easyMockPlayer; + private Effect feedFullyEffect; + private Effect feedPartiallyEffect; + + @Before + public void setup() { + easyMockPlayer = EasyMock.niceMock(Player.class); + feedFullyEffect = Effect.parse("feed {_player}", null); + feedPartiallyEffect = Effect.parse("feed {_player} by {_amount} beef", null); + } + + @Test + public void test() { + if (feedFullyEffect == null) + Assert.fail("Fully effect is null"); + if (feedPartiallyEffect == null) + Assert.fail("Partially effect is null"); + + int amountToFeed = 1; + int maxFoodLevel = 20; + ContextlessEvent event = ContextlessEvent.get(); + Variables.setVariable("player", getMockPlayer(), event, true); + Variables.setVariable("amount", amountToFeed, event, true); + + easyMockPlayer.setFoodLevel(EasyMock.eq(maxFoodLevel)); + EasyMock.expectLastCall(); + EasyMock.replay(easyMockPlayer); + TriggerItem.walk(feedFullyEffect, event); + EasyMock.verify(easyMockPlayer); + + EasyMock.resetToNice(easyMockPlayer); + easyMockPlayer.setFoodLevel(EasyMock.eq(amountToFeed)); + EasyMock.expectLastCall(); + EasyMock.replay(easyMockPlayer); + TriggerItem.walk(feedPartiallyEffect, event); + EasyMock.verify(easyMockPlayer); + } + + private Player getMockPlayer() { + InvocationHandler handler = (proxy, method, args) -> { + if (method.getName().equals("getFoodLevel")) + return 0; + return method.invoke(easyMockPlayer, args); + }; + return (Player) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Player.class }, handler); + } + +} diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffMakeFlyTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffMakeFlyTest.java new file mode 100644 index 00000000000..28497059e53 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffMakeFlyTest.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 org.skriptlang.skript.test.tests.syntaxes.effects; + +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.ContextlessEvent; +import ch.njol.skript.test.runner.SkriptJUnitTest; +import ch.njol.skript.variables.Variables; +import org.bukkit.entity.Player; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class EffMakeFlyTest extends SkriptJUnitTest { + + private Player testPlayer; + private Effect startFlyingEffect; + private Effect stopFlyingEffect; + + @Before + public void setup() { + testPlayer = EasyMock.niceMock(Player.class); + startFlyingEffect = Effect.parse("make {_player} start flying", null); + stopFlyingEffect = Effect.parse("make {_player} stop flying", null); + } + + @Test + public void test() { + if (startFlyingEffect == null) + Assert.fail("Start flying effect is null"); + if (stopFlyingEffect == null) + Assert.fail("Stop flying effect is null"); + + ContextlessEvent event = ContextlessEvent.get(); + Variables.setVariable("player", testPlayer, event, true); + + testPlayer.setAllowFlight(true); + EasyMock.expectLastCall(); + testPlayer.setFlying(true); + EasyMock.expectLastCall(); + EasyMock.replay(testPlayer); + TriggerItem.walk(startFlyingEffect, event); + EasyMock.verify(testPlayer); + + EasyMock.resetToNice(testPlayer); + testPlayer.setAllowFlight(false); + EasyMock.expectLastCall(); + testPlayer.setFlying(false); + EasyMock.expectLastCall(); + EasyMock.replay(testPlayer); + TriggerItem.walk(stopFlyingEffect, event); + EasyMock.verify(testPlayer); + } + +} diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffOpTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffOpTest.java new file mode 100644 index 00000000000..69b237292d9 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffOpTest.java @@ -0,0 +1,69 @@ +/** + * 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.syntaxes.effects; + +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.ContextlessEvent; +import ch.njol.skript.test.runner.SkriptJUnitTest; +import ch.njol.skript.variables.Variables; +import org.bukkit.entity.Player; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class EffOpTest extends SkriptJUnitTest { + + private Player testPlayer; + private Effect opPlayerEffect; + private Effect deopPlayerEffect; + + @Before + public void setup() { + testPlayer = EasyMock.niceMock(Player.class); + opPlayerEffect = Effect.parse("op {_player}", null); + deopPlayerEffect = Effect.parse("deop {_player}", null); + } + + @Test + public void test() { + if (opPlayerEffect == null) + Assert.fail("Op player effect is null"); + if (deopPlayerEffect == null) + Assert.fail("Deop player effect is null"); + + ContextlessEvent event = ContextlessEvent.get(); + Variables.setVariable("player", testPlayer, event, true); + + testPlayer.setOp(true); + EasyMock.expectLastCall(); + EasyMock.replay(testPlayer); + TriggerItem.walk(opPlayerEffect, event); + EasyMock.verify(testPlayer); + + EasyMock.resetToNice(testPlayer); + testPlayer.setOp(false); + EasyMock.expectLastCall(); + EasyMock.replay(testPlayer); + TriggerItem.walk(deopPlayerEffect, event); + EasyMock.verify(testPlayer); + } + +} diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffSwingHandTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffSwingHandTest.java new file mode 100644 index 00000000000..1b5da4d1a46 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/effects/EffSwingHandTest.java @@ -0,0 +1,75 @@ +/** + * 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.syntaxes.effects; + +import ch.njol.skript.Skript; +import ch.njol.skript.effects.EffSwingHand; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.SyntaxElementInfo; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.ContextlessEvent; +import ch.njol.skript.test.runner.SkriptJUnitTest; +import ch.njol.skript.variables.Variables; +import org.bukkit.entity.LivingEntity; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + + +public class EffSwingHandTest extends SkriptJUnitTest { + + private LivingEntity testEntity; + private Effect swingMainHandEffect; + private Effect swingOffhandEffect; + + @Before + public void setup() { + testEntity = EasyMock.niceMock(LivingEntity.class); + swingMainHandEffect = Effect.parse("make {_entity} swing their main hand", null); + swingOffhandEffect = Effect.parse("make {_entity} swing their offhand", null); + } + + @Test + public void test() { + if (!EffSwingHand.SWINGING_IS_SUPPORTED) + return; + if (swingMainHandEffect == null) + Assert.fail("Main hand is null"); + if (swingOffhandEffect == null) + Assert.fail("Offhand effect is null"); + + ContextlessEvent event = ContextlessEvent.get(); + Variables.setVariable("entity", testEntity, event, true); + + testEntity.swingMainHand(); + EasyMock.expectLastCall(); + EasyMock.replay(testEntity); + TriggerItem.walk(swingMainHandEffect, event); + EasyMock.verify(testEntity); + + EasyMock.resetToNice(testEntity); + testEntity.swingOffHand(); + EasyMock.expectLastCall(); + EasyMock.replay(testEntity); + TriggerItem.walk(swingOffhandEffect, event); + EasyMock.verify(testEntity); + } + +} diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/EvtGrowTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtGrowTest.java similarity index 97% rename from src/test/java/org/skriptlang/skript/test/tests/syntaxes/EvtGrowTest.java rename to src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtGrowTest.java index 96baf1b5c32..44cb566c33f 100644 --- a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/EvtGrowTest.java +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtGrowTest.java @@ -16,7 +16,7 @@ * * Copyright Peter Güttinger, SkriptLang team and contributors */ -package org.skriptlang.skript.test.tests.syntaxes; +package org.skriptlang.skript.test.tests.syntaxes.events; import ch.njol.skript.Skript; import ch.njol.skript.test.runner.SkriptJUnitTest; diff --git a/src/test/skript/junit/EvtGrow.sk b/src/test/skript/junit/EvtGrow.sk index e932519af9b..080dfd819b9 100644 --- a/src/test/skript/junit/EvtGrow.sk +++ b/src/test/skript/junit/EvtGrow.sk @@ -2,69 +2,69 @@ on script load: # prior to 1.18, applyBoneMeal either did not exist or did not fire events # so we need to complete the objectives manually to avoid the tests failing if running below minecraft "1.18": - complete objective "grow of wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow from wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow to wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow of wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow from wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow to wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow of birch tree" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow of birch sapling" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow from birch sapling" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow to birch tree" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + complete objective "grow of wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow from wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow to wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow of wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow from wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow to wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow of birch tree" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow of birch sapling" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow from birch sapling" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow to birch tree" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" # itemtype - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow of wheat" - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow from wheat" - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow to wheat" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow of wheat" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow from wheat" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow to wheat" # blockdata - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow of wheat (blockdata)" - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow from wheat (blockdata)" - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow to wheat (blockdata)" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow of wheat (blockdata)" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow from wheat (blockdata)" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow to wheat (blockdata)" # structures - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow of birch tree" - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow of birch sapling" - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow from birch sapling" - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" completes "grow to birch tree" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow of birch tree" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow of birch sapling" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow from birch sapling" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" completes "grow to birch tree" on grow of wheat: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow of wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow of wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" on grow of wheat[age=0]: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow of wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow of wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" on grow from wheat: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow from wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow from wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" on grow from wheat[age=0]: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow from wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow from wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" on grow to wheat: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow to wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow to wheat" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" on grow to wheat[age=7]: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow to wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow to wheat (blockdata)" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" on grow of birch tree: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow of birch tree" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow of birch tree" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" on grow of birch sapling: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow of birch sapling" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow of birch sapling" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" on grow from birch sapling: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow from birch sapling" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow from birch sapling" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" on grow to birch tree: - junit test is "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" - complete objective "grow to birch tree" for junit test "org.skriptlang.skript.test.tests.syntaxes.EvtGrowTest" + junit test is "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" + complete objective "grow to birch tree" for junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtGrowTest" diff --git a/src/test/skript/tests/syntaxes/effects/EffDoIf.sk b/src/test/skript/tests/syntaxes/effects/EffDoIf.sk new file mode 100644 index 00000000000..da17471d8d5 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffDoIf.sk @@ -0,0 +1,6 @@ +test "do if": + set {_false} to false if 1 is 1 + assert {_false} is false with "Do if didn't run when it should have" + + set {_unset} to true if 1 is 2 + assert {_unset} is not set with "Do if ran when it shouldn't have" diff --git a/src/test/skript/tests/syntaxes/effects/EffPvP.sk b/src/test/skript/tests/syntaxes/effects/EffPvP.sk new file mode 100644 index 00000000000..bb41b776670 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffPvP.sk @@ -0,0 +1,6 @@ +test "pvp effect": + set {_world} to first element out of all worlds + disable pvp in {_world} + assert pvp is disabled in {_world} with "PvP was not disabled" + enable pvp in {_world} + assert pvp is enabled in {_world} with "PvP was not enabled" From 27d1a304683375579d99fc01ff587b95bb2f2f4b Mon Sep 17 00:00:00 2001 From: Moderocky Date: Mon, 1 Jan 2024 01:02:06 +0000 Subject: [PATCH 07/67] Re-enable the vector from location syntax (#6180) * Re-enable (and fix) location from vector. * Add test for location from vector. * Add better support for direction. * Add direction tests. * Update src/main/java/ch/njol/skript/expressions/ExprLocationFromVector.java Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * Fix silly sovde's broken tests * Update src/main/java/ch/njol/skript/expressions/ExprLocationFromVector.java Co-authored-by: Patrick Miller --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Patrick Miller --- .../expressions/ExprLocationFromVector.java | 55 +++++++++++-------- .../expressions/ExprLocationFromVector.sk | 24 ++++++++ 2 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprLocationFromVector.sk diff --git a/src/main/java/ch/njol/skript/expressions/ExprLocationFromVector.java b/src/main/java/ch/njol/skript/expressions/ExprLocationFromVector.java index 145604fa8fb..f8dd3fc9c81 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprLocationFromVector.java +++ b/src/main/java/ch/njol/skript/expressions/ExprLocationFromVector.java @@ -18,11 +18,12 @@ */ package ch.njol.skript.expressions; +import ch.njol.skript.Skript; +import ch.njol.skript.lang.ExpressionType; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.event.Event; import org.bukkit.util.Vector; -import org.eclipse.jdt.annotation.Nullable; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; @@ -33,6 +34,7 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; +import org.jetbrains.annotations.Nullable; /** * @author bi0qaw @@ -46,11 +48,12 @@ public class ExprLocationFromVector extends SimpleExpression { static { - // TODO fix slowdowns and enable again, for now nuked for greater good -// Skript.registerExpression(ExprLocationFromVector.class, Location.class, ExpressionType.SIMPLE, -// "%vector% [to location] [in] %world%", "location (from|of) %vector% [(from|in)] %world%", -// "%vector% [to location] [in] %world% with yaw %number% and pitch %number%", -// "location (from|of) %vector% [(in|from)] %world% with yaw %number% and pitch %number%"); + Skript.registerExpression(ExprLocationFromVector.class, Location.class, ExpressionType.SIMPLE, + "%vector% [to location] in %world%", + "location (from|of) %vector% in %world%", + "%vector% [to location] in %world% with yaw %number% and pitch %number%", + "location (from|of) %vector% in %world% with yaw %number% and pitch %number%" + ); } @SuppressWarnings("null") @@ -60,17 +63,17 @@ public class ExprLocationFromVector extends SimpleExpression { private Expression world; @SuppressWarnings("null") - private Expression yaw, pitch; - private boolean yawpitch; + private @Nullable Expression yaw, pitch; + private boolean hasDirection; @Override @SuppressWarnings({"unchecked", "null"}) public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { if (exprs.length > 3) - yawpitch = true; + hasDirection = true; vector = (Expression) exprs[0]; world = (Expression) exprs[1]; - if (yawpitch) { + if (hasDirection) { yaw = (Expression) exprs[2]; pitch = (Expression) exprs[3]; } @@ -79,17 +82,21 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @SuppressWarnings("null") @Override - protected Location[] get(Event e) { - Vector v = vector.getSingle(e); - World w = world.getSingle(e); - Number y = yaw != null ? yaw.getSingle(e) : null; - Number p = pitch != null ? pitch.getSingle(e) : null; - if (v == null || w == null) + protected Location[] get(Event event) { + Vector vector = this.vector.getSingle(event); + World world = this.world.getSingle(event); + if (vector == null || world == null) return null; - if (y == null || p == null) - return CollectionUtils.array(v.toLocation(w)); - else - return CollectionUtils.array(v.toLocation(w, y.floatValue(), p.floatValue())); + direction: + if (hasDirection) { + assert yaw != null && pitch != null; + Number yaw = this.yaw.getSingle(event); + Number pitch = this.pitch.getSingle(event); + if (yaw == null && pitch == null) + break direction; + return CollectionUtils.array(vector.toLocation(world, yaw == null ? 0 : yaw.floatValue(), pitch == null ? 0 : pitch.floatValue())); + } + return CollectionUtils.array(vector.toLocation(world)); } @Override @@ -103,10 +110,10 @@ public Class getReturnType() { } @Override - public String toString(@Nullable Event e, boolean debug) { - if (yawpitch) - return "location from " + vector.toString(e, debug) + " with yaw " + yaw.toString() + " and pitch " + pitch.toString(e, debug); - return "location from " + vector.toString(e, debug); + public String toString(@Nullable Event event, boolean debug) { + if (hasDirection) + return "location of " + vector.toString(event, debug) + " in " + world.toString(event, debug) + " with yaw " + yaw.toString(event, debug) + " and pitch " + pitch.toString(event, debug); + return "location of " + vector.toString(event, debug) + " in " + world.toString(event, debug); } } diff --git a/src/test/skript/tests/syntaxes/expressions/ExprLocationFromVector.sk b/src/test/skript/tests/syntaxes/expressions/ExprLocationFromVector.sk new file mode 100644 index 00000000000..46ca2a34633 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprLocationFromVector.sk @@ -0,0 +1,24 @@ +test "location from vector": + set {_world} to world "world" + + set {_vector} to vector(x coordinate of spawn of {_world}, y coordinate of spawn of {_world}, z coordinate of spawn of {_world}) + set {_location} to location of {_vector} in {_world} + assert {_location} is spawn of {_world} with "location from vector failed, expected %spawn of {_world}%, got %{_vector}%" + + set {_vector} to vector of {_location} + assert the x component of {_vector} is x coordinate of spawn of {_world} with "re-creating vector x failed" + assert the y component of {_vector} is y coordinate of spawn of {_world} with "re-creating vector y failed" + assert the z component of {_vector} is z coordinate of spawn of {_world} with "re-creating vector z failed" + + set {_location} to location of vector(0, -5, 43.5) in {_world} + assert the x coordinate of {_location} is 0 with "vector to location x failed" + assert the y coordinate of {_location} is -5 with "vector to location y failed" + assert the z coordinate of {_location} is 43.5 with "vector to location z failed" + + set {_location} to location of vector(0, 0, 0) in {_world} with yaw 25 and pitch -90 + assert the yaw of {_location} is 25 with "vector to location yaw failed" + assert the pitch of {_location} is -90 with "vector to location pitch failed" + + set {_location} to location of vector(0, 0, 0) in {_world} with yaw 25 and pitch {_null} + assert the yaw of {_location} is 25 with "vector to location absent yaw failed" + assert the pitch of {_location} is 0 with "vector to location absent pitch failed" From 3b3ebb2d9cb925bdaee2d4e9c2f72df7f63b320c Mon Sep 17 00:00:00 2001 From: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:09:54 -0700 Subject: [PATCH 08/67] Fix ExprTarget not accounting for Spigot servers and add ignore blocks (#6157) * Fix ExprTarget not accounting for Spigot servers and add ignore blocks * Apply suggestions * Apply suggestions * Revert change * Update src/main/java/ch/njol/skript/expressions/ExprTarget.java Co-authored-by: _tud * Update ExprTarget.java * Update ExprTarget.java * Set the max target block distance * Paper not needed and add Display entity support * Update ExprTarget.java * Update ExprTarget.java * Update ExprTarget.java * Ignore spectators * Apply changes * Fix null pointer --------- Co-authored-by: _tud Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../njol/skript/expressions/ExprTarget.java | 110 +++++++++++++----- 1 file changed, 84 insertions(+), 26 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/ExprTarget.java b/src/main/java/ch/njol/skript/expressions/ExprTarget.java index 1d8097aceb5..8fba8caee04 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprTarget.java +++ b/src/main/java/ch/njol/skript/expressions/ExprTarget.java @@ -18,6 +18,22 @@ */ package ch.njol.skript.expressions; +import java.util.function.Predicate; + +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Mob; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.util.RayTraceResult; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval; +import org.jetbrains.annotations.Nullable; + import ch.njol.skript.Skript; import ch.njol.skript.SkriptConfig; import ch.njol.skript.classes.Changer.ChangeMode; @@ -34,23 +50,13 @@ import ch.njol.skript.registrations.EventValues; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Mob; -import org.bukkit.entity.Player; -import org.bukkit.event.Event; -import org.bukkit.event.entity.EntityTargetEvent; -import org.bukkit.util.RayTraceResult; -import org.bukkit.util.Vector; -import org.eclipse.jdt.annotation.Nullable; - -import java.util.List; @Name("Target") @Description({ "For players this is the entity at the crosshair.", - "For mobs and experience orbs this is the entity they are attacking/following (if any)." + "For mobs and experience orbs this is the entity they are attacking/following (if any).", + "Display entities have a hit box of 0, so you should use 'target display' to collect Display entities", + "May grab entities in unloaded chunks." }) @Examples({ "on entity target:", @@ -61,15 +67,22 @@ "delete targeted entity of player # for players it will delete the target", "delete target of last spawned zombie # for entities it will make them target-less" }) -@Since("1.4.2, 2.7 (Reset)") +@Since("1.4.2, 2.7 (Reset), INSERT VERSION (ignore blocks)") public class ExprTarget extends PropertyExpression { static { Skript.registerExpression(ExprTarget.class, Entity.class, ExpressionType.PROPERTY, - "[the] target[[ed] %-*entitydata%] [of %livingentities%]", - "%livingentities%'[s] target[[ed] %-*entitydata%]"); + "[the] target[[ed] %-*entitydata%] [of %livingentities%] [blocks:ignoring blocks] [[with|at] ray[ ]size %-number%]", // TODO add a where filter when extendable https://github.com/SkriptLang/Skript/issues/4856 + "%livingentities%'[s] target[[ed] %-*entitydata%] [blocks:ignoring blocks] [[with|at] ray[ ]size %-number%]" + ); } + private static boolean ignoreBlocks; + private static int targetBlockDistance; + + @Nullable + private Expression raysize; + @Nullable private EntityData type; @@ -78,11 +91,17 @@ public class ExprTarget extends PropertyExpression { public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) { type = exprs[matchedPattern] == null ? null : (EntityData) exprs[matchedPattern].getSingle(null); setExpr((Expression) exprs[1 - matchedPattern]); + targetBlockDistance = SkriptConfig.maxTargetBlockDistance.value(); + if (targetBlockDistance < 0) + targetBlockDistance = 100; + ignoreBlocks = parser.hasTag("blocks"); + raysize = (Expression) exprs[2]; return true; } @Override protected Entity[] get(Event event, LivingEntity[] source) { + double raysize = this.raysize != null ? this.raysize.getOptionalSingle(event).orElse(0.0).doubleValue() : 0.0D; return get(source, entity -> { if (event instanceof EntityTargetEvent && entity.equals(((EntityTargetEvent) event).getEntity()) && !Delay.isDelayed(event)) { Entity target = ((EntityTargetEvent) event).getTarget(); @@ -90,7 +109,7 @@ protected Entity[] get(Event event, LivingEntity[] source) { return null; return target; } - return getTarget(entity, type); + return getTarget(entity, type, raysize); }); } @@ -118,11 +137,12 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { targetEvent.setTarget(target); } } else { + double raysize = this.raysize != null ? this.raysize.getOptionalSingle(event).orElse(0.0).doubleValue() : 0.0D; for (LivingEntity entity : getExpr().getArray(event)) { if (entity instanceof Mob) { ((Mob) entity).setTarget(target); } else if (entity instanceof Player && mode == ChangeMode.DELETE) { - Entity playerTarget = getTarget(entity, type); + Entity playerTarget = getTarget(entity, type, raysize); if (playerTarget != null && !(playerTarget instanceof OfflinePlayer)) playerTarget.remove(); } @@ -153,21 +173,59 @@ public String toString(@Nullable Event event, boolean debug) { /** * Gets an entity's target. * - * @param entity The entity to get the target of + * @param origin The entity to get the target of. + * @param type The exact EntityData to find. Can be null for any entity. + * @return The entity's target. + * @deprecated Use {@link #getTarget(LivingEntity, EntityData, double)} to include raysize. + */ + @Deprecated + @ScheduledForRemoval + public static T getTarget(LivingEntity origin, @Nullable EntityData type) { + return getTarget(origin, type, 0.0D); + } + + /** + * Gets an entity's target entity. + * + * @param origin The entity to get the target of. * @param type The exact EntityData to find. Can be null for any entity. - * @return The entity's target + * @param raysize The size of the ray for the raytrace. + * @return The entity's target. */ @Nullable @SuppressWarnings("unchecked") - public static T getTarget(LivingEntity entity, @Nullable EntityData type) { - if (entity instanceof Mob) - return ((Mob) entity).getTarget() == null || type != null && !type.isInstance(((Mob) entity).getTarget()) ? null : (T) ((Mob) entity).getTarget(); - - RayTraceResult result = entity.rayTraceEntities(SkriptConfig.maxTargetBlockDistance.value()); + public static T getTarget(LivingEntity origin, @Nullable EntityData type, double raysize) { + if (origin instanceof Mob) + return ((Mob) origin).getTarget() == null || type != null && !type.isInstance(((Mob) origin).getTarget()) ? null : (T) ((Mob) origin).getTarget(); + Location location = origin.getLocation(); + RayTraceResult result = null; + // TODO when DisplayData is added. +// if (type.getClass().equals(DisplayData.class)) +// raysize = 1.0D; + Predicate predicate = entity -> { + if (entity.equals(origin)) + return false; + if (type != null && !type.isInstance(entity)) + return false; + if (entity instanceof Player && ((Player) entity).getGameMode() == GameMode.SPECTATOR) + return false; + return true; + }; + if (!ignoreBlocks) { + RayTraceResult blockResult = origin.getWorld().rayTraceBlocks(origin.getEyeLocation(), location.getDirection(), targetBlockDistance); + if (blockResult != null) { + Vector hit = blockResult.getHitPosition(); + Location eyes = origin.getEyeLocation(); + if (hit != null) + result = origin.getWorld().rayTraceEntities(eyes, location.getDirection(), eyes.toVector().distance(hit), raysize, predicate); + } + } else { + result = origin.getWorld().rayTraceEntities(origin.getEyeLocation(), location.getDirection(), targetBlockDistance, raysize, predicate); + } if (result == null) return null; Entity hitEntity = result.getHitEntity(); - if (type != null && !type.isInstance(hitEntity)) + if (hitEntity == null) return null; return (T) result.getHitEntity(); } From d0ed4fc4996e21e8cc82b142efceb0be41b1811a Mon Sep 17 00:00:00 2001 From: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:14:21 -0700 Subject: [PATCH 09/67] Make the multiple event-values less strict (#6122) * Make the multiple event-values less strict * Debug message * Remove debug message see issue 6124 --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../skript/registrations/EventValues.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main/java/ch/njol/skript/registrations/EventValues.java b/src/main/java/ch/njol/skript/registrations/EventValues.java index d6956f32ee1..47b784d3ac2 100644 --- a/src/main/java/ch/njol/skript/registrations/EventValues.java +++ b/src/main/java/ch/njol/skript/registrations/EventValues.java @@ -82,7 +82,6 @@ public Class getValueClass() { * @return The classes of the Excludes associated with this event value */ @Nullable - @SuppressWarnings("null") public Class[] getExcludes() { if (excludes != null) return Arrays.copyOf(excludes, excludes.length); @@ -98,26 +97,26 @@ public String getExcludeErrorMessage() { return excludeErrorMessage; } } - + private final static List> defaultEventValues = new ArrayList<>(30); private final static List> futureEventValues = new ArrayList<>(); private final static List> pastEventValues = new ArrayList<>(); - + /** * The past time of an event value. Represented by "past" or "former". */ public static final int TIME_PAST = -1; - + /** * The current time of an event value. */ public static final int TIME_NOW = 0; - + /** * The future time of an event value. */ public static final int TIME_FUTURE = 1; - + /** * Get Event Values list for the specified time * @param time The time of the event values. One of @@ -127,7 +126,7 @@ public String getExcludeErrorMessage() { public static List> getEventValuesListForTime(int time) { return ImmutableList.copyOf(getEventValuesList(time)); } - + private static List> getEventValuesList(int time) { if (time == -1) return pastEventValues; @@ -242,7 +241,7 @@ public static T getEventValue(E e, Class c, int time) { * @return true or false if the event and type have multiple getters. */ public static Kleenean hasMultipleGetters(Class event, Class type, int time) { - List> getters = getEventValueGetters(event, type, time, true); + List> getters = getEventValueGetters(event, type, time, true, false); if (getters == null) return Kleenean.UNKNOWN; return Kleenean.get(getters.size() > 1); @@ -273,13 +272,18 @@ public static Kleenean hasMultipleGetters(Class event, C return list.get(0); } + @Nullable + private static List> getEventValueGetters(Class event, Class type, int time, boolean allowDefault) { + return getEventValueGetters(event, type, time, allowDefault, true); + } + /* * We need to be able to collect all possible event-values to a list for determining problematic collisions. * Always return after the loop check if the list is not empty. */ @Nullable @SuppressWarnings("unchecked") - private static List> getEventValueGetters(Class event, Class type, int time, boolean allowDefault) { + private static List> getEventValueGetters(Class event, Class type, int time, boolean allowDefault, boolean allowConverting) { List> eventValues = getEventValuesList(time); List> list = new ArrayList<>(); // First check for exact classes matching the parameters. @@ -313,6 +317,8 @@ public T get(E event) { } if (!list.isEmpty()) return list; + if (!allowConverting) + return null; // Most checks have returned before this below is called, but Skript will attempt to convert or find an alternative. // Third check is if the returned object matches the class. for (EventValueInfo eventValueInfo : eventValues) { @@ -361,11 +367,11 @@ public T get(E event) { // The requesting event must be assignable to the event value's event. Otherwise it'll throw an error. if (!event.isAssignableFrom(eventValueInfo.event)) continue; - + Getter getter = (Getter) getConvertedGetter(eventValueInfo, type, true); if (getter == null) continue; - + if (!checkExcludes(eventValueInfo, event)) return null; list.add(getter); From 293727ff591d022e6c41c6441ce64741734dc155 Mon Sep 17 00:00:00 2001 From: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Date: Mon, 1 Jan 2024 04:19:28 +0300 Subject: [PATCH 10/67] =?UTF-8?q?=F0=9F=9B=A0=20Change=20player=20parsing?= =?UTF-8?q?=20to=20use=20`name=20starting=20with`=20(#5875)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Enhancement: Change player parsing to use 'starting with' * Little improvement * Address Review * Remove redundant variable * Update src/main/java/ch/njol/skript/classes/data/BukkitClasses.java Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Update src/main/java/ch/njol/skript/classes/data/BukkitClasses.java Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Add usage to player and offlineplayer types * Indentation --------- Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../skript/classes/data/BukkitClasses.java | 65 ++++++++++++------- 1 file changed, 41 insertions(+), 24 deletions(-) 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 b90442fd8c1..8e943ce4c7b 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -19,6 +19,7 @@ package ch.njol.skript.classes.data; import java.io.StreamCorruptedException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -660,32 +661,46 @@ public String toVariableNameString(final Inventory i) { Classes.registerClass(new ClassInfo<>(Player.class, "player") .user("players?") .name("Player") - .description("A player. Depending on whether a player is online or offline several actions can be performed with them, " + - "though you won't get any errors when using effects that only work if the player is online (e.g. changing their inventory) on an offline player.", + .description( + "A player. Depending on whether a player is online or offline several actions can be performed with them, " + + "though you won't get any errors when using effects that only work if the player is online (e.g. changing their inventory) on an offline player.", "You have two possibilities to use players as command arguments: <player> and <offline player>. " + - "The first requires that the player is online and also accepts only part of the name, " + - "while the latter doesn't require that the player is online, but the player's name has to be entered exactly.") - .usage("") - .examples("") - .since("1.0") + "The first requires that the player is online and also accepts only part of the name, " + + "while the latter doesn't require that the player is online, but the player's name has to be entered exactly." + ).usage( + "Parsing an offline player as a player (online) will return nothing (none), for that case you would need to parse as " + + "offlineplayer which only returns nothing (none) if player doesn't exist in Minecraft databases (name not taken) otherwise it will return the player regardless of their online status." + ).examples( + "set {_p} to \"Notch\" parsed as a player # returns unless Notch is actually online or starts with Notch like Notchan", + "set {_p} to \"N\" parsed as a player # returns Notch if Notch is online because their name starts with 'N' (case insensitive) however, it would return nothing if no player whose name starts with 'N' is online." + ).since("1.0") .defaultExpression(new EventValueExpression<>(Player.class)) .after("string", "world") .parser(new Parser() { @Override @Nullable - public Player parse(String s, ParseContext context) { - if (context == ParseContext.COMMAND || context == ParseContext.PARSE) { - if (s.isEmpty()) + public Player parse(String string, ParseContext context) { + if (context == ParseContext.COMMAND) { + if (string.isEmpty()) return null; - if (UUID_PATTERN.matcher(s).matches()) - return Bukkit.getPlayer(UUID.fromString(s)); - List ps = Bukkit.matchPlayer(s); - if (ps.size() == 1) - return ps.get(0); - if (ps.size() == 0) - Skript.error(String.format(Language.get("commands.no player starts with"), s)); + if (UUID_PATTERN.matcher(string).matches()) + return Bukkit.getPlayer(UUID.fromString(string)); + String name = string.toLowerCase(Locale.ENGLISH); + int nameLength = name.length(); // caching + List players = new ArrayList<>(); + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.getName().toLowerCase(Locale.ENGLISH).startsWith(name)) { + if (player.getName().length() == nameLength) // a little better in performance than String#equals() + return player; + players.add(player); + } + } + if (players.size() == 1) + return players.get(0); + if (players.size() == 0) + Skript.error(String.format(Language.get("commands.no player starts with"), string)); else - Skript.error(String.format(Language.get("commands.multiple players start with"), s)); + Skript.error(String.format(Language.get("commands.multiple players start with"), string)); return null; } assert false; @@ -721,18 +736,20 @@ public String getDebugMessage(final Player p) { Classes.registerClass(new ClassInfo<>(OfflinePlayer.class, "offlineplayer") .user("offline ?players?") .name("Offline Player") - .description("A player that is possibly offline. See player for more information. " + + .description( + "A player that is possibly offline. See player for more information. " + "Please note that while all effects and conditions that require a player can be used with an " + - "offline player as well, they will not work if the player is not actually online.") - .usage("") - .examples("") - .since("") + "offline player as well, they will not work if the player is not actually online." + ).usage( + "Parsing an offline player as a player (online) will return nothing (none), for that case you would need to parse as " + + "offlineplayer which only returns nothing (none) if player doesn't exist in Minecraft databases (name not taken) otherwise it will return the player regardless of their online status." + ).examples("set {_p} to \"Notch\" parsed as an offlineplayer # returns Notch even if they're offline") + .since("2.0 beta 8") .defaultExpression(new EventValueExpression<>(OfflinePlayer.class)) .after("string", "world") .parser(new Parser() { @Override @Nullable - @SuppressWarnings("deprecation") public OfflinePlayer parse(final String s, final ParseContext context) { if (context == ParseContext.COMMAND || context == ParseContext.PARSE) { if (UUID_PATTERN.matcher(s).matches()) From 4291e5b6d0f0519f84c0722302ae45e61bea14d1 Mon Sep 17 00:00:00 2001 From: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:24:52 -0700 Subject: [PATCH 11/67] Entity transform events (#5800) * Entity transform events * Add missing enum * Apply changes * Apply plurality to the transform reason syntax * Cleanup EvtEntityTransform toString * Update src/main/java/ch/njol/skript/events/EvtEntityTransform.java Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * Update src/main/java/ch/njol/skript/events/EvtEntityTransform.java Co-authored-by: Shane Bee * Update src/main/resources/lang/default.lang Co-authored-by: Shane Bee * Apply changes * Organize imports * Update src/main/java/ch/njol/skript/events/EvtEntityTransform.java Co-authored-by: Shane Bee * Update src/main/resources/lang/default.lang Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --------- Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Co-authored-by: Shane Bee Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../skript/classes/data/BukkitClasses.java | 7 ++ .../classes/data/BukkitEventValues.java | 17 ++++ .../skript/events/EvtEntityTransform.java | 79 +++++++++++++++++++ .../expressions/ExprTransformReason.java | 55 +++++++++++++ src/main/resources/lang/default.lang | 14 ++++ 5 files changed, 172 insertions(+) create mode 100644 src/main/java/ch/njol/skript/events/EvtEntityTransform.java create mode 100644 src/main/java/ch/njol/skript/expressions/ExprTransformReason.java 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 8e943ce4c7b..1588be715ea 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -60,6 +60,7 @@ import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; +import org.bukkit.event.entity.EntityTransformEvent.TransformReason; import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryCloseEvent; @@ -1541,6 +1542,12 @@ public String toVariableNameString(EnchantmentOffer eo) { .description("The inventory close reason in an inventory close event.") .requiredPlugins("Paper") .since("INSERT VERSION")); + + Classes.registerClass(new EnumClassInfo<>(TransformReason.class, "transformreason", "transform reasons") + .user("(entity)? ?transform ?(reason|cause)s?") + .name("Transform Reason") + .description("Represents a transform reason of an entity transform event.") + .since("INSERT VERSION")); } } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index 1543a5c2d1d..1998e124aa4 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -98,6 +98,8 @@ import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.EntityResurrectEvent; import org.bukkit.event.entity.EntityTameEvent; +import org.bukkit.event.entity.EntityTransformEvent; +import org.bukkit.event.entity.EntityTransformEvent.TransformReason; import org.bukkit.event.entity.FireworkExplodeEvent; import org.bukkit.event.entity.HorseJumpEvent; import org.bukkit.event.entity.ItemDespawnEvent; @@ -1780,6 +1782,21 @@ public ItemStack get(PlayerStonecutterRecipeSelectEvent event) { } }, EventValues.TIME_NOW); + // EntityTransformEvent + EventValues.registerEventValue(EntityTransformEvent.class, Entity[].class, new Getter() { + @Override + @Nullable + public Entity[] get(EntityTransformEvent event) { + return event.getTransformedEntities().stream().toArray(Entity[]::new); + } + }, EventValues.TIME_NOW); + EventValues.registerEventValue(EntityTransformEvent.class, TransformReason.class, new Getter() { + @Override + @Nullable + public TransformReason get(EntityTransformEvent event) { + return event.getTransformReason(); + } + }, EventValues.TIME_NOW); } } diff --git a/src/main/java/ch/njol/skript/events/EvtEntityTransform.java b/src/main/java/ch/njol/skript/events/EvtEntityTransform.java new file mode 100644 index 00000000000..3d3a8a26325 --- /dev/null +++ b/src/main/java/ch/njol/skript/events/EvtEntityTransform.java @@ -0,0 +1,79 @@ +/** + * 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.events; + +import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityTransformEvent; +import org.bukkit.event.entity.EntityTransformEvent.TransformReason; +import org.eclipse.jdt.annotation.Nullable; + +import ch.njol.skript.Skript; +import ch.njol.skript.entity.EntityData; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptEvent; +import ch.njol.skript.lang.SkriptParser.ParseResult; + +public class EvtEntityTransform extends SkriptEvent { + + static { + Skript.registerEvent("Entity Transform", EvtEntityTransform.class, EntityTransformEvent.class, "(entit(y|ies)|%*-entitydatas%) transform[ing] [due to %-transformreasons%]") + .description("Called when an entity is about to be replaced by another entity.", + "Examples when it's called include; when a zombie gets cured and a villager spawns, " + + "an entity drowns in water like a zombie that turns to a drown, " + + "an entity that gets frozen in powder snow, " + + "a mooshroom that when sheared, spawns a new cow.") + .examples("on a zombie transforming due to curing:", "on mooshroom transforming:", "on zombie, skeleton or slime transform:") + .keywords("entity transform") + .since("INSERT VERSION"); + } + + @Nullable + private Literal reasons; + + @Nullable + private Literal> datas; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Literal[] args, int matchedPattern, ParseResult parseResult) { + datas = (Literal>) args[0]; + reasons = (Literal) args[1]; + return true; + } + + @Override + public boolean check(Event event) { + if (!(event instanceof EntityTransformEvent)) + return false; + EntityTransformEvent transformEvent = (EntityTransformEvent) event; + if (reasons != null && !reasons.check(event, reason -> transformEvent.getTransformReason().equals(reason))) + return false; + if (datas != null && !datas.check(event, data -> data.isInstance(transformEvent.getEntity()))) + return false; + return true; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + if (datas == null) + return "entities transforming" + (reasons == null ? "" : " due to " + reasons.toString(event, debug)); + return datas.toString(event, debug) + " transforming" + (reasons == null ? "" : " due to " + reasons.toString(event, debug)); + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprTransformReason.java b/src/main/java/ch/njol/skript/expressions/ExprTransformReason.java new file mode 100644 index 00000000000..09d36074b93 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprTransformReason.java @@ -0,0 +1,55 @@ +/** + * 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 org.bukkit.event.Event; +import org.bukkit.event.entity.EntityTransformEvent.TransformReason; +import org.eclipse.jdt.annotation.Nullable; + +import ch.njol.skript.Skript; +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.EventValueExpression; +import ch.njol.skript.lang.ExpressionType; + +@Name("Transform Reason") +@Description("The transform reason within an entity entity transform event.") +@Examples({ + "on entity transform:", + "\ttransform reason is infection, drowned or frozen" +}) +@Since("INSERT VERSION") +public class ExprTransformReason extends EventValueExpression { + + static { + Skript.registerExpression(ExprTransformReason.class, TransformReason.class, ExpressionType.SIMPLE, "[the] transform[ing] (cause|reason|type)"); + } + + public ExprTransformReason() { + super(TransformReason.class); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "transform reason"; + } + +} diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index e1a2dccc4ac..07f1a1dc45b 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -1960,6 +1960,19 @@ quit reasons: kicked: kicked timed_out: timed out +# -- Transform Reasons -- +transform reasons: + cured: zombie cure, zombie curing, curing + drowned: entity drowning, drowning, zombie converting to drowned, zombie drowning + frozen: entity freezing, entity freeze, skeleton freezing, skeleton freeze, skeleton converting to stray + lightning: lightning, creeper super charge + metamorphosis: tadpole converting, tadpole converting to frog, tadpole metamorphosis, metamorphosis + piglin_zombified: piglin zombification + sheared: mooshroom shear, mooshroom shearing + split: slime split, slime splitting, split, magma slime split, magma slime splitting + unknown: unknown + infection: infection, villager infection + # -- Boolean -- boolean: true: @@ -2030,6 +2043,7 @@ types: gamerulevalue: gamerule value¦s @a quitreason: quit reason¦s @a inventoryclosereason: inventory close reason¦s @an + transformreason: transform reason¦s @a # Skript weathertype: weather type¦s @a From 7b669c97eec25a16562717ac718a4d329f6b6d5e Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Sun, 31 Dec 2023 20:28:55 -0500 Subject: [PATCH 12/67] Cleanup EnumUtils (#5744) * Cleanup and remove excessive validation This commit removes some validation performed by the class. Notably, validation will only be performed when the language updates, meaning this class no longer supports runtime Enum changes. * Use existing variable for obtaining array length Co-authored-by: Fusezion --------- Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Co-authored-by: Fusezion Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/util/EnumUtils.java | 113 +++++++++--------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/src/main/java/ch/njol/skript/util/EnumUtils.java b/src/main/java/ch/njol/skript/util/EnumUtils.java index 41e86ad7b66..31c64e1b23b 100644 --- a/src/main/java/ch/njol/skript/util/EnumUtils.java +++ b/src/main/java/ch/njol/skript/util/EnumUtils.java @@ -28,90 +28,93 @@ import java.util.HashMap; import java.util.Locale; +/** + * A language utility class to be used for easily handling language values representing an Enum. + * @param Generic representing the Enum. + * @see ch.njol.skript.classes.EnumClassInfo + */ public final class EnumUtils> { - private final Class c; + private final Class enumClass; private final String languageNode; + @SuppressWarnings("NotNullFieldNotInitialized") // initialized in constructor's refresh() call private String[] names; private final HashMap parseMap = new HashMap<>(); - public EnumUtils(Class c, String languageNode) { - assert c != null && c.isEnum() : c; - assert languageNode != null && !languageNode.isEmpty() && !languageNode.endsWith(".") : languageNode; + public EnumUtils(Class enumClass, String languageNode) { + assert enumClass.isEnum() : enumClass; + assert !languageNode.isEmpty() && !languageNode.endsWith(".") : languageNode; - this.c = c; + this.enumClass = enumClass; this.languageNode = languageNode; - names = new String[c.getEnumConstants().length]; + refresh(); - Language.addListener(() -> validate(true)); + Language.addListener(this::refresh); } /** - * Updates the names if the language has changed or the enum was modified (using reflection). + * Refreshes the representation of this Enum based on the currently stored language entries. */ - void validate(boolean force) { - boolean update = force; - - E[] constants = c.getEnumConstants(); + void refresh() { + E[] constants = enumClass.getEnumConstants(); + names = new String[constants.length]; + parseMap.clear(); + for (E constant : constants) { + String key = languageNode + "." + constant.name(); + int ordinal = constant.ordinal(); - if (constants.length != names.length) { // Simple check - names = new String[constants.length]; - update = true; - } else { // Deeper check - for (E constant : constants) { - if (!parseMap.containsValue(constant)) { // A new value was added to the enum - update = true; - break; + String[] options = Language.getList(key); + for (String option : options) { + option = option.toLowerCase(Locale.ENGLISH); + if (options.length == 1 && option.equals(key.toLowerCase(Locale.ENGLISH))) { + Skript.debug("Missing lang enum constant for '" + key + "'"); + continue; } - } - } - if (update) { - parseMap.clear(); - for (E e : constants) { - String key = languageNode + "." + e.name(); - int ordinal = e.ordinal(); + // Isolate the gender if one is present + NonNullPair strippedOption = Noun.stripGender(option, key); + String first = strippedOption.getFirst(); + Integer second = strippedOption.getSecond(); - String[] values = Language.getList(key); - for (String option : values) { - option = option.toLowerCase(Locale.ENGLISH); - if (values.length == 1 && option.equals(key.toLowerCase(Locale.ENGLISH))) { - Skript.warning("Missing lang enum constant for '" + key + "'"); - continue; - } - - NonNullPair strippedOption = Noun.stripGender(option, key); - String first = strippedOption.getFirst(); - Integer second = strippedOption.getSecond(); - - if (names[ordinal] == null) { // Add to name array if needed - names[ordinal] = first; - } + if (names[ordinal] == null) { // Add to name array if needed + names[ordinal] = first; + } - parseMap.put(first, e); - if (second != -1) { // There is a gender present - parseMap.put(Noun.getArticleWithSpace(second, Language.F_INDEFINITE_ARTICLE) + first, e); - } + parseMap.put(first, constant); + if (second != -1) { // There is a gender present + parseMap.put(Noun.getArticleWithSpace(second, Language.F_INDEFINITE_ARTICLE) + first, constant); } } } } - + + /** + * This method attempts to match the string input against one of the string representations of the enumerators. + * @param input a string to attempt to match against one the enumerators. + * @return The enumerator matching the input, or null if no match could be made. + */ @Nullable - public E parse(String s) { - validate(false); - return parseMap.get(s.toLowerCase(Locale.ENGLISH)); + public E parse(String input) { + return parseMap.get(input.toLowerCase(Locale.ENGLISH)); } - public String toString(E e, int flags) { - validate(false); - return names[e.ordinal()]; + /** + * This method returns the string representation of an enumerator. + * @param enumerator The enumerator to represent as a string. + * @param flags not currently used + * @return A string representation of the enumerator. + */ + public String toString(E enumerator, int flags) { + return names[enumerator.ordinal()]; } - + + /** + * @return A comma-separated string containing a list of all names representing the enumerators. + * Note that some entries may represent the same enumerator. + */ public String getAllNames() { - validate(false); return StringUtils.join(parseMap.keySet(), ", "); } From 7d2a5b5779a559d1e90f3fe9dfdec8430198d436 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Sun, 31 Dec 2023 20:38:51 -0500 Subject: [PATCH 13/67] Fix ExprDifference issues (#5406) * Rework ExprDifference init * Improve resolution of math when both types are known We will now attempt to convert into the type that has a math. If the first type has a math but the second type can't be converted, we will then try the second type's math * Avoid modifying syntax fields at runtime * Add a test --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../skript/expressions/ExprDifference.java | 224 +++++++++++------- .../syntaxes/expressions/ExprDifference.sk | 19 ++ 2 files changed, 156 insertions(+), 87 deletions(-) create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprDifference.sk diff --git a/src/main/java/ch/njol/skript/expressions/ExprDifference.java b/src/main/java/ch/njol/skript/expressions/ExprDifference.java index 944fe5c22ee..cfaf10f07df 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprDifference.java +++ b/src/main/java/ch/njol/skript/expressions/ExprDifference.java @@ -18,11 +18,6 @@ */ package ch.njol.skript.expressions; -import java.lang.reflect.Array; - -import org.bukkit.event.Event; -import org.eclipse.jdt.annotation.Nullable; - import ch.njol.skript.Skript; import ch.njol.skript.classes.Arithmetic; import ch.njol.skript.classes.ClassInfo; @@ -33,123 +28,178 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; -import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.skript.lang.Variable; import ch.njol.skript.lang.util.SimpleExpression; -import ch.njol.skript.log.ErrorQuality; import ch.njol.skript.registrations.Classes; -import ch.njol.skript.registrations.DefaultClasses; +import ch.njol.skript.util.LiteralUtils; import ch.njol.skript.util.Utils; import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; + +import java.lang.reflect.Array; -/** - * @author Peter Güttinger - */ @Name("Difference") -@Description("The difference between two values, e.g. numbers, dates or times.") -@Examples({"if difference between {command::%player%::lastuse} and now is smaller than a minute:", - "\tmessage \"You have to wait a minute before using this command again!\""}) +@Description({ + "The difference between two values", + "Supported types include numbers, dates and times." +}) +@Examples({ + "if difference between {command::%player%::lastuse} and now is smaller than a minute:", + "\tmessage \"You have to wait a minute before using this command again!\"" +}) @Since("1.4") public class ExprDifference extends SimpleExpression { - + static { - Skript.registerExpression(ExprDifference.class, Object.class, ExpressionType.COMBINED, "difference (between|of) %object% and %object%"); + Skript.registerExpression(ExprDifference.class, Object.class, ExpressionType.COMBINED, + "difference (between|of) %object% and %object%" + ); } - - @SuppressWarnings("null") + + @SuppressWarnings("NotNullFieldNotInitialized") private Expression first, second; - - @SuppressWarnings("rawtypes") + @Nullable + @SuppressWarnings("rawtypes") private Arithmetic math; - @SuppressWarnings("null") + @SuppressWarnings("NotNullFieldNotInitialized") private Class relativeType; - - @SuppressWarnings({"unchecked", "null", "unused"}) + @Override - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { - first = exprs[0]; - second = exprs[1]; - final ClassInfo ci; - if (first instanceof Variable && second instanceof Variable) { - ci = DefaultClasses.OBJECT; - } else if (first instanceof Literal && second instanceof Literal) { - first = first.getConvertedExpression(Object.class); - second = second.getConvertedExpression(Object.class); - if (first == null || second == null) - return false; - ci = Classes.getSuperClassInfo(Utils.getSuperType(first.getReturnType(), second.getReturnType())); - } else { - if (first instanceof Literal) { - first = first.getConvertedExpression(second.getReturnType()); - if (first == null) - return false; - } else if (second instanceof Literal) { - second = second.getConvertedExpression(first.getReturnType()); - if (second == null) - return false; - } - if (first instanceof Variable) { - first = first.getConvertedExpression(second.getReturnType()); - } else if (second instanceof Variable) { - second = second.getConvertedExpression(first.getReturnType()); - } - assert first != null && second != null; - ci = Classes.getSuperClassInfo(Utils.getSuperType(first.getReturnType(), second.getReturnType())); - } - assert ci != null; - if (!ci.getC().equals(Object.class) && ci.getMath() == null) { - Skript.error("Can't get the difference of " + CondCompare.f(first) + " and " + CondCompare.f(second), ErrorQuality.SEMANTIC_ERROR); + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + Expression first = LiteralUtils.defendExpression(exprs[0]); + Expression second = LiteralUtils.defendExpression(exprs[1]); + if (!LiteralUtils.canInitSafely(first, second)) { return false; } - if (ci.getC().equals(Object.class)) { - // Initialize less stuff, basically - relativeType = Object.class; // Relative math type would be null which the parser doesn't like + + Class firstReturnType = first.getReturnType(); + Class secondReturnType = second.getReturnType(); + ClassInfo classInfo = Classes.getSuperClassInfo(Utils.getSuperType(firstReturnType, secondReturnType)); + + boolean fail = false; + + if (classInfo.getC() == Object.class && (firstReturnType != Object.class || secondReturnType != Object.class)) { + // We may not have a way to obtain the difference between these two values. Further checks needed. + + // These two types are unrelated, meaning conversion is needed + if (firstReturnType != Object.class && secondReturnType != Object.class) { + + // We will work our way out of failure + fail = true; + + // Attempt to use first type's math + classInfo = Classes.getSuperClassInfo(firstReturnType); + if (classInfo.getMath() != null) { // Try to convert second to first + Expression secondConverted = second.getConvertedExpression(firstReturnType); + if (secondConverted != null) { + second = secondConverted; + fail = false; + } + } + + if (fail) { // First type won't work, try second type + classInfo = Classes.getSuperClassInfo(secondReturnType); + if (classInfo.getMath() != null) { // Try to convert first to second + Expression firstConverted = first.getConvertedExpression(secondReturnType); + if (firstConverted != null) { + first = firstConverted; + fail = false; + } + } + } + + } else { // It may just be the case that the type of one of our values cannot be known at parse time + Expression converted; + if (firstReturnType == Object.class) { + converted = first.getConvertedExpression(secondReturnType); + if (converted != null) { // This may fail if both types are Object + first = converted; + } + } else { // This is an else statement to avoid X->Object conversions + converted = second.getConvertedExpression(firstReturnType); + if (converted != null) { + second = converted; + } + } + + if (converted == null) { // It's unlikely that these two can be compared + fail = true; + } else { // Attempt to resolve a better class info + classInfo = Classes.getSuperClassInfo(Utils.getSuperType(first.getReturnType(), second.getReturnType())); + } + } + + } + + if (classInfo.getC() == Object.class) { // We will have to determine the type during runtime + relativeType = Object.class; + } else if (classInfo.getMath() == null || classInfo.getMathRelativeType() == null) { + fail = true; } else { - math = ci.getMath(); - relativeType = ci.getMathRelativeType(); + math = classInfo.getMath(); + relativeType = classInfo.getMathRelativeType(); + } + + if (fail) { + Skript.error("Can't get the difference of " + CondCompare.f(first) + " and " + CondCompare.f(second)); + return false; } + + this.first = first; + this.second = second; + return true; } - - @SuppressWarnings("unchecked") + @Override @Nullable - protected Object[] get(final Event e) { - final Object f = first.getSingle(e), s = second.getSingle(e); - if (f == null || s == null) - return null; - final Object[] one = (Object[]) Array.newInstance(relativeType, 1); - - // If we're comparing object expressions, such as variables, math is null right now - if (relativeType.equals(Object.class)) { - ClassInfo info = Classes.getSuperClassInfo(Utils.getSuperType(f.getClass(), s.getClass())); + @SuppressWarnings({"unchecked", "rawtypes"}) + protected Object[] get(Event event) { + Object first = this.first.getSingle(event); + Object second = this.second.getSingle(event); + if (first == null || second == null) { + return new Object[0]; + } + + Arithmetic math = this.math; + Class relativeType = this.relativeType; + + if (relativeType == Object.class) { // Try to determine now that actual types are known + ClassInfo info = Classes.getSuperClassInfo(Utils.getSuperType(first.getClass(), second.getClass())); math = info.getMath(); if (math == null) { // User did something stupid, just return for them - return one; + return new Object[0]; + } + relativeType = info.getMathRelativeType(); + if (relativeType == null) { // Unlikely to be the case, but math is not null meaning we can calculate the difference + relativeType = Object.class; } } - - assert math != null; // NOW it cannot be null - one[0] = math.difference(f, s); + + Object[] one = (Object[]) Array.newInstance(relativeType, 1); + + assert math != null; // it cannot be null here + one[0] = math.difference(first, second); return one; } - + @Override - public Class getReturnType() { - return relativeType; + public boolean isSingle() { + return true; } - + @Override - public String toString(final @Nullable Event e, final boolean debug) { - return "difference between " + first.toString(e, debug) + " and " + second.toString(e, debug); + public Class getReturnType() { + return relativeType; } - + @Override - public boolean isSingle() { - return true; + public String toString(@Nullable Event event, boolean debug) { + return "difference between " + first.toString(event, debug) + " and " + second.toString(event, debug); } - + } diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDifference.sk b/src/test/skript/tests/syntaxes/expressions/ExprDifference.sk new file mode 100644 index 00000000000..6492ead2da4 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDifference.sk @@ -0,0 +1,19 @@ +test "ExprDifference": + + # basic tests + + set {_val} to difference between 1 and 2 + assert {_val} is 1 with "(1) expected '1', got '%{_val}%'" + delete {_val} + + set {_val} to difference between 2 and 1 + assert {_val} is 1 with "(2) expected '1', got '%{_val}%'" + delete {_val} + + # ensure proper parsing order + set {_a} to "5" + set {_b} to "10" + set {_val} to difference between "%{_a}%" parsed as a number and "%{_b}%" parsed as a number + set {_exp} to difference between ("%{_a}%" parsed as a number) and ("%{_b}%" parsed as a number) + assert {_exp} is 5 with "(5) expected '5', but got '%{_exp}%'" + assert {_val} is 5 with "(6) expected '5', but got '%{_val}%'" From 5bc25d6742c4e73ba5055ab6f218225c80219cc5 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:20:45 -0800 Subject: [PATCH 14/67] Add warnings for using %player% without using the UUID config setting. (#6271) * Update VariableString.java * messed up imports * Catch offline players --- .../ch/njol/skript/lang/VariableString.java | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/VariableString.java b/src/main/java/ch/njol/skript/lang/VariableString.java index f9ae8f4ab2f..ccfc0c14c5d 100644 --- a/src/main/java/ch/njol/skript/lang/VariableString.java +++ b/src/main/java/ch/njol/skript/lang/VariableString.java @@ -18,21 +18,8 @@ */ package ch.njol.skript.lang; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.stream.Collectors; - -import org.bukkit.ChatColor; -import org.bukkit.event.Event; -import org.eclipse.jdt.annotation.Nullable; -import org.jetbrains.annotations.NotNull; -import org.skriptlang.skript.lang.script.Script; - -import com.google.common.collect.Lists; - import ch.njol.skript.Skript; +import ch.njol.skript.SkriptConfig; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.expressions.ExprColoured; @@ -54,6 +41,19 @@ import ch.njol.util.StringUtils; import ch.njol.util.coll.CollectionUtils; import ch.njol.util.coll.iterator.SingleItemIterator; +import com.google.common.collect.Lists; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.NotNull; +import org.skriptlang.skript.lang.script.Script; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; /** * Represents a string that may contain expressions, and is thus "variable". @@ -229,6 +229,12 @@ public static VariableString newInstance(String orig, StringMode mode) { log.printErrors("Can't understand this expression: " + s.substring(c + 1, c2)); return null; } else { + if (!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." + ); + } string.add(expr); } log.printLog(); From b6b63e64365026d0e78db323ca3f49b2cfb4cf5f Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:27:24 -0800 Subject: [PATCH 15/67] Add ExprSentCommands and EvtPlayerCommandSend (#5948) * ExprSentCommands and EvtPlayerCommandSend * add links * Requested Changes and Prevent Delayed Changes * Apply suggestions from code review Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * Suggestions * Requested Changes * silly mistake * Requested Changes * Apply suggestions from code review Co-authored-by: Patrick Miller * Update ExprSentCommands.java --------- Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Co-authored-by: Patrick Miller --- .../skript/events/EvtPlayerCommandSend.java | 75 +++++++++ .../skript/expressions/ExprSentCommands.java | 155 ++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 src/main/java/ch/njol/skript/events/EvtPlayerCommandSend.java create mode 100644 src/main/java/ch/njol/skript/expressions/ExprSentCommands.java diff --git a/src/main/java/ch/njol/skript/events/EvtPlayerCommandSend.java b/src/main/java/ch/njol/skript/events/EvtPlayerCommandSend.java new file mode 100644 index 00000000000..9ef380ada7a --- /dev/null +++ b/src/main/java/ch/njol/skript/events/EvtPlayerCommandSend.java @@ -0,0 +1,75 @@ +/** + * 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.events; + +import ch.njol.skript.Skript; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptEvent; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; +import org.bukkit.event.Event; +import org.bukkit.event.player.PlayerCommandSendEvent; +import org.eclipse.jdt.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Collection; + +public class EvtPlayerCommandSend extends SkriptEvent { + + static { + Skript.registerEvent("Send Command List", EvtPlayerCommandSend.class, PlayerCommandSendEvent.class, "send[ing] [of [the]] [server] command[s] list", "[server] command list send") + .description( + "Called when the server sends a list of commands to the player. This usually happens on join. The sent commands " + + "can be modified via the sent commands expression.", + "Modifications will affect what commands show up for the player to tab complete. They will not affect what commands the player can actually run.", + "Adding new commands to the list is illegal behavior and will be ignored." + ) + .examples( + "on send command list:", + "\tset command list to command list where [input does not contain \":\"]", + "\tremove \"help\" from command list" + ) + .since("INSERT VERSION"); + } + + private final Collection originalCommands = new ArrayList<>(); + + @Override + public boolean init(Literal[] args, int matchedPattern, ParseResult parseResult) { + return true; + } + + @Override + public boolean check(Event event) { + originalCommands.clear(); + originalCommands.addAll(((PlayerCommandSendEvent) event).getCommands()); + return true; + } + + public ImmutableCollection getOriginalCommands() { + return ImmutableList.copyOf(originalCommands); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "sending of the server command list"; + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprSentCommands.java b/src/main/java/ch/njol/skript/expressions/ExprSentCommands.java new file mode 100644 index 00000000000..5c4f17ab16e --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprSentCommands.java @@ -0,0 +1,155 @@ +/** + * 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.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Events; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.events.EvtPlayerCommandSend; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Kleenean; +import com.google.common.collect.Lists; +import org.bukkit.event.Event; +import org.bukkit.event.player.PlayerCommandSendEvent; +import org.eclipse.jdt.annotation.Nullable; +import org.skriptlang.skript.lang.structure.Structure; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@Name("Sent Command List") +@Description({ + "The commands that will be sent to the player in a send commands to player event.", + "Modifications will affect what commands show up for the player to tab complete. They will not affect what commands the player can actually run.", + "Adding new commands to the list is illegal behavior and will be ignored." +}) +@Examples({ + "on send command list:", + "\tset command list to command list where [input does not contain \":\"]", + "\tremove \"help\" from command list" +}) +@Since("INSERT VERSION") +@Events("send command list") +public class ExprSentCommands extends SimpleExpression { + + static { + Skript.registerExpression(ExprSentCommands.class, String.class, ExpressionType.SIMPLE, "[the] [sent] [server] command[s] list"); + } + + private EvtPlayerCommandSend parent; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + Structure structure = getParser().getCurrentStructure(); + if (!(structure instanceof EvtPlayerCommandSend)) { + Skript.error("The 'command list' expression can only be used in a 'send command list' event"); + return false; + } + + if (!isDelayed.isFalse()) { + Skript.error("Can't change the command list after the event has already passed"); + return false; + } + parent = (EvtPlayerCommandSend) structure; + return true; + } + + @Override + @Nullable + protected String[] get(Event event) { + if (!(event instanceof PlayerCommandSendEvent)) + return null; + return ((PlayerCommandSendEvent) event).getCommands().toArray(new String[0]); + } + + @Override + @Nullable + public Class[] acceptChange(ChangeMode mode) { + switch (mode) { + case REMOVE: + case DELETE: + case SET: + case RESET: + return new Class[]{String[].class}; + // note that ADD is not supported, as adding new commands to the commands collection is illegal behaviour + default: + return null; + } + } + + @Override + public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { + if (!(event instanceof PlayerCommandSendEvent)) + return; + + Collection commands = ((PlayerCommandSendEvent) event).getCommands(); + + // short circuit if we're just clearing the list + if (mode == ChangeMode.DELETE) { + commands.clear(); + return; + } + + List deltaCommands = (delta != null && delta.length > 0) ? Lists.newArrayList((String[]) delta) : new ArrayList<>(); + switch (mode) { + case REMOVE: + commands.removeAll(deltaCommands); + break; + case SET: + // remove all completely new commands, as adding new commands to the commands collection is illegal behaviour + List newCommands = new ArrayList<>(deltaCommands); + newCommands.removeAll(parent.getOriginalCommands()); + deltaCommands.removeAll(newCommands); + commands.clear(); + commands.addAll(deltaCommands); + break; + case RESET: + commands.clear(); + commands.addAll(parent.getOriginalCommands()); + break; + default: + assert false; + break; + } + } + + @Override + public boolean isSingle() { + return false; + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "the sent server command list"; + } + +} From a9e9a78b430ee168527ea7da12e858c6877d86ac Mon Sep 17 00:00:00 2001 From: Fusezion Date: Sun, 31 Dec 2023 21:35:14 -0500 Subject: [PATCH 16/67] Repeat Expression (#5098) * Adds new repeat() function which utilizes java's built in .repeat() * Invalid negative error * param name changes * Removed Java11 repeat * Convert to multiply() for consistency * Update branch Removed function repeat Added repeat expression * Add missing annotations * Mild change * Addressed requested changes * Added support for multiple strings * Add missing ending whitespace Co-authored-by: Patrick Miller * Requested changed Co-Authored-By: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Requested change Forgot about this one Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Fix nullable count Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Mild clean up Optional s is now required for `times` Added test skript Co-Authored-By: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Array revert * Update ExprRepeat.java Co-Authored-By: Pikachu920 <28607612+Pikachu920@users.noreply.github.com> * Revisiting after a couple months :sparkles: Updated test to be a little more informative :sparkles: Added an additional test for ensuring 0/null return nothing :sparkles: When inputting 0 or null for number we return null for strings :sparkles: Refactored some parts :sparkles: Changed `%number%` to `%integer%` zero reason not to anymore with new number converters * Apply suggestions from code review Woops thanks! Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * Use a stream Personally I'm not for this entirely Co-authored-by: TheLimeGlass * Use #collect instead of Java16 #toList * Use Expression#stream instead * Update test with an additional 14 or so For the love of god fix the list to list comparisons * Oops didn't see this * Remove duplicate var * Additional example showcasing comparison --------- Co-authored-by: Patrick Miller Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Co-authored-by: Pikachu920 <28607612+Pikachu920@users.noreply.github.com> Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../njol/skript/expressions/ExprRepeat.java | 83 +++++++++++++++++++ .../tests/syntaxes/expressions/ExprRepeat.sk | 72 ++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 src/main/java/ch/njol/skript/expressions/ExprRepeat.java create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprRepeat.sk diff --git a/src/main/java/ch/njol/skript/expressions/ExprRepeat.java b/src/main/java/ch/njol/skript/expressions/ExprRepeat.java new file mode 100644 index 00000000000..831c448ead0 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprRepeat.java @@ -0,0 +1,83 @@ +/** + * 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.Skript; +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.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Kleenean; +import ch.njol.util.StringUtils; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; + +@Name("Repeat String") +@Description("Repeats inputted strings a given amount of times.") +@Examples({ + "broadcast nl and nl repeated 200 times", + "broadcast \"Hello World \" repeated 5 times", + "if \"aa\" repeated 2 times is \"aaaa\":", + "\tbroadcast \"Ahhhh\" repeated 100 times" +}) +@Since("INSERT VERSION") +public class ExprRepeat extends SimpleExpression { + + static { + Skript.registerExpression(ExprRepeat.class, String.class, ExpressionType.COMBINED, "%strings% repeated %integer% time[s]"); + } + + private Expression strings; + private Expression repeatCount; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + strings = (Expression) exprs[0]; + repeatCount = (Expression) exprs[1]; + return true; + } + + @Override + protected @Nullable String[] get(Event event) { + int repeatCount = this.repeatCount.getOptionalSingle(event).orElse(0); + if (repeatCount < 1) + return new String[0]; + return strings.stream(event).map(string -> StringUtils.multiply(string, repeatCount)).toArray(String[]::new); + } + + @Override + public boolean isSingle() { + return strings.isSingle(); + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return strings.toString(event, debug) + " repeated " + repeatCount.toString(event, debug) + " times"; + } + +} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprRepeat.sk b/src/test/skript/tests/syntaxes/expressions/ExprRepeat.sk new file mode 100644 index 00000000000..fc2cbac598b --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprRepeat.sk @@ -0,0 +1,72 @@ +test "repeat expression": + + # Literal Test + assert "a" repeated 1 times is "a" with "ExprRepeat - 'a' repeated once is not 'a'" + assert length of "a a" repeated 10 times is less than 30 to fail with "ExprRepeat - length of 'a a' repeated 10 times is not less than 30" + assert nl repeated 3 times is "%nl%%nl%%nl%" with "ExprRepeat - 'nl' repeated 3 times is not '%nl%%nl%%nl%'" + assert "string" repeated 0 times is "string" to fail with "ExprRepeat - 'string' repeated 0 times is 'string'" + assert "" repeated 2 times is "" with "ExprRepeat - '' repeated 2 times is not ''" + + # Variable Test + set {_string} to "a" + set {_repeat} to 1 + assert {_string} repeated {_repeat} times is "a" with "ExprRepeat Var - 'a' repeated once is not 'a'" + + set {_string} to "a a" + set {_repeat} to 10 + assert length of {_string} repeated {_repeat} times is less than 30 to fail with "ExprRepeat Var - length of 'a a' repeated 10 times is not less than 30" + + set {_string} to nl + set {_repeat} to 3 + assert {_string} repeated {_repeat} times is "%nl%%nl%%nl%" with "ExprRepeat Var - 'nl' repeated 3 times is not '%nl%%nl%%nl%'" + + set {_string} to "string" + set {_repeat} to 0 + assert {_string} repeated {_repeat} times is "string" to fail with "ExprRepeat Var - 'string' repeated 0 times is 'string'" + + set {_string} to "" + set {_repeat} to 2 + assert {_string} repeated {_repeat} times is "" with "ExprRepeat Var - '' repeated times is not ''" + + # Null Test + assert {_null} repeated 1 times is set to fail with "ExprRepeat Nulls - null repeated 1 time is set" + assert "1" repeated {_null} times is set to fail with "ExprRepeat Nulls - '1' repeated null times is set" + assert {_null} repeated {_null} times is set to fail with "ExprRepeat Nulls - null repeated null times is set" + + # Multi Test + set {_repeat} to 3 + + set {_strings::*} to "aa" and "b" repeated 3 times + if any: + {_strings::1} is not "aaaaaa" + {_strings::2} is not "bbb" + then: + assert false is true with "ExprRepeat Multi - 1) 'aa' and 'b' repeated 3 times is not 'aaaaaa' and 'bbb'" + + set {_strings::*} to "aa", "b" + set {_strings::*} to {_strings::*} repeated {_repeat} times + if any: + {_strings::1} is not "aaaaaa" + {_strings::2} is not "bbb" + then: + assert false is true with "ExprRepeat Multi - 2) 'aa' and 'b' repeated 3 times is not 'aaaaaa' and 'bbb'" + + set {_strings::*} to "aa", "b" + set {_strings::*} to {_strings::*} repeated 3 times + if any: + {_strings::1} is not "aaaaaa" + {_strings::2} is not "bbb" + then: + assert false is true with "ExprRepeat Multi - 3) 'aa' and 'b' repeated 3 times is not 'aaaaaa' and 'bbb'" + + set {_strings::*} to "aa", "b" repeated {_repeat} times + if any: + {_strings::1} is not "aaaaaa" + {_strings::2} is not "bbb" + then: + assert false is true with "ExprRepeat Multi - 4) 'aa' and 'b' repeated 3 times is not 'aaaaaa' and 'bbb'" + + + # More Usage Test + assert "&8 &1 &2 &3 &6" repeated 2 times is "&8 &1 &2 &3 &6&8 &1 &2 &3 &6" with "ExprRepeat Extra - '&8 &1 &2 &3 &6' is not properly repeated" + assert "aa" repeated -5 times is set to fail with "ExprRepeat Extra - 'aa' repeated -5 times was set" From a52236e32da385b752fb41b8d241afab50bce9f3 Mon Sep 17 00:00:00 2001 From: Eren <67760502+erenkarakal@users.noreply.github.com> Date: Mon, 1 Jan 2024 05:40:06 +0300 Subject: [PATCH 17/67] Turkish Translation (#6198) * Create turkish.lang * Update config.sk * Fix for grammar (foreign words must be split with a quote before suffixes). and improvements to make it more formal. - hamza-cskn --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- src/main/resources/config.sk | 2 +- src/main/resources/lang/turkish.lang | 188 +++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/lang/turkish.lang diff --git a/src/main/resources/config.sk b/src/main/resources/config.sk index 765b7cb7159..81545ae169c 100644 --- a/src/main/resources/config.sk +++ b/src/main/resources/config.sk @@ -31,7 +31,7 @@ # ==== General Options ==== language: english -# Which language to use. Currently English, German, Korean, French, Polish, Japanese and Simplified Chinese +# Which language to use. Currently English, German, Korean, French, Polish, Japanese, Simplified Chinese and Turkish # are included in the download, but custom languages can be created as well. Use the name in lowercase and no spaces as the value. # Please note that not everything can be translated yet, i.e. parts of Skript will still be english if you use another language. # If you want to translate Skript to your language please read the readme.txt located in the /lang/ folder in the jar diff --git a/src/main/resources/lang/turkish.lang b/src/main/resources/lang/turkish.lang new file mode 100644 index 00000000000..4df33a52998 --- /dev/null +++ b/src/main/resources/lang/turkish.lang @@ -0,0 +1,188 @@ +# Which version of Skript this language file was written for +version: @version@ + +# What null (nothing) should show up as in a string/text +none: + +# -- Skript -- +skript: + copyright: ~ © Peter Güttinger yani Njol tarafından yapıldı ~ + prefix: [Skript] + quotes error: Tırnak işaretlerinin (") geçersiz kullanımı. "Tırnak işareti içindeki metinde" tırnak işaretleri kullanmak istiyorsanız 2 tane kullanın: "". + brackets error: Geçersiz miktar veya parantez yerleşimi. Lütfen her açılan paranteze karşılık gelen bir kapatma parantezine sahip olduğundan emin olun. + invalid reload: Skript sadece Bukkit'in '/reload' veya Skript'in '/skript reload' komuduyla tekrar yüklenebilir. + no scripts: Hiçbir script bulunamadı, belki birkaç tane yazmalısın ;) + no errors: Tüm scriptler hatasız yüklendi. + scripts loaded: %s tane script toplam %s tane structure ile %s'de yüklendi. + finished loading: Yükleme tamamlandı. + +# -- Skript command -- +skript command: + usage: Kullanım: + help: + description: Skript'in ana komutu + help: Bu yardım mesajını gösterir. Daha fazla bilgi almak için '/skript reload/enable/disable/update' kullanın. + reload: + description: Belirli bir script'i, tüm script'leri veya her şeyi tekrar yükler. + all: Configi, tüm alias configlerini ve tüm script'leri tekrar yükler. + config: Yapılandırma dosyasını yeniden yükler. + aliases: Aliases yapılandırma dosyasını yeniden yükler. (aliases-english.zip veya eklenti dosyası) + scripts: Tüm script'leri yeniden yükler +