From 9786eb49a1c0b901aa890ce98b9af06162a30746 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:23:30 +0100 Subject: [PATCH 01/18] Add number format function (#7166) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚀 Add number format expression * Little changes * Prevent variables usage * Support variables as custom format * Make it return null if format is invalid * Add test script * Update test script * Update test script * Changes * Fix class to use same technique as #4664 * Apply suggestions from code review Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Add helpful comments and use lambda * Update src/main/java/ch/njol/skript/expressions/ExprFormatNumber.java Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Change expression to function * Remove unused imports * Refactoring and merge base, updating examples * return null instead of empty string * Update examples * Warn instead of erroring, but probably better to remove * Address reviews * better variable naming * init commit * update file name --------- Co-authored-by: Ayham Al-Ali Co-authored-by: Ayham Al-Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Co-authored-by: Moderocky --- .../skript/classes/data/DefaultFunctions.java | 120 ++++++++++++------ .../regressions/7166-formatted numbers.sk | 26 ++++ 2 files changed, 107 insertions(+), 39 deletions(-) create mode 100644 src/test/skript/tests/regressions/7166-formatted numbers.sk diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java index 62997b3a76b..5c770ca0d5e 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java @@ -50,22 +50,26 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; +import java.text.DecimalFormat; import java.util.Calendar; import java.util.List; import java.util.UUID; public class DefaultFunctions { - + private static String str(double n) { return StringUtils.toString(n, 4); } - + + private static final DecimalFormat DEFAULT_INTEGER_FORMAT = new DecimalFormat("###,###"); + private static final DecimalFormat DEFAULT_DECIMAL_FORMAT = new DecimalFormat("###,###.##"); + static { Parameter[] numberParam = new Parameter[] {new Parameter<>("n", DefaultClasses.NUMBER, true, null)}; Parameter[] numbersParam = new Parameter[] {new Parameter<>("ns", DefaultClasses.NUMBER, false, null)}; - + // basic math functions - + Functions.registerFunction(new SimpleJavaFunction("floor", numberParam, DefaultClasses.LONG, true) { @Override public Long[] executeSimple(Object[][] params) { @@ -76,7 +80,7 @@ public Long[] executeSimple(Object[][] params) { }.description("Rounds a number down, i.e. returns the closest integer smaller than or equal to the argument.") .examples("floor(2.34) = 2", "floor(2) = 2", "floor(2.99) = 2") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("round", new Parameter[] {new Parameter<>("n", DefaultClasses.NUMBER, true, null), new Parameter<>("d", DefaultClasses.NUMBER, true, new SimpleLiteral(0, false))}, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -97,7 +101,7 @@ public Number[] executeSimple(Object[][] params) { }.description("Rounds a number, i.e. returns the closest integer to the argument. Place a second argument to define the decimal placement.") .examples("round(2.34) = 2", "round(2) = 2", "round(2.99) = 3", "round(2.5) = 3") .since("2.2, 2.7 (decimal placement)")); - + Functions.registerFunction(new SimpleJavaFunction("ceil", numberParam, DefaultClasses.LONG, true) { @Override public Long[] executeSimple(Object[][] params) { @@ -108,7 +112,7 @@ public Long[] executeSimple(Object[][] params) { }.description("Rounds a number up, i.e. returns the closest integer larger than or equal to the argument.") .examples("ceil(2.34) = 3", "ceil(2) = 2", "ceil(2.99) = 3") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("ceiling", numberParam, DefaultClasses.LONG, true) { @Override public Long[] executeSimple(Object[][] params) { @@ -119,7 +123,7 @@ public Long[] executeSimple(Object[][] params) { }.description("Alias of ceil.") .examples("ceiling(2.34) = 3", "ceiling(2) = 2", "ceiling(2.99) = 3") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("abs", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -131,7 +135,7 @@ public Number[] executeSimple(Object[][] params) { }.description("Returns the absolute value of the argument, i.e. makes the argument positive.") .examples("abs(3) = 3", "abs(-2) = 2") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("mod", new Parameter[] {new Parameter<>("d", DefaultClasses.NUMBER, true, null), new Parameter<>("m", DefaultClasses.NUMBER, true, null)}, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -146,7 +150,7 @@ public Number[] executeSimple(Object[][] params) { "The returned value is always positive. Returns NaN (not a number) if the second argument is zero.") .examples("mod(3, 2) = 1", "mod(256436, 100) = 36", "mod(-1, 10) = 9") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("exp", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -155,7 +159,7 @@ public Number[] executeSimple(Object[][] params) { }.description("The exponential function. You probably don't need this if you don't know what this is.") .examples("exp(0) = 1", "exp(1) = " + str(Math.exp(1))) .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("ln", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -165,7 +169,7 @@ public Number[] executeSimple(Object[][] params) { "Returns NaN (not a number) if the argument is negative.") .examples("ln(1) = 0", "ln(exp(5)) = 5", "ln(2) = " + StringUtils.toString(Math.log(2), 4)) .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("log", new Parameter[] {new Parameter<>("n", DefaultClasses.NUMBER, true, null), new Parameter<>("base", DefaultClasses.NUMBER, true, new SimpleLiteral(10, false))}, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -177,7 +181,7 @@ public Number[] executeSimple(Object[][] params) { "Returns NaN (not a number) if any of the arguments are negative.") .examples("log(100) = 2 # 10^2 = 100", "log(16, 2) = 4 # 2^4 = 16") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("sqrt", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -187,9 +191,9 @@ public Number[] executeSimple(Object[][] params) { "Returns NaN (not a number) if the argument is negative.") .examples("sqrt(4) = 2", "sqrt(2) = " + str(Math.sqrt(2)), "sqrt(-1) = " + str(Math.sqrt(-1))) .since("2.2")); - + // trigonometry - + Functions.registerFunction(new SimpleJavaFunction("sin", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -198,7 +202,7 @@ public Number[] executeSimple(Object[][] params) { }.description("The sine function. It starts at 0° with a value of 0, goes to 1 at 90°, back to 0 at 180°, to -1 at 270° and then repeats every 360°. Uses degrees, not radians.") .examples("sin(90) = 1", "sin(60) = " + str(Math.sin(Math.toRadians(60)))) .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("cos", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -207,7 +211,7 @@ public Number[] executeSimple(Object[][] params) { }.description("The cosine function. This is basically the sine shifted by 90°, i.e. cos(a) = sin(a + 90°), for any number a. Uses degrees, not radians.") .examples("cos(0) = 1", "cos(90) = 0") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("tan", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -216,7 +220,7 @@ public Number[] executeSimple(Object[][] params) { }.description("The tangent function. This is basically sin(arg)/cos(arg). Uses degrees, not radians.") .examples("tan(0) = 0", "tan(45) = 1", "tan(89.99) = " + str(Math.tan(Math.toRadians(89.99)))) .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("asin", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -225,7 +229,7 @@ public Number[] executeSimple(Object[][] params) { }.description("The inverse of the sine, also called arcsin. Returns result in degrees, not radians. Only returns values from -90 to 90.") .examples("asin(0) = 0", "asin(1) = 90", "asin(0.5) = " + str(Math.toDegrees(Math.asin(0.5)))) .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("acos", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -234,7 +238,7 @@ public Number[] executeSimple(Object[][] params) { }.description("The inverse of the cosine, also called arccos. Returns result in degrees, not radians. Only returns values from 0 to 180.") .examples("acos(0) = 90", "acos(1) = 0", "acos(0.5) = " + str(Math.toDegrees(Math.asin(0.5)))) .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("atan", numberParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -243,7 +247,7 @@ public Number[] executeSimple(Object[][] params) { }.description("The inverse of the tangent, also called arctan. Returns result in degrees, not radians. Only returns values from -90 to 90.") .examples("atan(0) = 0", "atan(1) = 45", "atan(10000) = " + str(Math.toDegrees(Math.atan(10000)))) .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("atan2", new Parameter[] { new Parameter<>("x", DefaultClasses.NUMBER, true, null), new Parameter<>("y", DefaultClasses.NUMBER, true, null) @@ -256,9 +260,9 @@ public Number[] executeSimple(Object[][] params) { "The returned angle is measured counterclockwise in a standard mathematical coordinate system (x to the right, y to the top).") .examples("atan2(0, 1) = 0", "atan2(10, 0) = 90", "atan2(-10, 5) = " + str(Math.toDegrees(Math.atan2(-10, 5)))) .since("2.2")); - + // more stuff - + Functions.registerFunction(new SimpleJavaFunction("sum", numbersParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -271,7 +275,7 @@ public Number[] executeSimple(Object[][] params) { }.description("Sums a list of numbers.") .examples("sum(1) = 1", "sum(2, 3, 4) = 9", "sum({some list variable::*})", "sum(2, {_v::*}, and the player's y-coordinate)") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("product", numbersParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -284,7 +288,7 @@ public Number[] executeSimple(Object[][] params) { }.description("Calculates the product of a list of numbers.") .examples("product(1) = 1", "product(2, 3, 4) = 24", "product({some list variable::*})", "product(2, {_v::*}, and the player's y-coordinate)") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("max", numbersParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -300,7 +304,7 @@ public Number[] executeSimple(Object[][] params) { }.description("Returns the maximum number from a list of numbers.") .examples("max(1) = 1", "max(1, 2, 3, 4) = 4", "max({some list variable::*})") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("min", numbersParam, DefaultClasses.NUMBER, true) { @Override public Number[] executeSimple(Object[][] params) { @@ -359,7 +363,7 @@ public Class getReturnType(Expression... arguments) { .since("2.8.0"); // misc - + Functions.registerFunction(new SimpleJavaFunction("world", new Parameter[] { new Parameter<>("name", DefaultClasses.STRING, true, null) }, DefaultClasses.WORLD, true) { @@ -419,7 +423,7 @@ public Location[] execute(FunctionEvent event, Object[][] params) { "delete all entities in radius 25 around location(50,50,50, world \"world_nether\")", "ignite all entities in radius 25 around location(1,1,1, world of player)") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("date", new Parameter[] { new Parameter<>("year", DefaultClasses.NUMBER, true, null), new Parameter<>("month", DefaultClasses.NUMBER, true, null), @@ -452,7 +456,7 @@ public Location[] execute(FunctionEvent event, Object[][] params) { 0, 0, 0 }; - + { int length = getSignature().getMaxParameters(); assert fields.length == length @@ -460,7 +464,7 @@ public Location[] execute(FunctionEvent event, Object[][] params) { && scale.length == length && relations.length == length; } - + @Override public Date[] executeSimple(Object[][] params) { Calendar c = Calendar.getInstance(); @@ -469,21 +473,21 @@ public Date[] executeSimple(Object[][] params) { for (int i = 0; i < fields.length; i++) { int field = fields[i]; Number n = (Number) params[i][0]; - + double value = n.doubleValue() * scale[i] + offsets[i] + carry; int v = (int) Math2.floor(value); carry = (value - v) * relations[i]; //noinspection MagicConstant c.set(field, v); } - + return new Date[] {new Date(c.getTimeInMillis(), c.getTimeZone())}; } }.description("Creates a date from a year, month, and day, and optionally also from hour, minute, second and millisecond.", "A time zone and DST offset can be specified as well (in minutes), if they are left out the server's time zone and DST offset are used (the created date will not retain this information).") .examples("date(2014, 10, 1) # 0:00, 1st October 2014", "date(1990, 3, 5, 14, 30) # 14:30, 5th May 1990", "date(1999, 12, 31, 23, 59, 59, 999, -3*60, 0) # almost year 2000 in parts of Brazil (-3 hours offset, no DST)") .since("2.2")); - + Functions.registerFunction(new SimpleJavaFunction("vector", new Parameter[] { new Parameter<>("x", DefaultClasses.NUMBER, true, null), new Parameter<>("y", DefaultClasses.NUMBER, true, null), @@ -497,11 +501,11 @@ public Vector[] executeSimple(Object[][] params) { ((Number)params[2][0]).doubleValue() )}; } - + }.description("Creates a new vector, which can be used with various expressions, effects and functions.") .examples("vector(0, 0, 0)") .since("2.2-dev23")); - + Functions.registerFunction(new SimpleJavaFunction("calcExperience", new Parameter[] { new Parameter<>("level", DefaultClasses.LONG, true, null) }, DefaultClasses.LONG, true) { @@ -518,13 +522,13 @@ public Long[] executeSimple(Object[][] params) { } else { // Half experience points do not exist, anyway exp = (int) (4.5 * level * level - 162.5 * level + 2220); } - + return new Long[] {exp}; } - + }.description("Calculates the total amount of experience needed to achieve given level from scratch in Minecraft.") .since("2.2-dev32")); - + Functions.registerFunction(new SimpleJavaFunction("rgb", new Parameter[] { new Parameter<>("red", DefaultClasses.LONG, true, null), new Parameter<>("green", DefaultClasses.LONG, true, null), @@ -537,7 +541,7 @@ public ColorRGB[] executeSimple(Object[][] params) { Long green = (Long) params[1][0]; Long blue = (Long) params[2][0]; Long alpha = (Long) params[3][0]; - + return CollectionUtils.array(ColorRGB.fromRGBA(red.intValue(), green.intValue(), blue.intValue(), alpha.intValue())); } }).description("Returns a RGB color from the given red, green and blue parameters. Alpha values can be added optionally, " + @@ -687,6 +691,44 @@ public Quaternionf[] executeSimple(Object[][] params) { } } // end joml functions + Functions.registerFunction(new SimpleJavaFunction<>("formatNumber", new Parameter[]{ + new Parameter<>("number", DefaultClasses.NUMBER, true, null), + new Parameter<>("format", DefaultClasses.STRING, true, new SimpleLiteral<>("", true)) + }, DefaultClasses.STRING, true) { + @Override + public String[] executeSimple(Object[][] params) { + Number number = (Number) params[0][0]; + String format = (String) params[1][0]; + + if (format.isEmpty()) { + if (number instanceof Double || number instanceof Float) { + return new String[]{DEFAULT_DECIMAL_FORMAT.format(number)}; + } else { + return new String[]{DEFAULT_INTEGER_FORMAT.format(number)}; + } + } + + try { + return new String[]{new DecimalFormat(format).format(number)}; + } catch (IllegalArgumentException e) { + return null; // invalid format + } + } + }) + .description( + "Converts numbers to human-readable format. By default, '###,###' (e.g. '123,456,789') " + + "will be used for whole numbers and '###,###.##' (e.g. '123,456,789.00) will be used for decimal numbers. " + + "A hashtag '#' represents a digit, a comma ',' is used to separate numbers, and a period '.' is used for decimals. ", + "Will return none if the format is invalid.", + "For further reference, see this article.") + .examples( + "command /balance:", + "\taliases: bal", + "\texecutable by: players", + "\ttrigger:", + "\t\tset {_money} to formatNumber({money::%sender's uuid%})", + "\t\tsend \"Your balance: %{_money}%\" to sender") + .since("INSERT VERSION"); } } diff --git a/src/test/skript/tests/regressions/7166-formatted numbers.sk b/src/test/skript/tests/regressions/7166-formatted numbers.sk new file mode 100644 index 00000000000..9bbba8a3da8 --- /dev/null +++ b/src/test/skript/tests/regressions/7166-formatted numbers.sk @@ -0,0 +1,26 @@ +test "formatted numbers function": + assert formatNumber(123456789) is "123,456,789" with "default number format failed ##1" + assert formatNumber(1234567) is "1,234,567" with "default number format failed ##2" + assert formatNumber(123.456) is "123.46" with "default number format failed ##3" + + assert formatNumber(12345678, "##,##.00") is "12,34,56,78.00" with "custom number format failed ##1" + assert formatNumber(12345678, "####,####") is "1234,5678" with "custom number format failed ##2" + assert formatNumber(123456.789, "$###,###.##") is "$123,456.79" with "custom number format failed ##3" + + assert formatNumber(12345678, "##.,##") is not set with "invalid number format returns a value" + assert formatNumber(123.45678, "##.,##") is not set with "invalid number format returns a value" + + set {_n} to "a" parsed as number + assert formatNumber({_n}) is not set with "invalid number returns a value" + assert formatNumber({_n}, "##,##") is not set with "invalid number with format returns a value" + assert formatNumber({_n}, "##.,##") is not set with "invalid number with invalid format returns a value" + + set {_n} to NaN value + assert formatNumber({_n}) is "NaN" with "NaN doesn't return a value" + assert formatNumber({_n}, "##,##") is "NaN" with "NaN with format doesn't return a value" + assert formatNumber({_n}, "##.,##") is not set with "NaN with invalid format returns a value" + + set {_n} to infinity value + assert formatNumber({_n}) is "∞" with "infinity doesn't return a value" + assert formatNumber({_n}, "##,##") is "∞" with "infinity with format doesn't return a value" + assert formatNumber({_n}, "##.,##") is not set with "infinity with invalid format returns a value" From 8c91cc87bb773919254f9a7c9cee2d47ced8b748 Mon Sep 17 00:00:00 2001 From: Eren <67760502+erenkarakal@users.noreply.github.com> Date: Thu, 31 Oct 2024 20:26:59 +0300 Subject: [PATCH 02/18] Make KeyValueEntryData validation not case-sensitive (#7143) * fix entry casing * lowercase seperator as well Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> * use regionMatches instead * use regionMatches instead * remove import --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> --- .../org/skriptlang/skript/lang/entry/KeyValueEntryData.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/skriptlang/skript/lang/entry/KeyValueEntryData.java b/src/main/java/org/skriptlang/skript/lang/entry/KeyValueEntryData.java index 6953f53d5e2..57c157a9c01 100644 --- a/src/main/java/org/skriptlang/skript/lang/entry/KeyValueEntryData.java +++ b/src/main/java/org/skriptlang/skript/lang/entry/KeyValueEntryData.java @@ -83,7 +83,8 @@ public boolean canCreateWith(Node node) { if (key == null) return false; key = ScriptLoader.replaceOptions(key); - return key.startsWith(getKey() + getSeparator()); + String prefix = getKey() + getSeparator(); + return key.regionMatches(true, 0, prefix, 0, prefix.length()); } } From 5512583e0c3ead918afe0cec623e89041aeb1645 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Thu, 31 Oct 2024 19:09:54 +0100 Subject: [PATCH 03/18] Fix leather horse armor being unequipable (#7141) * init commit * oops * update * update ver * fix paper * hmm * yeah ok * oops * biggera oops * dont run tests on 1.20.6 * requested changes * tabs --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/effects/EffEquip.java | 75 ++++++++----------- .../7140-leather horse armor unequipable.sk | 12 +++ 2 files changed, 42 insertions(+), 45 deletions(-) create mode 100644 src/test/skript/tests/regressions/7140-leather horse armor unequipable.sk diff --git a/src/main/java/ch/njol/skript/effects/EffEquip.java b/src/main/java/ch/njol/skript/effects/EffEquip.java index 8d818170062..74ca1572497 100644 --- a/src/main/java/ch/njol/skript/effects/EffEquip.java +++ b/src/main/java/ch/njol/skript/effects/EffEquip.java @@ -1,42 +1,7 @@ -/** - * 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.effects; -import ch.njol.skript.aliases.ItemData; -import org.bukkit.Material; -import org.bukkit.Tag; -import org.bukkit.entity.AbstractHorse; -import org.bukkit.entity.ChestedHorse; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Llama; -import org.bukkit.entity.Pig; -import org.bukkit.entity.Player; -import org.bukkit.entity.Steerable; -import org.bukkit.event.Event; -import org.bukkit.inventory.EntityEquipment; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.LlamaInventory; -import org.jetbrains.annotations.Nullable; - import ch.njol.skript.Skript; -import ch.njol.skript.aliases.Aliases; +import ch.njol.skript.aliases.ItemData; import ch.njol.skript.aliases.ItemType; import ch.njol.skript.bukkitutil.PlayerUtils; import ch.njol.skript.doc.Description; @@ -47,26 +12,47 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import org.bukkit.Material; +import org.bukkit.Tag; +import org.bukkit.entity.*; +import org.bukkit.event.Event; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.LlamaInventory; +import org.jetbrains.annotations.Nullable; @Name("Equip") -@Description("Equips or unequips an entity with some given armor. This will replace any armor that the entity is wearing.") +@Description( + "Equips or unequips an entity with some given armor. " + + "This will replace any armor that the entity is wearing." +) @Examples({ - "equip player with diamond helmet", - "equip player with all diamond armor", - "unequip diamond chestplate from player", - "unequip all armor from player", - "unequip player's armor" + "equip player with diamond helmet", + "equip player with all diamond armor", + "unequip diamond chestplate from player", + "unequip all armor from player", + "unequip player's armor" }) @Since("1.0, 2.7 (multiple entities, unequip)") public class EffEquip extends Effect { + private static final ItemType HORSE_ARMOR; + static { + if (Skript.isRunningMinecraft(1, 14)) { + HORSE_ARMOR = new ItemType(Material.IRON_HORSE_ARMOR, Material.GOLDEN_HORSE_ARMOR, + Material.DIAMOND_HORSE_ARMOR, Material.LEATHER_HORSE_ARMOR); + } else { + HORSE_ARMOR = new ItemType(Material.IRON_HORSE_ARMOR, Material.GOLDEN_HORSE_ARMOR, + Material.DIAMOND_HORSE_ARMOR); + } + Skript.registerEffect(EffEquip.class, "equip [%livingentities%] with %itemtypes%", "make %livingentities% wear %itemtypes%", "unequip %itemtypes% [from %livingentities%]", - "unequip %livingentities%'[s] (armor|equipment)" - ); + "unequip %livingentities%'[s] (armo[u]r|equipment)"); } @SuppressWarnings("NotNullFieldNotInitialized") @@ -99,7 +85,6 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye private static ItemType LEGGINGS; private static ItemType BOOTS; private static ItemType CARPET; - private static final ItemType HORSE_ARMOR = new ItemType(Material.IRON_HORSE_ARMOR, Material.GOLDEN_HORSE_ARMOR, Material.DIAMOND_HORSE_ARMOR); private static final ItemType SADDLE = new ItemType(Material.SADDLE); private static final ItemType CHEST = new ItemType(Material.CHEST); diff --git a/src/test/skript/tests/regressions/7140-leather horse armor unequipable.sk b/src/test/skript/tests/regressions/7140-leather horse armor unequipable.sk new file mode 100644 index 00000000000..fbfae46cc9f --- /dev/null +++ b/src/test/skript/tests/regressions/7140-leather horse armor unequipable.sk @@ -0,0 +1,12 @@ +test "leather horse armor unequipable" when running minecraft "1.14": + if minecraft version is "1.20.6": + stop # horse armor equipping is broken on Paper 1.20.6. see https://github.com/PaperMC/Paper/pull/11139 + + spawn horse at spawn of world "world": + set {_h} to entity + + equip {_h} with leather horse armor + + assert {_h} has leather horse armor with "horse doesn't have horse armor" + + delete entity within {_h} From b60d8ed8aa4c7836f5f317f194c08ef9cb9e6599 Mon Sep 17 00:00:00 2001 From: SirSmurfy2 <82696841+TheAbsolutionism@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:11:08 -0400 Subject: [PATCH 04/18] On Click BlockData (#7130) * Starter Commit * Remove License * Requested Changes * Requested Changes * Error Messages Change * Changes + Revert * Tab Fixes + Line Splits * Requested Changes --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/events/EvtClick.java | 137 ++++++++---------- 1 file changed, 62 insertions(+), 75 deletions(-) diff --git a/src/main/java/ch/njol/skript/events/EvtClick.java b/src/main/java/ch/njol/skript/events/EvtClick.java index 5d7e9137bae..ed455173889 100644 --- a/src/main/java/ch/njol/skript/events/EvtClick.java +++ b/src/main/java/ch/njol/skript/events/EvtClick.java @@ -1,24 +1,8 @@ -/** - * 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.util.Predicate; import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; import org.bukkit.entity.Vehicle; @@ -55,15 +39,15 @@ public class EvtClick extends SkriptEvent { /** * Tracks PlayerInteractEvents to deduplicate them. */ - public static final ClickEventTracker interactTracker = new ClickEventTracker(Skript.getInstance()); + public final static ClickEventTracker interactTracker = new ClickEventTracker(Skript.getInstance()); static { Class[] eventTypes = CollectionUtils.array( PlayerInteractEvent.class, PlayerInteractEntityEvent.class, PlayerInteractAtEntityEvent.class ); Skript.registerEvent("Click", EvtClick.class, eventTypes, - "[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] [on %-entitydata/itemtype%] [(with|using|holding) %-itemtype%]", - "[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] (with|using|holding) %itemtype% on %entitydata/itemtype%") + "[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] [on %-entitydata/itemtype/blockdata%] [(with|using|holding) %-itemtype%]", + "[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] (with|using|holding) %itemtype% on %entitydata/itemtype/blockdata%") .description("Called when a user clicks on a block, an entity or air with or without an item in their hand.", "Please note that rightclick events with an empty hand while not looking at a block are not sent to the server, so there's no way to detect them.", "Also note that a leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'damage' event.") @@ -71,21 +55,21 @@ public class EvtClick extends SkriptEvent { "on rightclick holding a fishing rod:", "on leftclick on a stone or obsidian:", "on rightclick on a creeper:", - "on click with a sword:") - .since("1.0"); + "on click with a sword:", + "on click on chest[facing=north]:", + "on click on campfire[lit=true]:") + .since("1.0, INSERT VERSION (blockdata)"); } /** * Only trigger when one of these is interacted with. */ - @Nullable - private Literal type; + private @Nullable Literal type; /** - * Only trigger when then item player clicks with is one of these. + * Only trigger when the item that the player clicks with is one of these. */ - @Nullable - private Literal tools; + private @Nullable Literal tools; /** * Click types to trigger. @@ -97,18 +81,18 @@ public class EvtClick extends SkriptEvent { public boolean init(Literal[] args, int matchedPattern, ParseResult parseResult) { click = parseResult.mark == 0 ? ANY : parseResult.mark; type = args[matchedPattern]; - if (type != null && !ItemType.class.isAssignableFrom(type.getReturnType())) { + if (type != null && !ItemType.class.isAssignableFrom(type.getReturnType()) && !BlockData.class.isAssignableFrom(type.getReturnType())) { Literal> entitydata = (Literal>) type; if (click == LEFT) { if (Vehicle.class.isAssignableFrom(entitydata.getSingle().getType())) { - Skript.error("A leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'vehicle damage' event."); + Skript.error("A leftclick on a vehicle entity is an attack and thus not covered by the 'click' event, but the 'vehicle damage' event."); } else { Skript.error("A leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'damage' event."); } return false; } else if (click == ANY) { if (Vehicle.class.isAssignableFrom(entitydata.getSingle().getType())) { - Skript.error("A leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'vehicle damage' event. " + + Skript.error("A leftclick on a vehicle entity is an attack and thus not covered by the 'click' event, but the 'vehicle damage' event. " + "Change this event to a rightclick to fix this warning message."); } else { Skript.error("A leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'damage' event. " + @@ -125,12 +109,11 @@ public boolean check(Event event) { Block block; Entity entity; - if (event instanceof PlayerInteractEntityEvent) { - PlayerInteractEntityEvent clickEvent = ((PlayerInteractEntityEvent) event); - Entity clicked = clickEvent.getRightClicked(); + if (event instanceof PlayerInteractEntityEvent interactEntityEvent) { + Entity clicked = interactEntityEvent.getRightClicked(); // Usually, don't handle these events - if (clickEvent instanceof PlayerInteractAtEntityEvent) { + if (interactEntityEvent instanceof PlayerInteractAtEntityEvent) { // But armor stands are an exception // Later, there may be more exceptions... if (!(clicked instanceof ArmorStand)) @@ -142,73 +125,72 @@ public boolean check(Event event) { // PlayerInteractAtEntityEvent called only once for armor stands if (!(event instanceof PlayerInteractAtEntityEvent)) { - if (!interactTracker.checkEvent(clickEvent.getPlayer(), clickEvent, clickEvent.getHand())) { + if (!interactTracker.checkEvent(interactEntityEvent.getPlayer(), interactEntityEvent, interactEntityEvent.getHand())) { return false; // Not first event this tick } } entity = clicked; block = null; - } else if (event instanceof PlayerInteractEvent) { - PlayerInteractEvent clickEvent = ((PlayerInteractEvent) event); - + } else if (event instanceof PlayerInteractEvent interactEvent) { // Figure out click type, filter non-click events - Action a = clickEvent.getAction(); + Action action = interactEvent.getAction(); int click; - switch (a) { - case LEFT_CLICK_AIR: - case LEFT_CLICK_BLOCK: - click = LEFT; - break; - case RIGHT_CLICK_AIR: - case RIGHT_CLICK_BLOCK: - click = RIGHT; - break; - case PHYSICAL: // Not a click event - default: + switch (action) { + case LEFT_CLICK_AIR, LEFT_CLICK_BLOCK -> click = LEFT; + case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> click = RIGHT; + default -> { return false; + } } if ((this.click & click) == 0) return false; // We don't want to handle this kind of events - EquipmentSlot hand = clickEvent.getHand(); + EquipmentSlot hand = interactEvent.getHand(); assert hand != null; // Not PHYSICAL interaction - if (!interactTracker.checkEvent(clickEvent.getPlayer(), clickEvent, hand)) { + if (!interactTracker.checkEvent(interactEvent.getPlayer(), interactEvent, hand)) { return false; // Not first event this tick } - block = clickEvent.getClickedBlock(); + block = interactEvent.getClickedBlock(); entity = null; } else { assert false; return false; } - - if (tools != null && !tools.check(event, new Checker() { - @Override - public boolean check(final ItemType t) { - if (event instanceof PlayerInteractEvent) { - return t.isOfType(((PlayerInteractEvent) event).getItem()); - } else { // PlayerInteractEntityEvent doesn't have item associated with it - PlayerInventory invi = ((PlayerInteractEntityEvent) event).getPlayer().getInventory(); - ItemStack item = ((PlayerInteractEntityEvent) event).getHand() == EquipmentSlot.HAND - ? invi.getItemInMainHand() : invi.getItemInOffHand(); - return t.isOfType(item); - } + + Checker checker = itemType -> { + if (event instanceof PlayerInteractEvent interactEvent) { + return itemType.isOfType(interactEvent.getItem()); + } else { + PlayerInventory invi = ((PlayerInteractEntityEvent) event).getPlayer().getInventory(); + ItemStack item = ((PlayerInteractEntityEvent) event).getHand() == EquipmentSlot.HAND + ? invi.getItemInMainHand() : invi.getItemInOffHand(); + return itemType.isOfType(item); } - })) { + }; + + if (tools != null && !tools.check(event, checker)) return false; - } - + if (type != null) { + BlockData blockDataCheck = block != null ? block.getBlockData() : null; return type.check(event, new Checker() { @Override - public boolean check(final Object o) { + public boolean check(Object object) { if (entity != null) { - return o instanceof EntityData ? ((EntityData) o).isInstance(entity) : Relation.EQUAL.isImpliedBy(DefaultComparators.entityItemComparator.compare(EntityData.fromEntity(entity), (ItemType) o)); - } else { - return o instanceof EntityData ? false : ((ItemType) o).isOfType(block); + if (object instanceof EntityData entityData) { + return entityData.isInstance(entity); + } else { + Relation compare = DefaultComparators.entityItemComparator.compare(EntityData.fromEntity(entity), (ItemType) object); + return Relation.EQUAL.isImpliedBy(compare); + } + } else if (object instanceof ItemType itemType) { + return itemType.isOfType(block); + } else if (blockDataCheck != null && object instanceof BlockData blockData) { + return blockDataCheck.matches(blockData); } + return false; } }); } @@ -216,8 +198,13 @@ public boolean check(final Object o) { } @Override - public String toString(@Nullable Event e, boolean debug) { - return (click == LEFT ? "left" : click == RIGHT ? "right" : "") + "click" + (type != null ? " on " + type.toString(e, debug) : "") + (tools != null ? " holding " + tools.toString(e, debug) : ""); + public String toString(@Nullable Event event, boolean debug) { + return switch (click) { + case LEFT -> "left"; + case RIGHT -> "right"; + default -> ""; + } + "click" + (type != null ? " on " + type.toString(event, debug) : "") + + (tools != null ? " holding " + tools.toString(event, debug) : ""); } } From 6c23496246d774c36edc4b06577e2f30e719820d Mon Sep 17 00:00:00 2001 From: XPYEX <50171612+0XPYEX0@users.noreply.github.com> Date: Fri, 1 Nov 2024 02:21:07 +0800 Subject: [PATCH 05/18] Mod Material support, for Hybrid Server (#7126) * fix: Mod items support, for Hybrid Server * style: style * perf: rename function name and aliases name * perf: notes * perf: not a break change * perf: the @Nullable annotation * docs: javadoc * style: to upper cases * perf: resolved mentioned * style: style * perf: resolved mentioned * feat: the expression: from * fix: wrong space * fix: ..s * perf: add warnings * style: just sort codes, using IDEA * perf: resolved mentioned --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/aliases/Aliases.java | 233 ++++++------- .../njol/skript/aliases/AliasesProvider.java | 330 ++++++++---------- .../njol/skript/bukkitutil/BukkitUnsafe.java | 88 ++--- 3 files changed, 314 insertions(+), 337 deletions(-) diff --git a/src/main/java/ch/njol/skript/aliases/Aliases.java b/src/main/java/ch/njol/skript/aliases/Aliases.java index 3e67971698a..b92a4196f84 100644 --- a/src/main/java/ch/njol/skript/aliases/Aliases.java +++ b/src/main/java/ch/njol/skript/aliases/Aliases.java @@ -1,21 +1,3 @@ -/** - * 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.aliases; import ch.njol.skript.Skript; @@ -25,8 +7,6 @@ import ch.njol.skript.config.Node; import ch.njol.skript.config.SectionNode; import ch.njol.skript.entity.EntityData; -import org.bukkit.entity.EntityType; -import org.skriptlang.skript.lang.script.Script; import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.localization.ArgsMessage; import ch.njol.skript.localization.Language; @@ -37,11 +17,6 @@ import ch.njol.skript.util.EnchantmentType; import ch.njol.skript.util.Utils; import ch.njol.skript.util.Version; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.jetbrains.annotations.Nullable; - import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; @@ -56,14 +31,53 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.script.Script; public abstract class Aliases { - static final boolean USING_ITEM_COMPONENTS = Skript.isRunningMinecraft(1, 20, 5); private static final AliasesProvider provider = createProvider(10000, null); private static final AliasesParser parser = createParser(provider); - + // this is not an alias! + private final static ItemType everything = new ItemType(); + private final static Message m_empty_string = new Message("aliases.empty string"); + private final static ArgsMessage m_invalid_item_type = new ArgsMessage("aliases.invalid item type"); + private final static Message m_outside_section = new Message("aliases.outside section"); + private final static RegexMessage p_any = new RegexMessage("aliases.any", "", " (.+)", Pattern.CASE_INSENSITIVE); + private final static RegexMessage p_every = new RegexMessage("aliases.every", "", " (.+)", Pattern.CASE_INSENSITIVE); + private final static RegexMessage p_of_every = new RegexMessage("aliases.of every", "(\\d+) ", " (.+)", Pattern.CASE_INSENSITIVE); + private final static RegexMessage p_of = new RegexMessage("aliases.of", "(\\d+) (?:", " )?(.+)", Pattern.CASE_INSENSITIVE); + + /** + * Go through these whenever aliases are reloaded, and update them. + */ + private static final Map trackedTypes = new HashMap<>(); + + /** + * If user had an obscure config option set, don't crash due to missing + * Java item types. + */ + private static final boolean noHardExceptions = SkriptConfig.apiSoftExceptions.value(); + static String itemSingular = "item"; + static String itemPlural = "items"; + @Nullable + static String itemGender = null; + static String blockSingular = "block"; + static String blockPlural = "blocks"; + @Nullable + static String blockGender = null; + + static { + everything.setAll(true); + ItemData all = new ItemData(Material.AIR); + all.isAnything = true; + everything.add(all); + } + @Nullable private static ItemType getAlias_i(final String s) { // Check script aliases first @@ -71,10 +85,10 @@ private static ItemType getAlias_i(final String s) { if (aliases != null) { return aliases.provider.getAlias(s); // Delegates to global provider if needed } - + return provider.getAlias(s); } - + /** * Creates an aliases provider with Skript's default configuration. * @param expectedCount Expected alias count. @@ -84,14 +98,14 @@ private static ItemType getAlias_i(final String s) { private static AliasesProvider createProvider(int expectedCount, @Nullable AliasesProvider parent) { return new AliasesProvider(expectedCount, parent); } - + /** * Creates an aliases parser with Skript's default configuration. * @return Aliases parser. */ private static AliasesParser createParser(AliasesProvider provider) { AliasesParser parser = new AliasesParser(provider); - + // Register standard conditions parser.registerCondition("minecraft version", (str) -> { int orNewer = str.indexOf("or newer"); // For example: 1.12 or newer @@ -100,14 +114,14 @@ private static AliasesParser createParser(AliasesProvider provider) { Version ver = new Version(str.substring(0, orNewer - 1)); return Skript.getMinecraftVersion().compareTo(ver) >= 0; } - + int orOlder = str.indexOf("or older"); // For example: 1.11 or older if (orOlder != -1) { @SuppressWarnings("null") Version ver = new Version(str.substring(0, orOlder - 1)); return Skript.getMinecraftVersion().compareTo(ver) <= 0; } - + int to = str.indexOf("to"); // For example: 1.11 to 1.12 if (to != -1) { @SuppressWarnings("null") @@ -117,39 +131,17 @@ private static AliasesParser createParser(AliasesProvider provider) { Version current = Skript.getMinecraftVersion(); return current.compareTo(first) >= 0 && current.compareTo(second) <= 0; } - + return Skript.getMinecraftVersion().equals(new Version(str)); }); - + return parser; } - static String itemSingular = "item"; - static String itemPlural = "items"; - @Nullable - static String itemGender = null; - static String blockSingular = "block"; - static String blockPlural = "blocks"; - @Nullable - static String blockGender = null; - - // this is not an alias! - private final static ItemType everything = new ItemType(); - static { - everything.setAll(true); - ItemData all = new ItemData(Material.AIR); - all.isAnything = true; - everything.add(all); - } - - private final static Message m_empty_string = new Message("aliases.empty string"); - private final static ArgsMessage m_invalid_item_type = new ArgsMessage("aliases.invalid item type"); - private final static Message m_outside_section = new Message("aliases.outside section"); - /** * Concatenates parts of an alias's name. This currently 'lowercases' the first character of any part if there's no space in front of it. It also replaces double spaces with a * single one and trims the resulting string. - * + * * @param parts */ static String concatenate(final String... parts) { @@ -171,7 +163,7 @@ static String concatenate(final String... parts) { } return "" + b.toString().replace(" ", " ").trim(); } - + @Nullable private static MaterialName getMaterialNameData(ItemData type) { // Check script aliases first @@ -179,11 +171,11 @@ private static MaterialName getMaterialNameData(ItemData type) { if (aliases != null) { return aliases.provider.getMaterialName(type); } - + // Then global aliases return provider.getMaterialName(type); } - + public static String getMaterialName(ItemData type, boolean plural) { MaterialName name = getMaterialNameData(type); if (name == null) { @@ -191,7 +183,7 @@ public static String getMaterialName(ItemData type, boolean plural) { } return name.toString(plural); } - + /** * @return The item's gender or -1 if no name is found */ @@ -201,10 +193,10 @@ public static int getGender(ItemData item) { return n.gender; return -1; } - + /** * Parses an ItemType to be used as an alias, i.e. it doesn't parse 'all'/'every' and the amount. - * + * * @param s mixed case string * @return A new ItemType representing the given value */ @@ -216,28 +208,23 @@ public static ItemType parseAlias(final String s) { } if (s.equals("*")) return everything; - + final ItemType t = new ItemType(); - + final String[] types = s.split("\\s*,\\s*"); for (final String type : types) { if (type == null || parseType(type, t, true) == null) return null; } - + return t; } - - private final static RegexMessage p_any = new RegexMessage("aliases.any", "", " (.+)", Pattern.CASE_INSENSITIVE); - private final static RegexMessage p_every = new RegexMessage("aliases.every", "", " (.+)", Pattern.CASE_INSENSITIVE); - private final static RegexMessage p_of_every = new RegexMessage("aliases.of every", "(\\d+) ", " (.+)", Pattern.CASE_INSENSITIVE); - private final static RegexMessage p_of = new RegexMessage("aliases.of", "(\\d+) (?:", " )?(.+)", Pattern.CASE_INSENSITIVE); - + /** * Parses an ItemType. *

* Prints errors. - * + * * @param s * @return The parsed ItemType or null if the input is invalid. */ @@ -245,10 +232,10 @@ public static ItemType parseAlias(final String s) { public static ItemType parseItemType(String s) { if (s.isEmpty()) return null; - s = "" + s.trim(); - + s = s.trim(); + final ItemType t = new ItemType(); - + Matcher m; if ((m = p_of_every.matcher(s)).matches()) { t.setAmount(Utils.parseInt("" + m.group(1))); @@ -266,11 +253,12 @@ public static ItemType parseItemType(String s) { if (s.length() != l) // had indefinite article t.setAmount(1); } - + String lc = s.toLowerCase(Locale.ENGLISH); String of = Language.getSpaced("of").toLowerCase(); int c = -1; - outer: while ((c = lc.indexOf(of, c + 1)) != -1) { + outer: + while ((c = lc.indexOf(of, c + 1)) != -1) { ItemType t2 = t.clone(); try (BlockingLogHandler ignored = new BlockingLogHandler().start()) { if (parseType("" + s.substring(0, c), t2, false) == null) @@ -287,19 +275,19 @@ public static ItemType parseItemType(String s) { } return t2; } - + if (parseType(s, t, false) == null) return null; - + if (t.numTypes() == 0) return null; - + return t; } - + /** * Prints errors. - * + * * @param s The string holding the type, can be either a number or an alias, plus an optional data part. Case does not matter. * @param t The ItemType to add the parsed ItemData(s) to (i.e. this ItemType will be modified) * @param isAlias Whether this type is parsed for an alias. @@ -323,10 +311,10 @@ private static ItemType parseType(final String s, final ItemType t, final boolea Skript.error(m_invalid_item_type.toString(s)); return null; } - + /** * Gets an alias from the aliases defined in the config. - * + * * @param s The alias to get, case does not matter * @return A copy of the ItemType represented by the given alias or null if no such alias exists. */ @@ -372,14 +360,14 @@ private static ItemType getAlias(final String s) { } return null; } - + /** * Clears aliases. Make sure to load them after this! */ public static void clear() { provider.clearAliases(); } - + /** * Loads aliases from Skript's standard locations. * Exceptions will be logged, but not thrown. @@ -400,19 +388,41 @@ public static void load() { private static void loadMissingAliases() { if (!Skript.methodExists(Material.class, "getKey")) return; + boolean modItemRegistered = false; for (Material material : Material.values()) { if (!material.isLegacy() && !provider.hasAliasForMaterial(material)) { NamespacedKey key = material.getKey(); - String name = key.getKey().replace("_", " "); - parser.loadAlias(name + "¦s", key.toString()); + // mod:an_item -> (mod's an item) | (an item from mod) + // minecraft:dirt -> dirt + if (NamespacedKey.MINECRAFT.equals(key.getNamespace())) { + parser.loadAlias(key.getKey().replace("_", " ") + "¦s", key.toString()); + } else { + if (!modItemRegistered) modItemRegistered = true; + parser.loadAlias((key.getNamespace() + "'s " + key.getKey() + "¦s").replace("_", " "), key.toString()); + parser.loadAlias((key.getKey() + "¦s from " + key.getNamespace()).replace("_", " "), key.toString()); + } Skript.debug(ChatColor.YELLOW + "Creating temporary alias for: " + key); } } + + if (modItemRegistered) { + Skript.warning("=============================================================="); + Skript.warning("Some materials were found that seem to be modded."); + Skript.warning("An item that has the id 'mod:item' can be used as 'mod's item' or 'item from mod'."); + Skript.warning("WARNING: Skript does not officially support any modded servers."); + Skript.warning("Any issues you encounter related to modded items will be your responsibility to fix."); + Skript.warning("The server will keep loading after 5 seconds."); + Skript.warning("=============================================================="); + try { + Thread.sleep(5000L); + } catch (InterruptedException ignored) { + } + } } - + private static void loadInternal() throws IOException { Path dataFolder = Skript.getInstance().getDataFolder().toPath(); - + // Load aliases.zip OR aliases from jar (never both) Path zipPath = dataFolder.resolve("aliases-english.zip"); if (!SkriptConfig.loadDefaultAliases.value()) { @@ -436,9 +446,9 @@ private static void loadInternal() throws IOException { } catch (URISyntaxException e) { assert false; } - + } - + // Load everything from aliases folder (user aliases) Path aliasesFolder = dataFolder.resolve("aliases"); if (Files.exists(aliasesFolder)) { @@ -448,19 +458,19 @@ private static void loadInternal() throws IOException { // generate aliases from item names for any missing items loadMissingAliases(); - + // Update tracked item types for (Map.Entry entry : trackedTypes.entrySet()) { @SuppressWarnings("null") // No null keys in this map ItemType type = parseItemType(entry.getKey()); if (type == null) Skript.warning("Alias '" + entry.getKey() + "' is required by Skript, but does not exist anymore. " - + "Make sure to fix this before restarting the server."); + + "Make sure to fix this before restarting the server."); else entry.getValue().setTo(type); } } - + /** * Loads aliases from given directory. * @param dir Directory of aliases. @@ -484,7 +494,7 @@ else if (name.endsWith(".sk")) throw e.getCause(); } } - + /** * Loads aliases from given path. * @param f Path of alias file. @@ -494,7 +504,7 @@ public static void load(Path f) throws IOException { Config config = new Config(f, false, false, "="); load(config); } - + /** * Loads aliases from configuration. * @param config Configuration containing the aliases. @@ -505,7 +515,7 @@ public static void load(Config config) { Skript.error(m_outside_section.toString()); continue; } - + parser.load((SectionNode) n); } } @@ -523,7 +533,7 @@ public static String getMinecraftId(ItemData data) { } return provider.getMinecraftId(data); } - + /** * Gets an entity type related to given item. For example, an armor stand * item is related with armor stand entity. @@ -538,22 +548,11 @@ public static EntityData getRelatedEntity(ItemData data) { } return provider.getRelatedEntity(data); } - - /** - * Go through these whenever aliases are reloaded, and update them. - */ - private static final Map trackedTypes = new HashMap<>(); - - /** - * If user had an obscure config option set, don't crash due to missing - * Java item types. - */ - private static final boolean noHardExceptions = SkriptConfig.apiSoftExceptions.value(); - + /** * Gets an item type that matches the given name. * If it doesn't exist, an exception is thrown instead. - * + * *

Item types provided by this method are updated when aliases are * reloaded. However, this also means they are tracked by aliases system * and NOT necessarily garbage-collected. @@ -580,7 +579,7 @@ public static ItemType javaItemType(String name) { trackedTypes.put(name, type); return type; } - + /** * Creates an aliases provider to be used by given addon. It can be used to * register aliases and variations to be used in scripts. @@ -591,11 +590,11 @@ public static AliasesProvider getAddonProvider(@Nullable SkriptAddon addon) { if (addon == null) { throw new IllegalArgumentException("addon needed"); } - + // TODO in future, maybe record and allow unloading addon-provided aliases? return provider; // For now, just allow loading aliases easily } - + /** * Creates script aliases for the provided Script. * @return Script aliases that are ready to be added to. diff --git a/src/main/java/ch/njol/skript/aliases/AliasesProvider.java b/src/main/java/ch/njol/skript/aliases/AliasesProvider.java index 06ae5317f78..1cbaa0c9cfd 100644 --- a/src/main/java/ch/njol/skript/aliases/AliasesProvider.java +++ b/src/main/java/ch/njol/skript/aliases/AliasesProvider.java @@ -1,45 +1,24 @@ -/** - * 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.aliases; +import ch.njol.skript.Skript; +import ch.njol.skript.bukkitutil.BukkitUnsafe; +import ch.njol.skript.bukkitutil.ItemUtils; +import ch.njol.skript.bukkitutil.block.BlockCompat; +import ch.njol.skript.bukkitutil.block.BlockValues; +import ch.njol.skript.entity.EntityData; +import com.google.gson.Gson; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Set; import java.util.List; import java.util.Map; - -import ch.njol.skript.Skript; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import java.util.Set; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; -import com.google.gson.Gson; - -import ch.njol.skript.bukkitutil.BukkitUnsafe; -import ch.njol.skript.bukkitutil.ItemUtils; -import ch.njol.skript.bukkitutil.block.BlockCompat; -import ch.njol.skript.bukkitutil.block.BlockValues; -import ch.njol.skript.entity.EntityData; - /** * Provides aliases on Bukkit/Spigot platform. */ @@ -47,14 +26,14 @@ public class AliasesProvider { // not supported on Spigot versions older than 1.18 private static final boolean FASTER_SET_SUPPORTED = Skript.classExists("it.unimi.dsi.fastutil.objects.ObjectOpenHashSet"); - + /** * When an alias is not found, it will requested from this provider. * Null when this is global aliases provider. */ @Nullable private final AliasesProvider parent; - + /** * All aliases that are currently loaded by this provider. */ @@ -64,112 +43,21 @@ public class AliasesProvider { * All materials that are currently loaded by this provider. */ private final Set materials; - + /** * Tags are in JSON format. We may need GSON when merging tags * (which might be done if variations are used). */ private final Gson gson; - - /** - * Represents a variation of material. It could, for example, define one - * more tag or change base id, but keep tag intact. - */ - public static class Variation { - - @Nullable - private final String id; - private final int insertPoint; - - private final Map tags; - private final Map states; - - public Variation(@Nullable String id, int insertPoint, Map tags, Map states) { - this.id = id; - this.insertPoint = insertPoint; - this.tags = tags; - this.states = states; - } - - @Nullable - public String getId() { - return id; - } - - public int getInsertPoint() { - return insertPoint; - } - - @Nullable - public String insertId(@Nullable String inserted) { - if (inserted == null) // Nothing to insert - return id; - inserted = inserted.substring(0, inserted.length() - 1); // Strip out - - if (id == null) // Inserting to nothing - return inserted; - - String id = this.id; - assert id != null; - if (insertPoint == -1) // No place where to insert - return inserted; - - // Insert given string to in middle of our id - String before = id.substring(0, insertPoint); - String after = id.substring(insertPoint + 1); - return before + inserted + after; - } - - public Map getTags() { - return tags; - } - - - public Map getBlockStates() { - return states; - } - - - public Variation merge(Variation other) { - // Merge tags and block states - Map mergedTags = new HashMap<>(other.tags); - mergedTags.putAll(tags); - Map mergedStates = new HashMap<>(other.states); - mergedStates.putAll(states); - - // Potentially merge ids - String id = insertId(other.id); - - return new Variation(id, -1, mergedTags, mergedStates); - } - } - - public static class VariationGroup { - - public final List keys; - - public final List values; - - public VariationGroup() { - this.keys = new ArrayList<>(); - this.values = new ArrayList<>(); - } - - public void put(String key, Variation value) { - keys.add(key); - values.add(value); - } - } - /** * Contains all variations. */ private final Map variations; - /** * Allows looking up aliases based on item datas created runtime. */ private final AliasesMap aliasesMap; - + /** * Constructs a new aliases provider with no data. */ @@ -184,20 +72,20 @@ public AliasesProvider(int expectedCount, @Nullable AliasesProvider parent) { } else { this.materials = new HashSet<>(); } - + this.gson = new Gson(); } - + /** * Uses GSON to parse Mojang's JSON format to a map. * @param raw Raw JSON. - * @return String,Object map. + * @return String, Object map. */ @SuppressWarnings({"null", "unchecked"}) public Map parseMojangson(String raw) { return (Map) gson.fromJson(raw, Object.class); } - + /** * Applies given tags to an item stack. * @param stack Item stack. @@ -213,10 +101,10 @@ public int applyTags(ItemStack stack, Map tags) { tags.remove("Damage"); flags |= ItemFlags.CHANGED_DURABILITY; } - + if (tags.isEmpty()) // No real tags to apply return flags; - + // Apply random tags using JSON if (Aliases.USING_ITEM_COMPONENTS) { String components = (String) tags.get("components"); // only components are supported for modifying a stack @@ -231,40 +119,10 @@ public int applyTags(ItemStack stack, Map tags) { BukkitUnsafe.modifyItemStack(stack, json); } flags |= ItemFlags.CHANGED_TAGS; - + return flags; } - - /** - * Name of an alias used by {@link #addAlias(AliasName, String, Map, Map)} - * for registration. - */ - public static class AliasName { - - /** - * Singular for of alias name. - */ - public final String singular; - - /** - * Plural form of alias name. - */ - public final String plural; - - /** - * Gender of alias name. - */ - public final int gender; - public AliasName(String singular, String plural, int gender) { - super(); - this.singular = singular; - this.plural = plural; - this.gender = gender; - } - - } - /** * Adds an alias to this provider. * @param name Name of alias without any patterns or variation blocks. @@ -290,19 +148,19 @@ public void addAlias(AliasName name, String id, @Nullable Map ta datas = typeOfId.getTypes(); } else { // ... but quite often, we just got Vanilla id // Prepare and modify ItemStack (using somewhat Unsafe methods) - Material material = BukkitUnsafe.getMaterialFromMinecraftId(id); + Material material = BukkitUnsafe.getMaterialFromNamespacedId(id); if (material == null) { // If server doesn't recognize id, do not proceed throw new InvalidMinecraftIdException(id); } materials.add(material); - + // Hacky: get related entity from block states String entityName = blockStates.remove("relatedEntity"); if (entityName != null) { related = EntityData.parse(entityName); } - + // Apply (NBT) tags to item stack ItemStack stack = null; int itemFlags = 0; @@ -312,14 +170,14 @@ public void addAlias(AliasName name, String id, @Nullable Map ta itemFlags = applyTags(stack, new HashMap<>(tags)); } } - + // Parse block state to block values BlockValues blockValues = BlockCompat.INSTANCE.createBlockValues(material, blockStates, stack, itemFlags); - + ItemData data = stack != null ? new ItemData(stack, blockValues) : new ItemData(material, blockValues); data.isAlias = true; data.itemFlags = itemFlags; - + // Deduplicate item data if this has been loaded before if (deduplicate) { AliasesMap.Match canonical = aliasesMap.exactMatch(data); @@ -329,10 +187,10 @@ public void addAlias(AliasName name, String id, @Nullable Map ta data = aliasData.getItem(); } } - + datas = Collections.singletonList(data); } - + // If this is first time we're defining an item, store additional data about it if (typeOfId == null) { ItemData data = datas.get(0); @@ -351,7 +209,8 @@ public void addAlias(AliasName name, String id, @Nullable Map ta aliases.put(name.plural, type); // Plural form type.addAll(datas); } else { // There is already an item type with this name, we need to *only* add new data - newDataLoop: for (ItemData newData : datas) { + newDataLoop: + for (ItemData newData : datas) { for (ItemData existingData : type.getTypes()) { if (newData == existingData || newData.matchAlias(existingData).isAtLeast(MatchQuality.EXACT)) // Don't add this data, the item type already contains it! continue newDataLoop; @@ -360,11 +219,11 @@ public void addAlias(AliasName name, String id, @Nullable Map ta } } } - + public void addVariationGroup(String name, VariationGroup group) { variations.put(name, group); } - + @Nullable public VariationGroup getVariationGroup(String name) { return variations.get(name); @@ -378,7 +237,7 @@ public ItemType getAlias(String alias) { } return item; } - + public AliasesMap.@Nullable AliasData getAliasData(ItemData item) { AliasesMap.AliasData data = aliasesMap.matchAlias(item).getData(); if (data == null && parent != null) { @@ -395,7 +254,7 @@ public String getMinecraftId(ItemData item) { } return null; } - + @Nullable public MaterialName getMaterialName(ItemData item) { AliasesMap.AliasData data = getAliasData(item); @@ -404,7 +263,7 @@ public MaterialName getMaterialName(ItemData item) { } return null; } - + @Nullable public EntityData getRelatedEntity(ItemData item) { AliasesMap.AliasData data = getAliasData(item); @@ -434,4 +293,123 @@ public boolean hasAliasForMaterial(Material material) { return materials.contains(material); } + /** + * Represents a variation of material. It could, for example, define one + * more tag or change base id, but keep tag intact. + */ + public static class Variation { + + @Nullable + private final String id; + private final int insertPoint; + + private final Map tags; + private final Map states; + + public Variation(@Nullable String id, int insertPoint, Map tags, Map states) { + this.id = id; + this.insertPoint = insertPoint; + this.tags = tags; + this.states = states; + } + + @Nullable + public String getId() { + return id; + } + + public int getInsertPoint() { + return insertPoint; + } + + @Nullable + public String insertId(@Nullable String inserted) { + if (inserted == null) // Nothing to insert + return id; + inserted = inserted.substring(0, inserted.length() - 1); // Strip out - + if (id == null) // Inserting to nothing + return inserted; + + String id = this.id; + assert id != null; + if (insertPoint == -1) // No place where to insert + return inserted; + + // Insert given string to in middle of our id + String before = id.substring(0, insertPoint); + String after = id.substring(insertPoint + 1); + return before + inserted + after; + } + + public Map getTags() { + return tags; + } + + + public Map getBlockStates() { + return states; + } + + + public Variation merge(Variation other) { + // Merge tags and block states + Map mergedTags = new HashMap<>(other.tags); + mergedTags.putAll(tags); + Map mergedStates = new HashMap<>(other.states); + mergedStates.putAll(states); + + // Potentially merge ids + String id = insertId(other.id); + + return new Variation(id, -1, mergedTags, mergedStates); + } + } + + public static class VariationGroup { + + public final List keys; + + public final List values; + + public VariationGroup() { + this.keys = new ArrayList<>(); + this.values = new ArrayList<>(); + } + + public void put(String key, Variation value) { + keys.add(key); + values.add(value); + } + } + + /** + * Name of an alias used by {@link #addAlias(AliasName, String, Map, Map)} + * for registration. + */ + public static class AliasName { + + /** + * Singular for of alias name. + */ + public final String singular; + + /** + * Plural form of alias name. + */ + public final String plural; + + /** + * Gender of alias name. + */ + public final int gender; + + public AliasName(String singular, String plural, int gender) { + super(); + this.singular = singular; + this.plural = plural; + this.gender = gender; + } + + } + } diff --git a/src/main/java/ch/njol/skript/bukkitutil/BukkitUnsafe.java b/src/main/java/ch/njol/skript/bukkitutil/BukkitUnsafe.java index f47a3f5d8d7..e7e255f1e72 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/BukkitUnsafe.java +++ b/src/main/java/ch/njol/skript/bukkitutil/BukkitUnsafe.java @@ -1,76 +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.bukkitutil; +import ch.njol.skript.Skript; +import ch.njol.util.EnumTypeAdapter; +import com.google.common.io.ByteStreams; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.io.InputStream; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; - import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.UnsafeValues; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; -import com.google.common.io.ByteStreams; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import ch.njol.util.EnumTypeAdapter; -import ch.njol.skript.Skript; -import ch.njol.skript.util.Version; - /** * Contains helpers for Bukkit's not so safe stuff. */ @SuppressWarnings("deprecation") public class BukkitUnsafe { - + /** * Bukkit's UnsafeValues allows us to do stuff that would otherwise * require NMS. It has existed for a long time, too, so 1.9 support is * not particularly hard to achieve. - * + * * UnsafeValues' existence and behavior is not guaranteed across future versions. */ @Nullable private static final UnsafeValues unsafe = Bukkit.getUnsafe(); + /** + * Maps pre 1.12 ids to materials for variable conversions. + */ + private static @Nullable Map idMappings; + static { if (unsafe == null) throw new Error("UnsafeValues are not available."); } /** - * Maps pre 1.12 ids to materials for variable conversions. + * Get a material from a minecraft id. + * + * @param id Namespaced ID with or without a namespace. IDs without a namespace will be treated + * as minecraft namespaced IDs. ('minecraft:dirt' and 'dirt' are equivalent.) + * @return The Material which the id represents, or null if no material can be matched. + * @deprecated Prefer {@link BukkitUnsafe#getMaterialFromNamespacedId(String)} for including modded item support */ - @Nullable - private static Map idMappings; + @Deprecated + public static @Nullable Material getMaterialFromMinecraftId(String id) { + return getMaterialFromNamespacedId(id); + } - @Nullable - public static Material getMaterialFromMinecraftId(String id) { - return Material.matchMaterial(id); + /** + * Get a material from a namespaced ID. + * For example, 'minecraft:iron_ingot' -> Material.IRON_INGOT; 'mod:an_item' -> Material.MOD_AN_ITEM + * + * @param id Namespaced ID with or without a namespace. IDs without a namespace will be treated + * as minecraft namespaced IDs. ('minecraft:dirt' and 'dirt' are equivalent.) + * @return The Material which the id represents, or null if no material can be matched. + */ + public static @Nullable Material getMaterialFromNamespacedId(String id) { + return Material.matchMaterial(id.toLowerCase().startsWith(NamespacedKey.MINECRAFT + ":") + ? id + : id.replace(":", "_") //For Hybrid Server + ); } public static void modifyItemStack(ItemStack stack, String arguments) { @@ -78,19 +77,20 @@ public static void modifyItemStack(ItemStack stack, String arguments) { throw new IllegalStateException("modifyItemStack could not be performed as UnsafeValues are not available."); unsafe.modifyItemStack(stack, arguments); } - + private static void initIdMappings() { try (InputStream is = Skript.getInstance().getResource("materials/ids.json")) { if (is == null) { throw new AssertionError("missing id mappings"); } String data = new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8); - - Type type = new TypeToken>(){}.getType(); + + Type type = new TypeToken>() { + }.getType(); Map rawMappings = new GsonBuilder(). - registerTypeAdapterFactory(EnumTypeAdapter.factory) - .create().fromJson(data, type); - + registerTypeAdapterFactory(EnumTypeAdapter.factory) + .create().fromJson(data, type); + // Process raw mappings Map parsed = new HashMap<>(rawMappings.size()); // Legacy material conversion API @@ -102,7 +102,7 @@ private static void initIdMappings() { throw new AssertionError(e); } } - + @Nullable public static Material getMaterialFromId(int id) { if (idMappings == null) { From d573ac7437e49a8c7c1d87a080959160cca18c9c Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Thu, 31 Oct 2024 19:36:08 +0100 Subject: [PATCH 06/18] Remove delay in tests (#7115) * hmm * hm --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Moderocky --- src/main/java/ch/njol/skript/Skript.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 0bd0a4877b7..5d25ebf6f78 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -667,9 +667,10 @@ protected void afterErrors() { debug("Early init done"); if (TestMode.ENABLED) { - Bukkit.getScheduler().runTaskLater(Skript.this, () -> info("Skript testing environment enabled, starting soon..."), 1); // Ignore late init (scripts, etc.) in test mode Bukkit.getScheduler().runTaskLater(Skript.this, () -> { + info("Skript testing environment enabled, starting..."); + // Delay is in Minecraft ticks. AtomicLong shutdownDelay = new AtomicLong(0); List> asyncTests = new ArrayList<>(); @@ -778,7 +779,7 @@ protected void afterErrors() { Bukkit.getServer().shutdown(); }, shutdownDelay.get()); }); - }, 100); + }, 5); } Skript.metrics = new Metrics(Skript.getInstance(), 722); // 722 is our bStats plugin ID From 13e00f622ed0ecdb48bb7943bd00b7c5d698c716 Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Thu, 31 Oct 2024 21:52:18 +0300 Subject: [PATCH 07/18] Implement ADD and REMOVE changers to ExprMetadata (#7113) * Implement ADD and REMOVE changers to the metadata value expression * Remove license comment and import ParseResult directly * Update src/main/java/ch/njol/skript/expressions/ExprMetadata.java Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * Requested Changes --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../njol/skript/expressions/ExprMetadata.java | 159 ++++++++++-------- .../syntaxes/expressions/ExprMetadata.sk | 24 +++ 2 files changed, 109 insertions(+), 74 deletions(-) create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprMetadata.sk diff --git a/src/main/java/ch/njol/skript/expressions/ExprMetadata.java b/src/main/java/ch/njol/skript/expressions/ExprMetadata.java index 7608b73dc83..d6e37fb226b 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprMetadata.java +++ b/src/main/java/ch/njol/skript/expressions/ExprMetadata.java @@ -1,138 +1,148 @@ -/** - * 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 java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.event.Event; -import org.bukkit.metadata.FixedMetadataValue; -import org.bukkit.metadata.MetadataValue; -import org.bukkit.metadata.Metadatable; -import org.jetbrains.annotations.Nullable; - import ch.njol.skript.Skript; -import ch.njol.skript.classes.Changer; +import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; -import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; -import org.skriptlang.skript.lang.converter.Converters; import ch.njol.skript.util.Utils; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; +import org.bukkit.event.Event; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.metadata.Metadatable; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; +import org.skriptlang.skript.lang.arithmetic.Arithmetics; +import org.skriptlang.skript.lang.arithmetic.Operation; +import org.skriptlang.skript.lang.arithmetic.OperationInfo; +import org.skriptlang.skript.lang.arithmetic.Operator; +import org.skriptlang.skript.lang.converter.Converters; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; @Name("Metadata") -@Description("Metadata is a way to store temporary data on entities, blocks and more that " + - "disappears after a server restart.") -@Examples({"set metadata value \"healer\" of player to true", - "broadcast \"%metadata value \"\"healer\"\" of player%\"", - "clear metadata value \"healer\" of player"}) -@Since("2.2-dev36") -@SuppressWarnings({"unchecked", "null"}) +@Description("Metadata is a way to store temporary data on entities, blocks and more that disappears after a server restart.") +@Examples({ + "set metadata value \"healer\" of player to true", + "broadcast \"%metadata value \"\"healer\"\" of player%\"", + "clear metadata value \"healer\" of player" +}) +@Since("2.2-dev36, INSERT VERSION (add, remove)") public class ExprMetadata extends SimpleExpression { static { + //noinspection unchecked Skript.registerExpression(ExprMetadata.class, Object.class, ExpressionType.PROPERTY, "metadata [(value|tag)[s]] %strings% of %metadataholders%", "%metadataholders%'[s] metadata [(value|tag)[s]] %string%" ); } - private ExprMetadata source; - @Nullable - private Expression values; - @Nullable - private Expression holders; - private Class[] types; - private Class superType; + private final ExprMetadata source; + private final Class[] types; + private final Class superType; + + private @UnknownNullability Expression keys; + private @UnknownNullability Expression holders; public ExprMetadata() { + //noinspection unchecked this(null, (Class) Object.class); } + @SafeVarargs private ExprMetadata(ExprMetadata source, Class... types) { this.source = source; if (source != null) { - this.values = source.values; + this.keys = source.keys; this.holders = source.holders; } this.types = types; + //noinspection unchecked this.superType = (Class) Utils.getSuperType(types); } @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { holders = (Expression) exprs[matchedPattern ^ 1]; - values = (Expression) exprs[matchedPattern]; + keys = (Expression) exprs[matchedPattern]; return true; } @Override - @Nullable - protected T[] get(Event e) { + protected T @Nullable [] get(Event event) { List values = new ArrayList<>(); - for (String value : this.values.getArray(e)) { - for (Metadatable holder : holders.getArray(e)) { - List metadata = holder.getMetadata(value); + String[] keys = this.keys.getArray(event); + for (Metadatable holder : holders.getArray(event)) { + for (String key : keys) { + List metadata = holder.getMetadata(key); if (!metadata.isEmpty()) values.add(metadata.get(metadata.size() - 1).value()); // adds the most recent metadata value } } try { return Converters.convert(values.toArray(), types, superType); - } catch (ClassCastException e1) { + } catch (ClassCastException exception) { + //noinspection unchecked return (T[]) Array.newInstance(superType, 0); } } @Override - @Nullable - public Class[] acceptChange(Changer.ChangeMode mode) { - if (mode == Changer.ChangeMode.DELETE || mode == Changer.ChangeMode.SET) - return CollectionUtils.array(Object.class); - return null; + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, ADD, REMOVE, DELETE -> CollectionUtils.array(Object.class); + default -> null; + }; } @Override - public void change(Event e, @Nullable Object[] delta, Changer.ChangeMode mode) { - for (String value : values.getArray(e)) { - for (Metadatable holder : holders.getArray(e)) { - switch (mode) { - case SET: - holder.setMetadata(value, new FixedMetadataValue(Skript.getInstance(), delta[0])); - break; - case DELETE: - holder.removeMetadata(value, Skript.getInstance()); - } + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + String[] keys = this.keys.getArray(event); + for (Metadatable holder : holders.getArray(event)) { + for (String key : keys) { + switch (mode) { + case SET -> holder.setMetadata(key, new FixedMetadataValue(Skript.getInstance(), delta[0])); + case ADD, REMOVE -> { + assert delta != null; + Operator operator = mode == ChangeMode.ADD ? Operator.ADDITION : Operator.SUBTRACTION; + List metadata = holder.getMetadata(key); + Object value = metadata.isEmpty() ? null : metadata.get(metadata.size() - 1).value(); + OperationInfo info; + if (value != null) { + info = Arithmetics.getOperationInfo(operator, value.getClass(), delta[0].getClass()); + if (info == null) + continue; + } else { + info = Arithmetics.getOperationInfo(operator, delta[0].getClass(), delta[0].getClass()); + if (info == null) + continue; + value = Arithmetics.getDefaultValue(info.getLeft()); + if (value == null) + continue; + } + //noinspection unchecked,rawtypes + Object newValue = ((Operation) info.getOperation()).calculate(value, delta[0]); + holder.setMetadata(key, new FixedMetadataValue(Skript.getInstance(), newValue)); + } + case DELETE -> holder.removeMetadata(key, Skript.getInstance()); + } } } } @Override public boolean isSingle() { - return holders.isSingle() && values.isSingle(); + return holders.isSingle() && keys.isSingle(); } @Override @@ -141,7 +151,8 @@ public Class getReturnType() { } @Override - public Expression getConvertedExpression(Class... to) { + @SafeVarargs + public final Expression getConvertedExpression(Class... to) { return new ExprMetadata<>(this, to); } @@ -151,8 +162,8 @@ public Expression getSource() { } @Override - public String toString(@Nullable Event e, boolean debug) { - return "metadata values " + values.toString(e, debug) + " of " + holders.toString(e, debug); + public String toString(@Nullable Event event, boolean debug) { + return "metadata values " + keys.toString(event, debug) + " of " + holders.toString(event, debug); } -} \ No newline at end of file +} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprMetadata.sk b/src/test/skript/tests/syntaxes/expressions/ExprMetadata.sk new file mode 100644 index 00000000000..20b052899c2 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprMetadata.sk @@ -0,0 +1,24 @@ +test "metadata value": + spawn armor stand at spawn of "world": + set metadata "number" of entity to 10 + assert metadata "number" of entity is 10 with "didn't set metadata value" + + add 5 to metadata "number" of entity + assert metadata "number" of entity is 15 with "didn't add to metadata value" + + remove 3 from metadata "number" of entity + assert metadata "number" of entity is 12 with "didn't remove from metadata value" + + add "not a number" to metadata "number" of entity + assert metadata "number" of entity is 12 with "added incompatible type to metadata" + + remove "not a number" from metadata "number" of entity + assert metadata "number" of entity is 12 with "removed incompatible type from metadata" + + add vector(1, 0, 1) to metadata "vector" of entity + assert metadata "vector" of entity is vector(1, 0, 1) with "didn't add to unset metadata value" + + delete metadata "number" and "vector" of entity + assert metadata "number" and "vector" of entity are not set with "didn't delete metadata value" + + delete entity From ead4ca448d3ae668b4e68ce7d5696eca7cb5c842 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Thu, 31 Oct 2024 20:13:34 +0100 Subject: [PATCH 08/18] Add breeding (#7110) * Update aliases I guess? * Revert "Update aliases I guess?" This reverts commit a437ca9ac96e0610c99a724c187920f12ebecf3c. * Adding Events :paperclip: Added EntityEnterLoveModeEvent :pig: Added EntityBreedEvent :sparkles: Added EventValues for these events * Breeding Event Expressions :family: Added family expression for Father, Mother, Child, and Breeder :sparkles: Added breeding experience to experience class *update class to current standards * More Breeding Related Syntax :sparkles: Added condition for checking ageable :sparkles: Added condition for checking breedable :sparkles: Added effect for toggle breedable :sparkles: Added effect for locking age :sparkles: Added expression for love ticks * Pattern Fixes & Class Exist checks :bug: Fixed issue with `livingentities` :bug: Added class exist checks for Breedable instances :sparkles: Added RequirePlugins annotation to select classes * Test, Test, Test :clipboard: Added test for ExprLoveTime & CondIsInLove :clipboard: Addes test for EffBreedable & CondCanBreed :clipboard: Added test fpr EffLockAge & CondCanAge * :purple_heart: Formatting * Added Adult Syntax :sparkles: Added Condition for checking is adult :sparkles: Added Effect to make an entity a baby or adult :clipboard: Added test for these syntaxes :large_blue_circle: 1.16 for any mob, below that is animals only * Apply suggestions from code review Thanks for all these formatting changes it's just what I wanted when waking up Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * ExprLoveTime -> ExprLoveTicks :sparkles: Renamed expression to ExprLoveTicks :sparkles: Added support for ticks via integers :clipboard: Added a new test for using integers :clipboard: Removed unneeded test condition * Update ExprBreedingFamily.java :sparkles: Changed expression for father, mother and offspring a bit :sparkles: Converted get to a switch statement * Update ExprExperience.java :bug: Fixed an issue where set allowed more than one value :bug: Fixed a typo in examples :sparkles: Added a spacer to examples * Added comment to CondIsAdult and EffMakeAdult Co-Authored-By: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * Syntax description notes Just adds some notes in descriptions to bring more light to animal exclusive syntax Co-Authored-By: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * Update ExprLoveTicks and SimpleEvents :bug: fix accidental forgotten example :red_circle: remove integer support from love ticks expression :clipboard: remove a test from CondIsInLove.sk Co-Authored-By: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * Fix ExprLoveTicks & Update Test :bug: Fixed an issue with ExprLoveTicks not working correctly :clipboard: Updated test to include more checks for add, remove and reset * LoveTicks -> LoveTime Co-Authored-By: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Change CondIsAdult condition checks Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Apply requested code review changes Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Apply more requested changes * update conds * update exprs * update effs * update event values * update annot * update annotations * update CondIsInLove * renames * updates * update tests * oops * fix * grr * gahr * add breeding trigger event * more * MORE * MORE!!!! * MORE!!!!!! * stupid copilot * g * fixes * agag * move to module * load * fix imports * fix indent --------- Co-authored-by: Fusezion Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- src/main/java/ch/njol/skript/Skript.java | 2 + .../skript/expressions/ExprExperience.java | 186 ++++++++---------- .../bukkit/breeding/BreedingModule.java | 52 +++++ .../bukkit/breeding/elements/CondCanAge.java | 35 ++++ .../breeding/elements/CondCanBreed.java | 36 ++++ .../bukkit/breeding/elements/CondIsAdult.java | 35 ++++ .../bukkit/breeding/elements/CondIsBaby.java | 36 ++++ .../breeding/elements/CondIsInLove.java | 38 ++++ .../breeding/elements/EffAllowAging.java | 63 ++++++ .../breeding/elements/EffBreedable.java | 61 ++++++ .../breeding/elements/EffMakeAdultOrBaby.java | 64 ++++++ .../bukkit/breeding/elements/EvtBreed.java | 61 ++++++ .../breeding/elements/ExprBreedingFamily.java | 79 ++++++++ .../breeding/elements/ExprLoveTime.java | 86 ++++++++ .../tests/syntaxes/events/EvtBreedTest.java | 53 +++++ src/test/skript/junit/EvtBreed.sk | 39 ++++ .../tests/syntaxes/conditions/CondIsInLove.sk | 26 +++ .../tests/syntaxes/effects/EffAllowAging.sk | 10 + .../tests/syntaxes/effects/EffBreedable.sk | 10 + .../syntaxes/effects/EffMakeAdultOrBaby.sk | 21 ++ .../syntaxes/expressions/ExprLoveTime.sk | 19 ++ 21 files changed, 909 insertions(+), 103 deletions(-) create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/BreedingModule.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondCanAge.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondCanBreed.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsAdult.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsBaby.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsInLove.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffAllowAging.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffBreedable.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffMakeAdultOrBaby.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EvtBreed.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/ExprBreedingFamily.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/breeding/elements/ExprLoveTime.java create mode 100644 src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtBreedTest.java create mode 100644 src/test/skript/junit/EvtBreed.sk create mode 100644 src/test/skript/tests/syntaxes/conditions/CondIsInLove.sk create mode 100644 src/test/skript/tests/syntaxes/effects/EffAllowAging.sk create mode 100644 src/test/skript/tests/syntaxes/effects/EffBreedable.sk create mode 100644 src/test/skript/tests/syntaxes/effects/EffMakeAdultOrBaby.sk create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprLoveTime.sk diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 5d25ebf6f78..8b03eaca40b 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -94,6 +94,7 @@ import org.junit.runner.Result; import org.junit.runner.notification.Failure; import org.skriptlang.skript.bukkit.SkriptMetrics; +import org.skriptlang.skript.bukkit.breeding.BreedingModule; import org.skriptlang.skript.bukkit.displays.DisplayModule; import org.skriptlang.skript.lang.comparator.Comparator; import org.skriptlang.skript.lang.comparator.Comparators; @@ -555,6 +556,7 @@ public void onEnable() { getAddonInstance().loadClasses("org.skriptlang.skript.bukkit", "misc"); // todo: become proper module once registry api is merged DisplayModule.load(); + BreedingModule.load(); } catch (final Exception e) { exception(e, "Could not load required .class files: " + e.getLocalizedMessage()); setEnabled(false); diff --git a/src/main/java/ch/njol/skript/expressions/ExprExperience.java b/src/main/java/ch/njol/skript/expressions/ExprExperience.java index 29be39d0fa2..22437910cee 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprExperience.java +++ b/src/main/java/ch/njol/skript/expressions/ExprExperience.java @@ -1,35 +1,8 @@ -/** - * 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.block.BlockBreakEvent; -import org.bukkit.event.player.PlayerExpChangeEvent; -import org.jetbrains.annotations.Nullable; - 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.doc.*; import ch.njol.skript.events.bukkit.ExperienceSpawnEvent; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; @@ -37,110 +10,117 @@ import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.util.Experience; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.event.Event; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityBreedEvent; +import org.bukkit.event.player.PlayerExpChangeEvent; +import org.jetbrains.annotations.Nullable; -/** - * @author Peter Güttinger - */ @Name("Experience") @Description("How much experience was spawned in an experience spawn or block break event. Can be changed.") -@Examples({"on experience spawn:", +@Examples({ + "on experience spawn:", "\tadd 5 to the spawned experience", - "on break of coal ore:", + "", + "on break of coal ore:", "\tclear dropped experience", - "on break of diamond ore:", + "", + "on break of diamond ore:", "\tif tool of player = diamond pickaxe:", - "\t\tadd 100 to dropped experience"}) -@Since("2.1, 2.5.3 (block break event), 2.7 (experience change event)") -@Events({"experience spawn", "break / mine", "experience change"}) + "\t\tadd 100 to dropped experience", + "", + "on breed:", + "\tbreeding father is a cow", + "\tset dropped experience to 10" +}) +@Since("2.1, 2.5.3 (block break event), 2.7 (experience change event), INSERT VERSION (breeding event)") +@Events({"experience spawn", "break / mine", "experience change", "entity breeding"}) public class ExprExperience extends SimpleExpression { + static { - Skript.registerExpression(ExprExperience.class, Experience.class, ExpressionType.SIMPLE, "[the] (spawned|dropped|) [e]xp[erience] [orb[s]]"); + Skript.registerExpression(ExprExperience.class, Experience.class, ExpressionType.SIMPLE, + "[the] (spawned|dropped|) [e]xp[erience] [orb[s]]"); } - + @Override - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { - if (!getParser().isCurrentEvent(ExperienceSpawnEvent.class, BlockBreakEvent.class, PlayerExpChangeEvent.class)) { - Skript.error("The experience expression can only be used in experience spawn, block break and player experience change events"); + public boolean init(Expression[] expressions, int matchedPattern, + Kleenean isDelayed, ParseResult parseResult) { + if (!getParser().isCurrentEvent(ExperienceSpawnEvent.class, BlockBreakEvent.class, + PlayerExpChangeEvent.class, EntityBreedEvent.class)) { + Skript.error("The experience expression can only be used in experience spawn, " + + "block break, player experience change and entity breeding events"); return false; } + return true; } @Override - @Nullable - protected Experience[] get(final Event e) { - if (e instanceof ExperienceSpawnEvent) - return new Experience[] {new Experience(((ExperienceSpawnEvent) e).getSpawnedXP())}; - else if (e instanceof BlockBreakEvent) - return new Experience[] {new Experience(((BlockBreakEvent) e).getExpToDrop())}; - else if (e instanceof PlayerExpChangeEvent) - return new Experience[] {new Experience(((PlayerExpChangeEvent) e).getAmount())}; - else - return new Experience[0]; + protected Experience @Nullable [] get(Event event) { + Experience[] exp; + + if (event instanceof ExperienceSpawnEvent experienceSpawnEvent) { + exp = new Experience[]{new Experience(experienceSpawnEvent.getSpawnedXP())}; + } else if (event instanceof BlockBreakEvent blockBreakEvent) { + exp = new Experience[]{new Experience(blockBreakEvent.getExpToDrop())}; + } else if (event instanceof PlayerExpChangeEvent playerExpChangeEvent) { + exp = new Experience[]{new Experience(playerExpChangeEvent.getAmount())}; + } else if (event instanceof EntityBreedEvent entityBreedEvent) { + exp = new Experience[]{new Experience(entityBreedEvent.getExperience())}; + } else { + exp = new Experience[0]; + } + + return exp; } @Override - @Nullable - public Class[] acceptChange(final ChangeMode mode) { - switch (mode) { - case ADD: - case DELETE: - case REMOVE: - case REMOVE_ALL: - return new Class[] {Experience[].class, Number[].class}; - case SET: - return new Class[] {Experience.class, Number.class}; - case RESET: - return null; - } - return null; + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE -> CollectionUtils.array(Experience.class, Integer.class); + case ADD, REMOVE -> CollectionUtils.array(Experience[].class, Integer[].class); + case RESET -> CollectionUtils.array(); + default -> null; + }; } @Override - public void change(final Event e, final @Nullable Object[] delta, final ChangeMode mode) { - double eventExp; - if (e instanceof ExperienceSpawnEvent) { - eventExp = ((ExperienceSpawnEvent) e).getSpawnedXP(); - } else if (e instanceof BlockBreakEvent) { - eventExp = ((BlockBreakEvent) e).getExpToDrop(); - } else if (e instanceof PlayerExpChangeEvent) { - eventExp = ((PlayerExpChangeEvent) e).getAmount(); + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + int exp; + + if (event instanceof ExperienceSpawnEvent experienceSpawnEvent) { + exp = experienceSpawnEvent.getSpawnedXP(); + } else if (event instanceof BlockBreakEvent blockBreakEvent) { + exp = blockBreakEvent.getExpToDrop(); + } else if (event instanceof PlayerExpChangeEvent playerExpChangeEvent) { + exp = playerExpChangeEvent.getAmount(); + } else if (event instanceof EntityBreedEvent entityBreedEvent) { + exp = entityBreedEvent.getExperience(); } else { return; } - if (delta == null) { - eventExp = 0; - } else { - for (Object obj : delta) { - double value = obj instanceof Experience ? ((Experience) obj).getXP() : ((Number) obj).doubleValue(); + + if (delta != null) { + for (Object object : delta) { + int value = object instanceof Experience experience ? experience.getXP() : (int) object; switch (mode) { - case ADD: - eventExp += value; - break; - case SET: - eventExp = value; - break; - case REMOVE: - case REMOVE_ALL: - eventExp -= value; - break; - case RESET: - case DELETE: - assert false; - break; + case ADD -> exp += value; + case SET -> exp = value; + case REMOVE, REMOVE_ALL -> exp -= value; } } } - - eventExp = Math.max(0, Math.round(eventExp)); - int roundedEventExp = (int) eventExp; - if (e instanceof ExperienceSpawnEvent) { - ((ExperienceSpawnEvent) e).setSpawnedXP(roundedEventExp); - } else if (e instanceof BlockBreakEvent) { - ((BlockBreakEvent) e).setExpToDrop(roundedEventExp); - } else if (e instanceof PlayerExpChangeEvent) { - ((PlayerExpChangeEvent) e).setAmount(roundedEventExp); + exp = Math.max(0, exp); + if (event instanceof ExperienceSpawnEvent experienceSpawnEvent) { + experienceSpawnEvent.setSpawnedXP(exp); + } else if (event instanceof BlockBreakEvent blockBreakEvent) { + blockBreakEvent.setExpToDrop(exp); + } else if (event instanceof PlayerExpChangeEvent playerExpChangeEvent) { + playerExpChangeEvent.setAmount(exp); + } else if (event instanceof EntityBreedEvent entityBreedEvent) { + entityBreedEvent.setExperience(exp); } } @@ -155,7 +135,7 @@ public Class getReturnType() { } @Override - public String toString(final @Nullable Event e, final boolean debug) { + public String toString(@Nullable Event event, boolean debug) { return "the experience"; } diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/BreedingModule.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/BreedingModule.java new file mode 100644 index 00000000000..7454d4c3196 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/BreedingModule.java @@ -0,0 +1,52 @@ +package org.skriptlang.skript.bukkit.breeding; + +import ch.njol.skript.Skript; +import ch.njol.skript.lang.util.SimpleEvent; +import ch.njol.skript.registrations.EventValues; +import ch.njol.skript.util.Getter; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityBreedEvent; +import org.bukkit.event.entity.EntityEnterLoveModeEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +public class BreedingModule { + + public static void load() throws IOException { + Skript.getAddonInstance().loadClasses("org.skriptlang.skript.bukkit.breeding", "elements"); + + Skript.registerEvent("Love Mode Enter", SimpleEvent.class, EntityEnterLoveModeEvent.class, + "[entity] enter[s] love mode", "[entity] love mode [enter]") + .description("Called whenever an entity enters a state of being in love.") + .examples( + "on love mode enter:", + "\tcancel event # No one is allowed love here" + ) + .since("INSERT VERSION"); + + EventValues.registerEventValue(EntityBreedEvent.class, ItemStack.class, new Getter<>() { + @Override + public @Nullable ItemStack get(EntityBreedEvent event) { + return event.getBredWith(); + } + }, EventValues.TIME_NOW); + + EventValues.registerEventValue(EntityEnterLoveModeEvent.class, LivingEntity.class, new Getter<>() { + @Override + public LivingEntity get(EntityEnterLoveModeEvent event) { + return event.getEntity(); + } + }, EventValues.TIME_NOW); + + EventValues.registerEventValue(EntityEnterLoveModeEvent.class, HumanEntity.class, new Getter<>() { + @Override + public @Nullable HumanEntity get(EntityEnterLoveModeEvent event) { + return event.getHumanEntity(); + } + }, EventValues.TIME_NOW); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondCanAge.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondCanAge.java new file mode 100644 index 00000000000..8047e31883b --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondCanAge.java @@ -0,0 +1,35 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +import ch.njol.skript.conditions.base.PropertyCondition; +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 org.bukkit.entity.Breedable; +import org.bukkit.entity.LivingEntity; + +@Name("Can Age") +@Description("Checks whether or not an entity will be able to age/grow up.") +@Examples({ + "on breeding:", + "\tentity can't age", + "\tbroadcast \"An immortal has been born!\" to player" +}) +@Since("INSERT VERSION") +public class CondCanAge extends PropertyCondition { + + static { + register(CondCanAge.class, PropertyType.CAN, "(age|grow (up|old[er]))", "livingentities"); + } + + @Override + public boolean check(LivingEntity entity) { + return entity instanceof Breedable breedable && !breedable.getAgeLock(); + } + + @Override + protected String getPropertyName() { + return "age"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondCanBreed.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondCanBreed.java new file mode 100644 index 00000000000..ee473c6498b --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondCanBreed.java @@ -0,0 +1,36 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +import ch.njol.skript.Skript; +import ch.njol.skript.conditions.base.PropertyCondition; +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 org.bukkit.entity.Breedable; +import org.bukkit.entity.LivingEntity; + +@Name("Can Breed") +@Description("Checks whether or not a living entity can be bred.") +@Examples({ + "on right click on living entity:", + "\tevent-entity can't breed", + "\tsend \"Turns out %event-entity% is not breedable. Must be a Skript user!\" to player" +}) +@Since("INSERT VERSION") +public class CondCanBreed extends PropertyCondition { + + static { + register(CondCanBreed.class, PropertyType.CAN, "(breed|be bred)", "livingentities"); + } + + @Override + public boolean check(LivingEntity entity) { + return entity instanceof Breedable breedable && breedable.canBreed(); + } + + @Override + protected String getPropertyName() { + return "breed"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsAdult.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsAdult.java new file mode 100644 index 00000000000..90036e04d82 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsAdult.java @@ -0,0 +1,35 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +import ch.njol.skript.conditions.base.PropertyCondition; +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 org.bukkit.entity.Ageable; +import org.bukkit.entity.LivingEntity; + +@Name("Is Adult") +@Description("Checks whether or not a living entity is an adult.") +@Examples({ + "on drink:", + "\tevent-entity is not an adult", + "\tkill event-entity" +}) +@Since("INSERT VERSION") +public class CondIsAdult extends PropertyCondition { + + static { + register(CondIsAdult.class, "[an] adult", "livingentities"); + } + + @Override + public boolean check(LivingEntity entity) { + return entity instanceof Ageable ageable && ageable.isAdult(); + } + + @Override + protected String getPropertyName() { + return "an adult"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsBaby.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsBaby.java new file mode 100644 index 00000000000..278b6da80ae --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsBaby.java @@ -0,0 +1,36 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +import ch.njol.skript.conditions.base.PropertyCondition; +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 org.bukkit.entity.Ageable; +import org.bukkit.entity.LivingEntity; + +@Name("Is Baby") +@Description("Checks whether or not a living entity is a baby.") +@Examples({ + "on drink:", + "\tevent-entity is a baby", + "\tkill event-entity" +}) +@Since("INSERT VERSION") +public class CondIsBaby extends PropertyCondition { + + static { + register(CondIsBaby.class, "a (child|baby)", "livingentities"); + } + + @Override + public boolean check(LivingEntity entity) { + return entity instanceof Ageable ageable && !ageable.isAdult(); + + } + + @Override + protected String getPropertyName() { + return "a baby"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsInLove.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsInLove.java new file mode 100644 index 00000000000..659a02961b2 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/CondIsInLove.java @@ -0,0 +1,38 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +import ch.njol.skript.conditions.base.PropertyCondition; +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 org.bukkit.entity.Animals; +import org.bukkit.entity.LivingEntity; + +@Name("Is In Love") +@Description("Checks whether or not a living entity is in love.") +@Examples({ + "on spawn of living entity:", + "\tif entity is in love:", + "broadcast \"That was quick!\"" +}) +@Since("INSERT VERSION") +public class CondIsInLove extends PropertyCondition { + + static { + register(CondIsInLove.class, "in lov(e|ing) [state|mode]", "livingentities"); + } + + @Override + public boolean check(LivingEntity entity) { + if (entity instanceof Animals animals) + return animals.isLoveMode(); + + return false; + } + + @Override + protected String getPropertyName() { + return "in love"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffAllowAging.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffAllowAging.java new file mode 100644 index 00000000000..4745125adbf --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffAllowAging.java @@ -0,0 +1,63 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +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.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Breedable; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Allow Aging") +@Description("Sets whether or not living entities will be able to age.") +@Examples({ + "on spawn of animal:", + "\tallow aging of entity" +}) +@Since("INSERT VERSION") +public class EffAllowAging extends Effect { + + static { + Skript.registerEffect(EffAllowAging.class, + "lock age of %livingentities%", + "prevent aging of %livingentities%", + "prevent %livingentities% from aging", + "unlock age of %livingentities%", + "allow aging of %livingentities%", + "allow %livingentities% to age"); + } + + private boolean unlock; + private Expression entities; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, + Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + entities = (Expression) expressions[0]; + unlock = matchedPattern > 2; + return true; + } + + @Override + protected void execute(Event event) { + for (LivingEntity livingEntity : entities.getArray(event)) { + if (!(livingEntity instanceof Breedable breedable)) + continue; + + breedable.setAgeLock(!unlock); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return (unlock ? "allow" : "prevent") + " aging of " + entities.toString(event,debug); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffBreedable.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffBreedable.java new file mode 100644 index 00000000000..c0c0cb0ac19 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffBreedable.java @@ -0,0 +1,61 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +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.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Breedable; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Make Breedable") +@Description("Sets whether or not entities will be able to breed. Only works on animals.") +@Examples({ + "on spawn of animal:", + "\tmake entity unbreedable" +}) +@Since("INSERT VERSION") +public class EffBreedable extends Effect { + + static { + Skript.registerEffect(EffBreedable.class, + "make %livingentities% breedable", + "unsterilize %livingentities%", + "make %livingentities% (not |non(-| )|un)breedable", + "sterilize %livingentities%"); + } + + private boolean sterilize; + private Expression entities; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, + Kleenean isDelayed, ParseResult parseResult) { + sterilize = matchedPattern > 1; + //noinspection unchecked + entities = (Expression) expressions[0]; + return true; + } + + @Override + protected void execute(Event event) { + for (LivingEntity entity : entities.getArray(event)) { + if (!(entity instanceof Breedable breedable)) + continue; + + breedable.setBreed(!sterilize); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "make " + entities.toString(event, debug) + (sterilize ? " non-" : " ") + "breedable"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffMakeAdultOrBaby.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffMakeAdultOrBaby.java new file mode 100644 index 00000000000..de3d98c8aa3 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EffMakeAdultOrBaby.java @@ -0,0 +1,64 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +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.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Make Adult/Baby") +@Description("Force a animal to become an adult or baby.") +@Examples({ + "on spawn of mob:", + "\tentity is not an adult", + "\tmake entity an adult", +}) +@Since("INSERT VERSION") +public class EffMakeAdultOrBaby extends Effect { + + static { + Skript.registerEffect(EffMakeAdultOrBaby.class, + "make %livingentities% [a[n]] (adult|:baby)", + "force %livingentities% to be[come] a[n] (adult|:baby)"); + } + + private boolean baby; + private Expression entities; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, + Kleenean isDelayed, ParseResult parseResult) { + baby = parseResult.hasTag("baby"); + //noinspection unchecked + entities = (Expression) expressions[0]; + return true; + } + + @Override + protected void execute(Event event) { + for (LivingEntity entity : entities.getArray(event)) { + if (!(entity instanceof Ageable ageable)) + continue; + + if (baby) { + ageable.setBaby(); + } else { + ageable.setAdult(); + } + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "make " + entities + (baby ? " a baby" : " an adult"); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EvtBreed.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EvtBreed.java new file mode 100644 index 00000000000..e268aaf7d66 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/EvtBreed.java @@ -0,0 +1,61 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +import ch.njol.skript.Skript; +import ch.njol.skript.entity.EntityType; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptEvent; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import org.bukkit.entity.Entity; +import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityBreedEvent; +import org.jetbrains.annotations.Nullable; + +public class EvtBreed extends SkriptEvent { + + static { + Skript.registerEvent("Entity Breed", EvtBreed.class, EntityBreedEvent.class, + "[entity] breed[ing] [of %-entitytypes%]") + .description("Called whenever two animals begin to conceive a child. The type can be specified.") + .examples( + "on breeding of llamas:", + "\tsend \"When a %breeding mother% and %breeding father% love each " + + "other very much they make %offspring%\" to breeder" + ) + .since("INSERT VERSION"); + } + + private @Nullable Literal entitiesLiteral; + private EntityType @Nullable [] entities; + + @Override + public boolean init(Literal[] args, int matchedPattern, ParseResult parseResult) { + if (args[0] != null) { + //noinspection unchecked + entitiesLiteral = ((Literal) args[0]); + entities = entitiesLiteral.getAll(); + } + return true; + } + + @Override + public boolean check(Event event) { + return event instanceof EntityBreedEvent breedEvent && checkEntity(breedEvent.getEntity()); + } + + private boolean checkEntity(Entity entity) { + if (entities != null) { + for (EntityType entityType : entities) { + if (entityType.isInstance(entity)) + return true; + } + return false; + } + return true; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "on breeding" + (entitiesLiteral == null ? "" : " of " + entitiesLiteral); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/ExprBreedingFamily.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/ExprBreedingFamily.java new file mode 100644 index 00000000000..4785a671357 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/ExprBreedingFamily.java @@ -0,0 +1,79 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +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 org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityBreedEvent; +import org.jetbrains.annotations.Nullable; + +@Name("Breeding Family") +@Description("Represents family members within a breeding event.") +@Examples({ + "on breeding:", + "\tsend \"When a %breeding mother% and %breeding father% love each other very much, " + + "they make a %bred offspring%\" to breeder" +}) +@Since("INSERT VERSION") +public class ExprBreedingFamily extends SimpleExpression { + + static { + Skript.registerExpression(ExprBreedingFamily.class, LivingEntity.class, ExpressionType.SIMPLE, + "[the] breeding mother", + "[the] breeding father", + "[the] [bred] (offspring|child)", + "[the] breeder"); + } + + private int pattern; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, + Kleenean isDelayed, ParseResult parseResult) { + if (!getParser().isCurrentEvent(EntityBreedEvent.class)) { + Skript.error("The 'breeding family' expression can only be used in an breed event."); + return false; + } + + pattern = matchedPattern; + return true; + } + + @Override + protected @Nullable LivingEntity [] get(Event event) { + if (!(event instanceof EntityBreedEvent breedEvent)) + return new LivingEntity[0]; + + return switch (pattern) { + case 0 -> new LivingEntity[]{breedEvent.getMother()}; + case 1 -> new LivingEntity[]{breedEvent.getFather()}; + case 2 -> new LivingEntity[]{breedEvent.getEntity()}; + case 3 -> new LivingEntity[]{breedEvent.getBreeder()}; + default -> new LivingEntity[0]; + }; + } + + @Override + public boolean isSingle() { + return true; + } + + @Override + public Class getReturnType() { + return LivingEntity.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "breeding family"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/ExprLoveTime.java b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/ExprLoveTime.java new file mode 100644 index 00000000000..ffca5d64a38 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/breeding/elements/ExprLoveTime.java @@ -0,0 +1,86 @@ +package org.skriptlang.skript.bukkit.breeding.elements; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.util.Timespan; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Animals; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Love Time") +@Description({ + "The amount of time the animals have been in love for. " + + "Using a value of 30 seconds is equivalent to using an item to breed them.", + "Only works on animals that can be bred and returns '0 seconds' for animals that can't be bred." +}) +@Examples({ + "on right click:", + "\tsend \"%event-entity% has been in love for %love time of event-entity% more than you!\" to player" +}) +@Since("INSERT VERSION") +public class ExprLoveTime extends SimplePropertyExpression { + + static { + register(ExprLoveTime.class, Timespan.class, "love[d] time", "livingentities"); + } + + @Override + public @Nullable Timespan convert(LivingEntity entity) { + if (entity instanceof Animals animal) + return new Timespan(Timespan.TimePeriod.TICK, animal.getLoveModeTicks()); + + return new Timespan(0); + } + + @Override + public @Nullable Class[] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET -> CollectionUtils.array(Timespan.class); + case ADD, REMOVE -> CollectionUtils.array(Timespan[].class); + case RESET -> CollectionUtils.array(); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + int changeTicks = 0; + + if (delta != null) { + for (Object object : delta) { + changeTicks += (int) ((Timespan) object).getAs(Timespan.TimePeriod.TICK); + } + } + + for (LivingEntity livingEntity : getExpr().getArray(event)) { + if (!(livingEntity instanceof Animals animal)) + continue; + + int loveTicks = animal.getLoveModeTicks(); + switch (mode) { + case ADD -> loveTicks += changeTicks; + case REMOVE -> loveTicks -= changeTicks; + case SET -> loveTicks = changeTicks; + case RESET -> loveTicks = 0; + } + animal.setLoveModeTicks(Math.max(loveTicks, 0)); + } + } + + @Override + public Class getReturnType() { + return Timespan.class; + } + + @Override + protected String getPropertyName() { + return "love time"; + } + +} diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtBreedTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtBreedTest.java new file mode 100644 index 00000000000..be6eb6da784 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/events/EvtBreedTest.java @@ -0,0 +1,53 @@ +package org.skriptlang.skript.test.tests.syntaxes.events; + +import ch.njol.skript.test.runner.SkriptJUnitTest; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Pig; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class EvtBreedTest extends SkriptJUnitTest { + + static { + setShutdownDelay(1); + } + + private Pig child; + private Pig mother; + private Pig father; + private Player player; + + @Before + public void before() { + child = spawnTestPig(); + child.setCustomName("child"); + mother = spawnTestPig(); + mother.setCustomName("mother"); + father = spawnTestPig(); + father.setCustomName("father"); + + player = EasyMock.niceMock(Player.class); + EasyMock.expect(player.getName()).andReturn("Efnilite"); + EasyMock.replay(player); + } + + @Test + public void test() { + Bukkit.getPluginManager().callEvent( + new org.bukkit.event.entity.EntityBreedEvent( + child, mother, father, player, new ItemStack(Material.CARROT), 0)); + } + + @After + public void after() { + child.remove(); + mother.remove(); + father.remove(); + } + +} diff --git a/src/test/skript/junit/EvtBreed.sk b/src/test/skript/junit/EvtBreed.sk new file mode 100644 index 00000000000..52a4091666e --- /dev/null +++ b/src/test/skript/junit/EvtBreed.sk @@ -0,0 +1,39 @@ +test "EvtBreedJUnit" when running JUnit: + set {_tests::1} to "correct breeder" + set {_tests::2} to "correct breeding mother" + set {_tests::3} to "correct breeding father" + set {_tests::4} to "correct bred child" + set {_tests::5} to "item is carrot" + + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.events.EvtBreedTest" completes {_tests::*} + +on breed of pig: + set {_test} to "org.skriptlang.skript.test.tests.syntaxes.events.EvtBreedTest" + junit test is {_test} + + if: + breeder is a player + breeder's name is "Efnilite" + then: + complete objective "correct breeder" for {_test} + + if: + breeding mother is a pig + breeding mother's name is "mother" + then: + complete objective "correct breeding mother" for {_test} + + if: + breeding father is a pig + breeding father's name is "father" + then: + complete objective "correct breeding father" for {_test} + + if: + bred child is pig + bred child's name is "child" + then: + complete objective "correct bred child" for {_test} + + if event-item is a carrot: + complete objective "item is carrot" for {_test} diff --git a/src/test/skript/tests/syntaxes/conditions/CondIsInLove.sk b/src/test/skript/tests/syntaxes/conditions/CondIsInLove.sk new file mode 100644 index 00000000000..2a23f4860e8 --- /dev/null +++ b/src/test/skript/tests/syntaxes/conditions/CondIsInLove.sk @@ -0,0 +1,26 @@ +test "valid entity is in love": + set {_spawn} to spawn of world "world" + spawn adult cow at {_spawn} + set {_cow} to last spawned cow + assert love time of {_cow} is 0 seconds with "cow love time was not 0 seconds after spawning" + assert {_cow} is not in love with "cow is in love after spawning" + set love time of {_cow} to 10 seconds + assert {_cow} is in love with "cow is not in love after setting love time" + add 10 seconds to love time of {_cow} + assert love time of {_cow} > 10 seconds with "cow love time didn't go up after adding" + remove 1 minute from love time of {_cow} + assert {_cow} is not in love with "cow is in love after removing more love than possible" + add 10 minutes to love time of {_cow} + assert {_cow} is in love with "cow didn't enter love mode after adding love time" + reset love time of {_cow} + assert {_cow} is not in love with "cow was still in love even after resetting love time" + delete all cows + +test "invalid entity is in love": + set {_spawn} to spawn of world "world" + spawn adult zombie at {_spawn} + set {_zombie} to last spawned zombie + assert love time of {_zombie} is 0 seconds with "zombie was in love on spawn" + set love time of {_zombie} to 10 minutes + assert love time of {_zombie} is 0 seconds with "zombie was in love after set" + delete all zombies diff --git a/src/test/skript/tests/syntaxes/effects/EffAllowAging.sk b/src/test/skript/tests/syntaxes/effects/EffAllowAging.sk new file mode 100644 index 00000000000..e79a3f06e18 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffAllowAging.sk @@ -0,0 +1,10 @@ +test "allow aging": + set {_spawn} to spawn of world "world" + spawn baby pig at {_spawn} + set {_pig} to last spawned pig + assert {_pig} can age with "baby pig can't age before locking" + lock age of {_pig} + assert {_pig} can't age with "baby pig can still age after locking" + unlock age of {_pig} + assert {_pig} can age with "baby pig can't age after unlocking" + delete all pigs diff --git a/src/test/skript/tests/syntaxes/effects/EffBreedable.sk b/src/test/skript/tests/syntaxes/effects/EffBreedable.sk new file mode 100644 index 00000000000..6010f26cff4 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffBreedable.sk @@ -0,0 +1,10 @@ +test "breedable": + set {_spawn} to spawn of world "world" + spawn baby chicken at {_spawn} + set {_chicken} to last spawned chicken + assert {_chicken} can't breed with "chicken could breed before growing up" + make {_chicken} breedable + assert {_chicken} can breed with "chicken can't breed after growing up" + make {_chicken} non-breedable + assert {_chicken} can't breed with "chicken can breed after growing up and preventing" + delete all chickens diff --git a/src/test/skript/tests/syntaxes/effects/EffMakeAdultOrBaby.sk b/src/test/skript/tests/syntaxes/effects/EffMakeAdultOrBaby.sk new file mode 100644 index 00000000000..749a8159edc --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffMakeAdultOrBaby.sk @@ -0,0 +1,21 @@ +test "make mob adult": + set {_spawn} to spawn of world "world" + spawn baby zombie at {_spawn} + set {_zombie} to last spawned zombie + assert {_zombie} is a baby with "spawned zombie was an adult" + make {_zombie} an adult + assert {_zombie} is an adult with "spawned zombie is still a baby zombie" + make {_zombie} a baby + assert {_zombie} is not an adult with "spawned zombie didn't become a baby zombie" + delete all zombies + +test "make animal adult": + set {_spawn} to spawn of world "world" + spawn baby sheep at {_spawn} + set {_sheep} to last spawned sheep + assert {_sheep} is a baby with "spawned sheep was an adult" + make {_sheep} an adult + assert {_sheep} is an adult with "spawned sheep is still a baby sheep" + make {_sheep} a baby + assert {_sheep} is not an adult with "spawned sheep didn't become a baby sheep" + delete all sheep diff --git a/src/test/skript/tests/syntaxes/expressions/ExprLoveTime.sk b/src/test/skript/tests/syntaxes/expressions/ExprLoveTime.sk new file mode 100644 index 00000000000..3e02d51377b --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprLoveTime.sk @@ -0,0 +1,19 @@ +test "love time": + spawn pig at spawn of world "world" + set {_pig} to last spawned pig + + assert love time of {_pig} is 0 seconds with "love time wasn't 0 seconds by default" + + set love time of {_pig} to 10 seconds + assert love time of {_pig} is 10 seconds with "love time wasn't set to 10 seconds" + + add 10 seconds to love time of {_pig} + assert love time of {_pig} is 20 seconds with "love time wasn't increased by 10 seconds" + + remove 1 minute from love time of {_pig} + assert love time of {_pig} is 0 seconds with "love time wasn't decreased by 1 minute" + + reset love time of {_pig} + assert love time of {_pig} is 0 seconds with "love time wasn't reset" + + delete all pigs From d75b47fda85376b28b12c9dbd627d317fb5a68af Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Thu, 31 Oct 2024 21:21:22 +0100 Subject: [PATCH 09/18] Add chat client options (#7105) * init commit * update * update * update doc * weird error * weird error * Apply suggestions from code review Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * ok * add new cond * remove chat visibility type * remove old * fix names * oops * oops * aight --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../skript/conditions/CondChatColors.java | 36 ++++++++ .../skript/conditions/CondChatFiltering.java | 35 ++++++++ .../skript/conditions/CondChatVisibility.java | 83 +++++++++++++++++++ .../conditions/CondChatVisibilityTest.java | 48 +++++++++++ src/test/skript/junit/CondChatVisibility.sk | 18 ++++ 5 files changed, 220 insertions(+) create mode 100644 src/main/java/ch/njol/skript/conditions/CondChatColors.java create mode 100644 src/main/java/ch/njol/skript/conditions/CondChatFiltering.java create mode 100644 src/main/java/ch/njol/skript/conditions/CondChatVisibility.java create mode 100644 src/test/java/org/skriptlang/skript/test/tests/syntaxes/conditions/CondChatVisibilityTest.java create mode 100644 src/test/skript/junit/CondChatVisibility.sk diff --git a/src/main/java/ch/njol/skript/conditions/CondChatColors.java b/src/main/java/ch/njol/skript/conditions/CondChatColors.java new file mode 100644 index 00000000000..25f90805835 --- /dev/null +++ b/src/main/java/ch/njol/skript/conditions/CondChatColors.java @@ -0,0 +1,36 @@ +package ch.njol.skript.conditions; + +import ch.njol.skript.Skript; +import ch.njol.skript.conditions.base.PropertyCondition; +import ch.njol.skript.doc.*; +import com.destroystokyo.paper.ClientOption; +import org.bukkit.entity.Player; + +@Name("Can See Chat Colors") +@Description("Checks whether a player can see chat colors.") +@Examples({ + "if player can see chat colors:", + "\tsend \"Find the red word in this message.\"", + "else:", + "\tsend \"You cannot partake in finding the colored word.\"" +}) +@RequiredPlugins("Paper") +@Since("INSERT VERSION") +public class CondChatColors extends PropertyCondition { + + static { + if (Skript.classExists("com.destroystokyo.paper.ClientOption")) + register(CondChatColors.class, PropertyType.CAN, "see chat colo[u]r[s|ing]", "players"); + } + + @Override + public boolean check(Player player) { + return player.getClientOption(ClientOption.CHAT_COLORS_ENABLED); + } + + @Override + protected String getPropertyName() { + return "see chat colors"; + } + +} diff --git a/src/main/java/ch/njol/skript/conditions/CondChatFiltering.java b/src/main/java/ch/njol/skript/conditions/CondChatFiltering.java new file mode 100644 index 00000000000..0cd28cfab10 --- /dev/null +++ b/src/main/java/ch/njol/skript/conditions/CondChatFiltering.java @@ -0,0 +1,35 @@ +package ch.njol.skript.conditions; + +import ch.njol.skript.Skript; +import ch.njol.skript.conditions.base.PropertyCondition; +import ch.njol.skript.doc.*; +import com.destroystokyo.paper.ClientOption; +import org.bukkit.entity.Player; + +@Name("Has Chat Filtering") +@Description("Checks whether a player has chat filtering enabled.") +@Examples({ + "if player doesn't have chat filtering enabled:", + "send \"This server may contain mature chat messages. You have been warned!\" to player", +}) +@RequiredPlugins("Paper") +@Since("INSERT VERSION") +public class CondChatFiltering extends PropertyCondition { + + static { + if (Skript.classExists("com.destroystokyo.paper.ClientOption")) + register(CondChatFiltering.class, PropertyType.HAVE, + "(chat|text) filtering (on|enabled)", "players"); + } + + @Override + public boolean check(Player player) { + return player.getClientOption(ClientOption.TEXT_FILTERING_ENABLED); + } + + @Override + protected String getPropertyName() { + return "chat filtering enabled"; + } + +} diff --git a/src/main/java/ch/njol/skript/conditions/CondChatVisibility.java b/src/main/java/ch/njol/skript/conditions/CondChatVisibility.java new file mode 100644 index 00000000000..b8045cc952f --- /dev/null +++ b/src/main/java/ch/njol/skript/conditions/CondChatVisibility.java @@ -0,0 +1,83 @@ +package ch.njol.skript.conditions; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.*; +import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import com.destroystokyo.paper.ClientOption; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Can See Messages") +@Description("Checks whether a player can see specific message types in chat.") +@Examples({ + "if player can see all messages:", + "\tsend \"You can see all messages.\"", + "if player can only see commands:", + "\tsend \"This game doesn't work with commands-only chat.\"", + "if player can't see any messages:", + "\tsend action bar \"Server shutting down in 5 minutes!\"" +}) +@RequiredPlugins("Paper") +@Since("INSERT VERSION") +public class CondChatVisibility extends Condition { + + static { + if (Skript.classExists("com.destroystokyo.paper.ClientOption$ChatVisibility")) + Skript.registerCondition(CondChatVisibility.class, + "%player% can see all messages [in chat]", + "%player% can only see (commands|system messages) [in chat]", + "%player% can('t|[ ]not) see any (command[s]|message[s]) [in chat]", + "%player% can('t|[ ]not) see all messages [in chat]", + "%player% can('t|[ ]not) only see (commands|system messages) [in chat]"); + } + + private int pattern = 0; + private Expression player; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] expressions, int matchedPattern, + Kleenean isDelayed, ParseResult parseResult) { + pattern = matchedPattern; + player = (Expression) expressions[0]; + + setNegated(matchedPattern > 1); + return true; + } + + @Override + public boolean check(Event event) { + Player player = this.player.getSingle(event); + + if (player == null) + return false; + + ClientOption.ChatVisibility current = player.getClientOption(ClientOption.CHAT_VISIBILITY); + + return switch (pattern) { + case 0 -> current == ClientOption.ChatVisibility.FULL; + case 1 -> current == ClientOption.ChatVisibility.SYSTEM; + case 2 -> current == ClientOption.ChatVisibility.HIDDEN; + case 3 -> current != ClientOption.ChatVisibility.FULL; + case 4 -> current != ClientOption.ChatVisibility.SYSTEM; + default -> throw new IllegalStateException("Unexpected value: " + pattern); + }; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return switch (pattern) { + case 0 -> player.toString(event, debug) + " can see all messages"; + case 1 -> player.toString(event, debug) + " can only see commands"; + case 2 -> player.toString(event, debug) + " can't see any messages"; + case 3 -> player.toString(event, debug) + " can't see all messages"; + case 4 -> player.toString(event, debug) + " can't only see commands"; + default -> throw new IllegalStateException("Unexpected value: " + pattern); + }; + } + +} diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/conditions/CondChatVisibilityTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/conditions/CondChatVisibilityTest.java new file mode 100644 index 00000000000..1dbcdc98c19 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/conditions/CondChatVisibilityTest.java @@ -0,0 +1,48 @@ +package org.skriptlang.skript.test.tests.syntaxes.conditions; + +import ch.njol.skript.Skript; +import ch.njol.skript.test.runner.SkriptJUnitTest; +import com.destroystokyo.paper.ClientOption; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerJoinEvent; +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; + +public class CondChatVisibilityTest extends SkriptJUnitTest { + + private static final boolean SUPPORTS_CHAT_VISIBILITY = + Skript.classExists("com.destroystokyo.paper.ClientOption$ChatVisibility"); + + static { + setShutdownDelay(1); + } + + private Player player; + + @Before + public void setup() { + if (!SUPPORTS_CHAT_VISIBILITY) + return; + + player = EasyMock.niceMock(Player.class); + + EasyMock.expect(player.getClientOption(ClientOption.CHAT_VISIBILITY)) + .andReturn(ClientOption.ChatVisibility.SYSTEM); + EasyMock.expect(player.getClientOption(ClientOption.TEXT_FILTERING_ENABLED)) + .andReturn(false); + EasyMock.expect(player.getClientOption(ClientOption.CHAT_COLORS_ENABLED)) + .andReturn(true); + EasyMock.replay(player); + } + + @Test + public void test() { + if (!SUPPORTS_CHAT_VISIBILITY) + return; + + Bukkit.getPluginManager().callEvent(new PlayerJoinEvent(player, "hi")); + } + +} diff --git a/src/test/skript/junit/CondChatVisibility.sk b/src/test/skript/junit/CondChatVisibility.sk new file mode 100644 index 00000000000..9ce5f39dd9c --- /dev/null +++ b/src/test/skript/junit/CondChatVisibility.sk @@ -0,0 +1,18 @@ +test "CondChatVisibilityJUnit" when running JUnit: + set {_tests::1} to "chat visibility is only commands" + set {_tests::2} to "no chat filtering" + set {_tests::3} to "can see chat colours" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.conditions.CondChatVisibilityTest" completes {_tests::*} + +on join: + set {_test} to "org.skriptlang.skript.test.tests.syntaxes.conditions.CondChatVisibilityTest" + junit test is {_test} + + if player can only see commands: + complete objective "chat visibility is only commands" for {_test} + + if player doesn't have chat filtering enabled: + complete objective "no chat filtering" for {_test} + + if player can see chat colours: + complete objective "can see chat colours" for {_test} From 0448a951a0b7f6b8e90bb4adc23612db95e6958b Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:54:36 -0400 Subject: [PATCH 10/18] Adds basic 1.21.3 support. (#7173) * 1.21.3 support * Update default.lang --- build.gradle | 4 +- gradle.properties | 2 +- .../ch/njol/skript/entity/BoatChestData.java | 22 +++++++++- .../java/ch/njol/skript/entity/BoatData.java | 44 +++++++++++++------ src/main/resources/lang/default.lang | 35 +++++++++++++++ .../{paper-1.21.0.json => paper-1.21.3.json} | 4 +- .../tests/syntaxes/effects/EffHealth.sk | 10 ++--- 7 files changed, 94 insertions(+), 27 deletions(-) rename src/test/skript/environments/java21/{paper-1.21.0.json => paper-1.21.3.json} (85%) diff --git a/build.gradle b/build.gradle index 23044477a27..1fa00be86b8 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ 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.21-R0.1-SNAPSHOT' + implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.21.3-R0.1-SNAPSHOT' 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.3', { @@ -240,7 +240,7 @@ def java21 = 21 def java17 = 17 def java11 = 11 -def latestEnv = 'java21/paper-1.21.0.json' +def latestEnv = 'java21/paper-1.21.3.json' def latestJava = java21 def oldestJava = java11 diff --git a/gradle.properties b/gradle.properties index 8fa041e0878..706418cbd62 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,5 +7,5 @@ groupid=ch.njol name=skript version=2.9.3 jarName=Skript.jar -testEnv=java21/paper-1.21.0 +testEnv=java21/paper-1.21.3 testEnvJavaVersion=21 diff --git a/src/main/java/ch/njol/skript/entity/BoatChestData.java b/src/main/java/ch/njol/skript/entity/BoatChestData.java index 16676637590..4b136650393 100644 --- a/src/main/java/ch/njol/skript/entity/BoatChestData.java +++ b/src/main/java/ch/njol/skript/entity/BoatChestData.java @@ -19,25 +19,41 @@ package ch.njol.skript.entity; import ch.njol.skript.Skript; -import ch.njol.skript.aliases.Aliases; import ch.njol.skript.aliases.ItemType; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser; import org.bukkit.Material; import org.bukkit.TreeSpecies; import org.bukkit.entity.ChestBoat; -import org.bukkit.inventory.ItemStack; +import org.bukkit.entity.boat.AcaciaChestBoat; +import org.bukkit.entity.boat.BirchChestBoat; +import org.bukkit.entity.boat.DarkOakChestBoat; +import org.bukkit.entity.boat.JungleChestBoat; +import org.bukkit.entity.boat.OakChestBoat; +import org.bukkit.entity.boat.SpruceChestBoat; import org.jetbrains.annotations.Nullable; +import java.util.EnumMap; import java.util.Random; public class BoatChestData extends EntityData { + private static final EnumMap> typeToClassMap = new EnumMap<>(TreeSpecies.class); + private static final boolean IS_RUNNING_1_21_3 = Skript.isRunningMinecraft(1, 21, 3); + static { if (Skript.classExists("org.bukkit.entity.ChestBoat")) { EntityData.register(BoatChestData.class, "chest boat", ChestBoat.class, 0, "chest boat", "any chest boat", "oak chest boat", "spruce chest boat", "birch chest boat", "jungle chest boat", "acacia chest boat", "dark oak chest boat"); + if (IS_RUNNING_1_21_3) { + typeToClassMap.put(TreeSpecies.GENERIC, OakChestBoat.class); + typeToClassMap.put(TreeSpecies.REDWOOD, SpruceChestBoat.class); + typeToClassMap.put(TreeSpecies.BIRCH, BirchChestBoat.class); + typeToClassMap.put(TreeSpecies.JUNGLE, JungleChestBoat.class); + typeToClassMap.put(TreeSpecies.ACACIA, AcaciaChestBoat.class); + typeToClassMap.put(TreeSpecies.DARK_OAK, DarkOakChestBoat.class); + } } } @@ -80,6 +96,8 @@ protected boolean match(ChestBoat entity) { @Override public Class getType() { + if (IS_RUNNING_1_21_3) + return typeToClassMap.get(TreeSpecies.values()[matchedPattern - 2]); return ChestBoat.class; } diff --git a/src/main/java/ch/njol/skript/entity/BoatData.java b/src/main/java/ch/njol/skript/entity/BoatData.java index 7d4a88c5930..22b8d58b251 100644 --- a/src/main/java/ch/njol/skript/entity/BoatData.java +++ b/src/main/java/ch/njol/skript/entity/BoatData.java @@ -18,30 +18,43 @@ */ package ch.njol.skript.entity; -import java.lang.reflect.Method; -import java.util.Random; - +import ch.njol.skript.Skript; +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptParser.ParseResult; import org.bukkit.Material; import org.bukkit.TreeSpecies; import org.bukkit.entity.Boat; -import org.bukkit.inventory.ItemStack; +import org.bukkit.entity.boat.AcaciaBoat; +import org.bukkit.entity.boat.BirchBoat; +import org.bukkit.entity.boat.DarkOakBoat; +import org.bukkit.entity.boat.JungleBoat; +import org.bukkit.entity.boat.OakBoat; +import org.bukkit.entity.boat.SpruceBoat; import org.jetbrains.annotations.Nullable; -import ch.njol.skript.Skript; -import ch.njol.skript.aliases.Aliases; -import ch.njol.skript.aliases.ItemType; -import ch.njol.skript.lang.Literal; -import ch.njol.skript.lang.SkriptParser.ParseResult; +import java.util.EnumMap; +import java.util.Random; public class BoatData extends EntityData { + + private static final EnumMap> typeToClassMap = new EnumMap<>(TreeSpecies.class); + private static final boolean IS_RUNNING_1_21_3 = Skript.isRunningMinecraft(1, 21, 3); + static { - // It will only register for 1.10+, - // See SimpleEntityData if 1.9 or lower. - if (Skript.methodExists(Boat.class, "getWoodType")) { //The 'boat' is the same of 'oak boat', 'any boat' works as supertype and it can spawn random boat. - EntityData.register(BoatData.class, "boat", Boat.class, 0, - "boat", "any boat", "oak boat", "spruce boat", "birch boat", "jungle boat", "acacia boat", "dark oak boat"); + EntityData.register(BoatData.class, "boat", Boat.class, 0, + "boat", "any boat", "oak boat", "spruce boat", "birch boat", "jungle boat", "acacia boat", "dark oak boat"); + if (IS_RUNNING_1_21_3) { + typeToClassMap.put(TreeSpecies.GENERIC, OakBoat.class); + typeToClassMap.put(TreeSpecies.REDWOOD, SpruceBoat.class); + typeToClassMap.put(TreeSpecies.BIRCH, BirchBoat.class); + typeToClassMap.put(TreeSpecies.JUNGLE, JungleBoat.class); + typeToClassMap.put(TreeSpecies.ACACIA, AcaciaBoat.class); + typeToClassMap.put(TreeSpecies.DARK_OAK, DarkOakBoat.class); } } + + public BoatData(){ this(0); @@ -83,6 +96,8 @@ protected boolean match(Boat entity) { @Override public Class getType() { + if (IS_RUNNING_1_21_3) + return typeToClassMap.get(TreeSpecies.values()[matchedPattern - 2]); return Boat.class; } @@ -129,4 +144,5 @@ else if (type == Material.DARK_OAK_BOAT) return hashCode_i() == ordinal + 2 || (matchedPattern + ordinal == 0) || ordinal == 0; } + } diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index fec454c2ac5..61cdfe3f909 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -274,6 +274,8 @@ biomes: deep_dark: deep dark # 1.19.4 cherry_grove: cherry grove + # 1.21.3 + pale_garden: pale garden # -- Tree Types -- tree types: @@ -2236,6 +2238,39 @@ attribute types: player.submerged_mining_speed: player submerged mining speed, submerged mining speed player.sweeping_damage_ratio: player sweeping damage ratio, sweeping damage ratio zombie.spawn_reinforcements: zombie spawn reinforcements + # 1.21.3 removes prefixes, adds tempt range + armor: generic armor, armor + armor_toughness: generic armor toughness, armor toughness + attack_damage: generic attack damage, attack damage + attack_knockback: generic attack knockback, attack knockback + attack_speed: generic attack speed, attack speed + burning_time: generic burning time, burning time + explosion_knockback_resistance: generic explosion knockback resistance, explosion knockback resistance + flying_speed: generic flying speed, flying speed + follow_range: generic follow range, follow range + gravity: generic gravity, gravity + jump_strength: generic jump strength, jump strength, horse jump strength + knockback_resistance: generic knockback resistance, knockback resistance + luck: generic luck, luck + max_absorption: generic max absorption, max absorption + max_health: generic max health, max health + movement_efficiency: generic movement efficiency, movement efficiency + movement_speed: generic movement speed, movement speed + oxygen_bonus: generic oxygen bonus, oxygen bonus + safe_fall_distance: generic safe fall distance, safe fall distance + fall_damage_multiplier: generic fall damage multiplier, fall damage multiplier + scale: generic scale, scale + step_height: generic step height, step height + tempt_range: generic tempt range, tempt range + water_movement_efficiency: generic water movement efficiency, water movement efficiency + block_break_speed: player block break speed, block break speed + block_interaction_range: player block interaction range, block interaction range + entity_interaction_range: player entity interaction range, entity interaction range + mining_efficiency: player mining efficiency, mining efficiency + sneaking_speed: player sneaking speed, sneaking speed + submerged_mining_speed: player submerged mining speed, submerged mining speed + sweeping_damage_ratio: player sweeping damage ratio, sweeping damage ratio + spawn_reinforcements: zombie spawn reinforcements # -- Environments -- environments: diff --git a/src/test/skript/environments/java21/paper-1.21.0.json b/src/test/skript/environments/java21/paper-1.21.3.json similarity index 85% rename from src/test/skript/environments/java21/paper-1.21.0.json rename to src/test/skript/environments/java21/paper-1.21.3.json index 22a72e3a73b..ace6be5d212 100644 --- a/src/test/skript/environments/java21/paper-1.21.0.json +++ b/src/test/skript/environments/java21/paper-1.21.3.json @@ -1,11 +1,11 @@ { - "name": "paper-1.21.0", + "name": "paper-1.21.3", "resources": [ {"source": "server.properties.generic", "target": "server.properties"} ], "paperDownloads": [ { - "version": "1.21", + "version": "1.21.3", "target": "paperclip.jar" } ], diff --git a/src/test/skript/tests/syntaxes/effects/EffHealth.sk b/src/test/skript/tests/syntaxes/effects/EffHealth.sk index b77c566076b..719522ed0a6 100644 --- a/src/test/skript/tests/syntaxes/effects/EffHealth.sk +++ b/src/test/skript/tests/syntaxes/effects/EffHealth.sk @@ -10,14 +10,12 @@ test "health effect": spawn cow at location(0, 64, 0, world "world") set {_m} to last spawned cow assert health of {_m} is 5 with "default cow health failed" - damage {_m} by 0.5 - assert health of {_m} is 4.5 with "damage cow failed" - damage {_m} by 99 - assert health of {_m} is 0 with "damage cow failed" + damage {_m} by 3 + assert health of {_m} is 2 with "damage cow failed" heal {_m} by 1 - assert health of {_m} is 1 with "heal cow failed" + assert health of {_m} is 3 with "heal cow failed" heal {_m} by 0.5 - assert health of {_m} is 1.5 with "heal cow failed" + assert health of {_m} is 3.5 with "heal cow failed" heal {_m} by 99 assert health of {_m} is 5 with "heal cow failed" clear all entities From 031c660583b1e316209ad8fd66c5673bfd6b1216 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:45:49 -0400 Subject: [PATCH 11/18] More 1.21.3 Support (#7176) --- .../njol/skript/bukkitutil/HealthUtils.java | 31 ++-- .../skript/classes/data/BukkitClasses.java | 79 +++++----- .../njol/skript/entity/SimpleEntityData.java | 141 ++---------------- src/main/resources/lang/default.lang | 14 +- 4 files changed, 73 insertions(+), 192 deletions(-) diff --git a/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java b/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java index be8675ccabb..aae3224b409 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java @@ -1,21 +1,3 @@ -/** - * 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.bukkitutil; import ch.njol.skript.Skript; @@ -36,6 +18,15 @@ public class HealthUtils { + private static final Attribute MAX_HEALTH; + static { + if (Skript.isRunningMinecraft(1, 21, 3)) { // In 1.21.3, Attribute became an Interface + MAX_HEALTH = Attribute.valueOf("MAX_HEALTH"); + } else { + MAX_HEALTH = (Attribute) Enum.valueOf((Class) Attribute.class, "GENERIC_MAX_HEALTH"); + } + } + /** * Get the health of an entity * @param e Entity to get health from @@ -62,7 +53,7 @@ public static void setHealth(Damageable e, double health) { * @return How many hearts the entity can have at most */ public static double getMaxHealth(Damageable e) { - AttributeInstance attributeInstance = ((Attributable) e).getAttribute(Attribute.GENERIC_MAX_HEALTH); + AttributeInstance attributeInstance = ((Attributable) e).getAttribute(MAX_HEALTH); assert attributeInstance != null; return attributeInstance.getValue() / 2; } @@ -73,7 +64,7 @@ public static double getMaxHealth(Damageable e) { * @param health How many hearts the entity can have at most */ public static void setMaxHealth(Damageable e, double health) { - AttributeInstance attributeInstance = ((Attributable) e).getAttribute(Attribute.GENERIC_MAX_HEALTH); + AttributeInstance attributeInstance = ((Attributable) e).getAttribute(MAX_HEALTH); assert attributeInstance != null; attributeInstance.setBaseValue(health * 2); } 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 ba0a66a1f41..98d072893ce 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -18,18 +18,32 @@ */ 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; -import java.util.Map.Entry; -import java.util.UUID; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - +import ch.njol.skript.Skript; +import ch.njol.skript.SkriptConfig; +import ch.njol.skript.aliases.Aliases; +import ch.njol.skript.aliases.ItemType; import ch.njol.skript.bukkitutil.BukkitUtils; +import ch.njol.skript.bukkitutil.EnchantmentUtils; +import ch.njol.skript.bukkitutil.ItemUtils; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.ConfigurationSerializer; +import ch.njol.skript.classes.EnumClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.classes.Serializer; +import ch.njol.skript.classes.registry.RegistryClassInfo; +import ch.njol.skript.entity.EntityData; +import ch.njol.skript.expressions.ExprDamageCause; +import ch.njol.skript.expressions.base.EventValueExpression; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.lang.util.SimpleLiteral; +import ch.njol.skript.localization.Language; +import ch.njol.skript.registrations.Classes; +import ch.njol.skript.util.BlockUtils; +import ch.njol.skript.util.PotionEffectUtils; +import ch.njol.skript.util.StringMode; +import ch.njol.util.StringUtils; +import ch.njol.yggdrasil.Fields; +import io.papermc.paper.world.MoonPhase; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Difficulty; @@ -79,34 +93,19 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.CachedServerIcon; import org.bukkit.util.Vector; - -import ch.njol.skript.Skript; -import ch.njol.skript.SkriptConfig; -import ch.njol.skript.aliases.Aliases; -import ch.njol.skript.aliases.ItemType; -import ch.njol.skript.bukkitutil.EnchantmentUtils; -import ch.njol.skript.bukkitutil.ItemUtils; -import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.classes.ConfigurationSerializer; -import ch.njol.skript.classes.EnumClassInfo; -import ch.njol.skript.classes.Parser; -import ch.njol.skript.classes.Serializer; -import ch.njol.skript.classes.registry.RegistryClassInfo; -import ch.njol.skript.entity.EntityData; -import ch.njol.skript.expressions.ExprDamageCause; -import ch.njol.skript.expressions.base.EventValueExpression; -import ch.njol.skript.lang.ParseContext; -import ch.njol.skript.lang.util.SimpleLiteral; -import ch.njol.skript.localization.Language; -import ch.njol.skript.registrations.Classes; -import ch.njol.skript.util.BlockUtils; -import ch.njol.skript.util.PotionEffectUtils; -import ch.njol.skript.util.StringMode; -import ch.njol.util.StringUtils; -import ch.njol.yggdrasil.Fields; -import io.papermc.paper.world.MoonPhase; import org.jetbrains.annotations.Nullable; +import java.io.StreamCorruptedException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + /** * @author Peter Güttinger */ @@ -987,7 +986,8 @@ public String toVariableNameString(final ItemStack i) { if (BukkitUtils.registryExists("BIOME")) { biomeClassInfo = new RegistryClassInfo<>(Biome.class, Registry.BIOME, "biome", "biomes"); } else { - biomeClassInfo = new EnumClassInfo<>(Biome.class, "biome", "biomes"); + //noinspection rawtypes,unchecked + biomeClassInfo = new EnumClassInfo<>((Class) Biome.class, "biome", "biomes"); } Classes.registerClass(biomeClassInfo .user("biomes?") @@ -1481,7 +1481,8 @@ public String toVariableNameString(EnchantmentOffer eo) { if (BukkitUtils.registryExists("ATTRIBUTE")) { attributeClassInfo = new RegistryClassInfo<>(Attribute.class, Registry.ATTRIBUTE, "attributetype", "attribute types"); } else { - attributeClassInfo = new EnumClassInfo<>(Attribute.class, "attributetype", "attribute types"); + //noinspection rawtypes,unchecked + attributeClassInfo = new EnumClassInfo<>((Class) Attribute.class, "attributetype", "attribute types"); } Classes.registerClass(attributeClassInfo .user("attribute ?types?") diff --git a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java index 24b8620a608..7b6c48c3001 100644 --- a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java +++ b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java @@ -18,138 +18,19 @@ */ package ch.njol.skript.entity; -import java.io.NotSerializableException; -import java.io.StreamCorruptedException; -import java.util.ArrayList; -import java.util.List; - -import ch.njol.util.Kleenean; -import org.bukkit.World; -import org.bukkit.entity.AbstractHorse; -import org.bukkit.entity.Allay; -import org.bukkit.entity.Animals; -import org.bukkit.entity.AreaEffectCloud; -import org.bukkit.entity.Armadillo; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.Arrow; -import org.bukkit.entity.Bat; -import org.bukkit.entity.Blaze; -import org.bukkit.entity.BlockDisplay; -import org.bukkit.entity.Bogged; -import org.bukkit.entity.Breeze; -import org.bukkit.entity.Camel; -import org.bukkit.entity.CaveSpider; -import org.bukkit.entity.ChestedHorse; -import org.bukkit.entity.Chicken; -import org.bukkit.entity.Cod; -import org.bukkit.entity.Cow; -import org.bukkit.entity.Creature; -import org.bukkit.entity.Damageable; -import org.bukkit.entity.Display; -import org.bukkit.entity.Dolphin; -import org.bukkit.entity.Donkey; -import org.bukkit.entity.DragonFireball; -import org.bukkit.entity.Drowned; -import org.bukkit.entity.Egg; -import org.bukkit.entity.ElderGuardian; -import org.bukkit.entity.EnderCrystal; -import org.bukkit.entity.EnderDragon; -import org.bukkit.entity.EnderPearl; -import org.bukkit.entity.EnderSignal; -import org.bukkit.entity.Endermite; -import org.bukkit.entity.Enemy; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Evoker; -import org.bukkit.entity.EvokerFangs; -import org.bukkit.entity.Fireball; -import org.bukkit.entity.Firework; -import org.bukkit.entity.Fish; -import org.bukkit.entity.FishHook; -import org.bukkit.entity.Ghast; -import org.bukkit.entity.Giant; -import org.bukkit.entity.GlowItemFrame; -import org.bukkit.entity.GlowSquid; -import org.bukkit.entity.Golem; -import org.bukkit.entity.Guardian; -import org.bukkit.entity.Hoglin; -import org.bukkit.entity.Horse; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Husk; -import org.bukkit.entity.Illager; -import org.bukkit.entity.Illusioner; -import org.bukkit.entity.Interaction; -import org.bukkit.entity.IronGolem; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.LargeFireball; -import org.bukkit.entity.LeashHitch; -import org.bukkit.entity.LightningStrike; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Llama; -import org.bukkit.entity.LlamaSpit; -import org.bukkit.entity.MagmaCube; -import org.bukkit.entity.Marker; -import org.bukkit.entity.Mob; -import org.bukkit.entity.Monster; -import org.bukkit.entity.Mule; -import org.bukkit.entity.MushroomCow; -import org.bukkit.entity.Painting; -import org.bukkit.entity.Phantom; -import org.bukkit.entity.PigZombie; -import org.bukkit.entity.Piglin; -import org.bukkit.entity.PiglinBrute; -import org.bukkit.entity.Pillager; -import org.bukkit.entity.Player; -import org.bukkit.entity.PolarBear; -import org.bukkit.entity.Projectile; -import org.bukkit.entity.PufferFish; -import org.bukkit.entity.Raider; -import org.bukkit.entity.Ravager; -import org.bukkit.entity.Salmon; -import org.bukkit.entity.Shulker; -import org.bukkit.entity.ShulkerBullet; -import org.bukkit.entity.Silverfish; -import org.bukkit.entity.Skeleton; -import org.bukkit.entity.SkeletonHorse; -import org.bukkit.entity.Slime; -import org.bukkit.entity.SmallFireball; -import org.bukkit.entity.Sniffer; -import org.bukkit.entity.Snowball; -import org.bukkit.entity.Snowman; -import org.bukkit.entity.SpectralArrow; -import org.bukkit.entity.Spellcaster; -import org.bukkit.entity.Spider; -import org.bukkit.entity.Squid; -import org.bukkit.entity.Stray; -import org.bukkit.entity.Strider; -import org.bukkit.entity.TNTPrimed; -import org.bukkit.entity.Tadpole; -import org.bukkit.entity.TextDisplay; -import org.bukkit.entity.ThrownExpBottle; -import org.bukkit.entity.TippedArrow; -import org.bukkit.entity.Trident; -import org.bukkit.entity.TropicalFish; -import org.bukkit.entity.Turtle; -import org.bukkit.entity.Vex; -import org.bukkit.entity.Vindicator; -import org.bukkit.entity.WanderingTrader; -import org.bukkit.entity.Warden; -import org.bukkit.entity.WaterMob; -import org.bukkit.entity.WindCharge; -import org.bukkit.entity.Witch; -import org.bukkit.entity.Wither; -import org.bukkit.entity.WitherSkeleton; -import org.bukkit.entity.WitherSkull; -import org.bukkit.entity.Zoglin; -import org.bukkit.entity.Zombie; -import org.bukkit.entity.ZombieHorse; - -import org.jetbrains.annotations.Nullable; - import ch.njol.skript.Skript; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; import ch.njol.yggdrasil.Fields; +import org.bukkit.World; +import org.bukkit.entity.*; +import org.jetbrains.annotations.Nullable; + +import java.io.NotSerializableException; +import java.io.StreamCorruptedException; +import java.util.ArrayList; +import java.util.List; public class SimpleEntityData extends EntityData { @@ -333,6 +214,10 @@ private static void addSuperEntity(String codeName, Class enti addSimpleEntity("bogged", Bogged.class); } + if (Skript.isRunningMinecraft(1,21,3)) { + addSimpleEntity("creaking", Creaking.class); + } + // Register zombie after Husk and Drowned to make sure both work addSimpleEntity("zombie", Zombie.class); // Register squid after glow squid to make sure both work diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 61cdfe3f909..4ae3049a85a 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -722,7 +722,7 @@ entities: pattern: spider(|1¦s) squid: name: squid¦s - pattern: squid(|1¦s) + pattern: squid(|1¦s) bottle of enchanting: name: bottle¦ of enchanting¦s of enchanting pattern: [thrown] bottle(|1¦s) o(f|') enchanting|[e]xp[erience] bottle(|1¦s) @@ -1072,7 +1072,7 @@ entities: pattern: drowned(|1¦s)|(4¦)drowned (kid(|1¦s)|child(|1¦ren)) dolphin: name: dolphin¦s - pattern: dolphin(|1¦s) + pattern: dolphin(|1¦s) phantom: name: phantom¦s pattern: phantom(|1¦s) @@ -1158,7 +1158,7 @@ entities: pattern: quiet goat(|1¦s)|(4¦)quiet goat (kid(|1¦s)|child(|1¦ren)) glow squid: name: glow squid¦s - pattern: glow squid(|1¦s) + pattern: glow squid(|1¦s) axolotl: name: axolotl¦s @an pattern: axolotl(|1¦s)|(4¦)axolotl (kid(|1¦s)|child(|1¦ren)) @@ -1267,8 +1267,12 @@ entities: name: armadillo¦s @an pattern: armadillo(|1¦s)|(4¦)armadillo (kid(|1¦s)|child(|1¦ren)) bogged: - name: bogged - pattern: bogged + name: bogged¦s + pattern: bogged[1:s] + # 1.21.3 Entities + creaking: + name: creaking¦s + pattern: creaking[1:s] # -- Heal Reasons -- heal reasons: From d82667540e5b8381456d87df0a6485c03517bddf Mon Sep 17 00:00:00 2001 From: APickledWalrus Date: Fri, 1 Nov 2024 16:52:40 -0400 Subject: [PATCH 12/18] Prepare For Release (2.9.4) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 706418cbd62..f6647e60f22 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.parallel=true groupid=ch.njol name=skript -version=2.9.3 +version=2.9.4 jarName=Skript.jar testEnv=java21/paper-1.21.3 testEnvJavaVersion=21 From 1fb7e67b9b03b2bb689f721e303ea012a272f34d Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Sun, 3 Nov 2024 18:37:02 +0300 Subject: [PATCH 13/18] Fix the time states expression not accepting plural expressions (#7188) Fix ExprTimeStates not accepting plural expressions --- .../skript/expressions/ExprTimeState.java | 80 ++++++++----------- 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/ExprTimeState.java b/src/main/java/ch/njol/skript/expressions/ExprTimeState.java index 3512c001062..06eed32440e 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprTimeState.java +++ b/src/main/java/ch/njol/skript/expressions/ExprTimeState.java @@ -1,23 +1,6 @@ -/** - * 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.registrations.EventValues; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -30,56 +13,57 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.skript.log.ErrorQuality; import ch.njol.util.Kleenean; -/** - * @author Peter Güttinger - */ @Name("Former/Future State") -@Description({"Represents the value of an expression before an event happened or the value it will have directly after the event, e.g. the old or new level respectively in a level change event.", - "Note: The past, future and present states of an expression are sometimes called 'time states' of an expression.", - "Note 2: If you don't specify whether to use the past or future state of an expression that has different values, its default value will be used which is usually the value after the event."}) -@Examples({"on teleport:", - " former world was \"world_nether\" # or 'world was'", - " world will be \"world\" # or 'world after the event is'", - "on tool change:", - " past tool is an axe", - " the tool after the event will be air", - "on weather change:", - " set {weather::%world%::old} to past weather", - " set {weather::%world%::current} to the new weather"}) +@Description({ + "Represents the value of an expression before an event happened or the value it will have directly after the event, e.g. the old or new level respectively in a level change event.", + "Note: The past, future and present states of an expression are sometimes called 'time states' of an expression.", + "Note 2: If you don't specify whether to use the past or future state of an expression that has different values, its default value will be used which is usually the value after the event." +}) +@Examples({ + "on teleport:", + "\tformer world was \"world_nether\" # or 'world was'", + "\tworld will be \"world\" # or 'world after the event is'", + "on tool change:", + "\tpast tool is an axe", + "\tthe tool after the event will be air", + "on weather change:", + "\tset {weather::%world%::old} to past weather", + "\tset {weather::%world%::current} to the new weather" +}) @Since("1.1") public class ExprTimeState extends WrapperExpression { + static { Skript.registerExpression(ExprTimeState.class, Object.class, ExpressionType.PROPERTY, - "[the] (former|past|old) [state] [of] %~object%", "%~object% before [the event]", - "[the] (future|to-be|new) [state] [of] %~object%", "%~object%(-to-be| after[(wards| the event)])"); + "[the] (former|past|old) [state] [of] %~objects%", "%~objects% before [the event]", + "[the] (future|to-be|new) [state] [of] %~objects%", "%~objects%(-to-be| after[(wards| the event)])"); } - + @Override - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { - final Expression expr = exprs[0]; + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + Expression expr = expressions[0]; if (isDelayed == Kleenean.TRUE) { - Skript.error("Cannot use time states after the event has already passed", ErrorQuality.SEMANTIC_ERROR); + Skript.error("Cannot use time states after the event has already passed"); return false; } - if (!expr.setTime(matchedPattern >= 2 ? 1 : -1)) { - Skript.error(expr + " does not have a " + (matchedPattern >= 2 ? "future" : "past") + " state", ErrorQuality.SEMANTIC_ERROR); + if (!expr.setTime(matchedPattern >= 2 ? EventValues.TIME_FUTURE : EventValues.TIME_PAST)) { + Skript.error(expr + " does not have a " + (matchedPattern >= 2 ? "future" : "past") + " state"); return false; } setExpr(expr); return true; } - + @Override - public String toString(final @Nullable Event e, final boolean debug) { - return "the " + (getTime() == -1 ? "past" : "future") + " state of " + getExpr().toString(e, debug); + public String toString(@Nullable Event event, boolean debug) { + return "the " + (getTime() == EventValues.TIME_PAST ? "past" : "future") + " state of " + getExpr().toString(event, debug); } - + @Override - public boolean setTime(final int time) { + public boolean setTime(int time) { return time == getTime(); } - + } From 00650def022da2392ca037634a4d6fbc3f289093 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Fri, 8 Nov 2024 12:39:10 -0800 Subject: [PATCH 14/18] SoundUtils - handle Sound class for enum/interface (#7199) * SoundUtils - handle Sound class for enum/interface * SoundUtils - fix an error with MC 1.15 * EffPlaySound.sk - add simple test to make sure syntax doesn't throw error * EffPlaySound - remove reflection and remove to effect class * EffPlaySound - some changes * EffPlaySound.sk - fix tests failing --- .../ch/njol/skript/effects/EffPlaySound.java | 31 ++++++++++-- .../java/ch/njol/skript/util/SoundUtils.java | 49 ------------------- .../tests/syntaxes/effects/EffPlaySound.sk | 3 ++ 3 files changed, 29 insertions(+), 54 deletions(-) delete mode 100644 src/main/java/ch/njol/skript/util/SoundUtils.java create mode 100644 src/test/skript/tests/syntaxes/effects/EffPlaySound.sk diff --git a/src/main/java/ch/njol/skript/effects/EffPlaySound.java b/src/main/java/ch/njol/skript/effects/EffPlaySound.java index 2846d616f06..b0a79a39033 100644 --- a/src/main/java/ch/njol/skript/effects/EffPlaySound.java +++ b/src/main/java/ch/njol/skript/effects/EffPlaySound.java @@ -11,6 +11,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import org.bukkit.Keyed; import org.bukkit.Location; import org.bukkit.NamespacedKey; import org.bukkit.Sound; @@ -72,6 +73,7 @@ public class EffPlaySound extends Effect { private static final boolean ENTITY_EMITTER_SOUND = Skript.methodExists(Player.class, "playSound", Entity.class, Sound.class, SoundCategory.class, float.class, float.class); private static final boolean ENTITY_EMITTER_STRING = Skript.methodExists(Player.class, "playSound", Entity.class, String.class, SoundCategory.class, float.class, float.class); private static final boolean ENTITY_EMITTER = ENTITY_EMITTER_SOUND || ENTITY_EMITTER_STRING; + private static final boolean SOUND_IS_INTERFACE = Sound.class.isInterface(); public static final Pattern KEY_PATTERN = Pattern.compile("([a-z0-9._-]+:)?([a-z0-9/._-]+)"); @@ -149,11 +151,8 @@ protected void execute(Event event) { // validate strings List validSounds = new ArrayList<>(); for (String sound : sounds.getArray(event)) { - NamespacedKey key = null; - try { - Sound enumSound = Sound.valueOf(sound.toUpperCase(Locale.ENGLISH)); - key = enumSound.getKey(); - } catch (IllegalArgumentException alternative) { + NamespacedKey key = getSoundKeyFromEnum(sound); + if (key == null) { sound = sound.toLowerCase(Locale.ENGLISH); Matcher keyMatcher = KEY_PATTERN.matcher(sound); if (!keyMatcher.matches()) @@ -240,4 +239,26 @@ public String toString(@Nullable Event event, boolean debug) { return builder.toString(); } + @SuppressWarnings({"deprecation", "unchecked", "rawtypes"}) + private static @Nullable NamespacedKey getSoundKeyFromEnum(String soundString) { + soundString = soundString.toUpperCase(Locale.ENGLISH); + // Sound.class is an Interface (rather than an enum) as of MC 1.21.3 + if (SOUND_IS_INTERFACE) { + try { + Sound sound = Sound.valueOf(soundString); + return sound.getKey(); + } catch (IllegalArgumentException ignore) { + } + } else { + try { + Enum soundEnum = Enum.valueOf((Class) Sound.class, soundString); + if (soundEnum instanceof Keyed) { + return ((Keyed) soundEnum).getKey(); + } + } catch (IllegalArgumentException ignore) { + } + } + return null; + } + } diff --git a/src/main/java/ch/njol/skript/util/SoundUtils.java b/src/main/java/ch/njol/skript/util/SoundUtils.java deleted file mode 100644 index bbf18e2ea1c..00000000000 --- a/src/main/java/ch/njol/skript/util/SoundUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * 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.util; - -import org.bukkit.Sound; -import org.jetbrains.annotations.Nullable; - -/** - * @author Peter Güttinger - */ -public abstract class SoundUtils { - private SoundUtils() {} - - static { - assert false; - } - - private final static EnumUtils util = new EnumUtils<>(Sound.class, "sounds"); - - @Nullable - public static Sound parse(final String s) { - return util.parse(s); - } - - public static String toString(final Sound s, final int flags) { - return util.toString(s, flags); - } - - public static String getAllNames() { - return util.getAllNames(); - } - -} diff --git a/src/test/skript/tests/syntaxes/effects/EffPlaySound.sk b/src/test/skript/tests/syntaxes/effects/EffPlaySound.sk new file mode 100644 index 00000000000..5cd27437d00 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffPlaySound.sk @@ -0,0 +1,3 @@ +test "play sound effect": + play sound "block.stone.break" with volume 1 at spawn of world "world" + play sound "BLOCK_STONE_BREAK" with volume 1 at spawn of world "world" From 95536786c2b5af8c8ac92ec8e4a1f9d7a93f953b Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 9 Nov 2024 15:33:57 -0500 Subject: [PATCH 15/18] Fix sound errors from API changes (#7205) --- .../ch/njol/skript/bukkitutil/SoundUtils.java | 57 +++++++++++++++++++ .../ch/njol/skript/effects/EffPlaySound.java | 55 +++++------------- .../skript/expressions/ExprBlockSound.java | 5 +- .../skript/expressions/ExprEntitySound.java | 3 +- .../syntaxes/expressions/ExprBlockSound.sk | 34 +++++------ .../syntaxes/expressions/ExprEntitySound.sk | 28 ++++----- 6 files changed, 105 insertions(+), 77 deletions(-) create mode 100644 src/main/java/ch/njol/skript/bukkitutil/SoundUtils.java diff --git a/src/main/java/ch/njol/skript/bukkitutil/SoundUtils.java b/src/main/java/ch/njol/skript/bukkitutil/SoundUtils.java new file mode 100644 index 00000000000..36ad5136d9a --- /dev/null +++ b/src/main/java/ch/njol/skript/bukkitutil/SoundUtils.java @@ -0,0 +1,57 @@ +package ch.njol.skript.bukkitutil; + +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Sound; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Locale; + +/** + * Utility class for working with sounds. + */ +public final class SoundUtils { + + // Sound.class is an interface (rather than an enum) as of MC 1.21.3 + private static final boolean SOUND_IS_INTERFACE = Sound.class.isInterface(); + + /** + * Gets the key of a sound, given its enum-name-style name. + * @param soundString The enum name to use to find the sound. + * @return The key of the sound. + */ + public static @Nullable NamespacedKey getKey(String soundString) { + soundString = soundString.toUpperCase(Locale.ENGLISH); + if (SOUND_IS_INTERFACE) { + try { + //noinspection deprecation + return Sound.valueOf(soundString).getKey(); + } catch (IllegalArgumentException ignore) { + } + } else { + try { + //noinspection unchecked,rawtypes + Enum soundEnum = Enum.valueOf((Class) Sound.class, soundString); + return ((Keyed) soundEnum).getKey(); + } catch (IllegalArgumentException ignore) { + } + } + return null; + } + + /** + * returns the key string for a sound. For version compat. + * @param sound The sound to get the key string of. + * @return The key string of the {@link NamespacedKey} of the sound. + */ + public static @NotNull NamespacedKey getKey(Sound sound) { + if (SOUND_IS_INTERFACE) { + //noinspection deprecation + return sound.getKey(); + } else { + return ((Keyed) sound).getKey(); + } + } + +} diff --git a/src/main/java/ch/njol/skript/effects/EffPlaySound.java b/src/main/java/ch/njol/skript/effects/EffPlaySound.java index b0a79a39033..c67a86adde3 100644 --- a/src/main/java/ch/njol/skript/effects/EffPlaySound.java +++ b/src/main/java/ch/njol/skript/effects/EffPlaySound.java @@ -1,6 +1,7 @@ package ch.njol.skript.effects; import ch.njol.skript.Skript; +import ch.njol.skript.bukkitutil.SoundUtils; import ch.njol.skript.bukkitutil.sounds.SoundReceiver; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; @@ -11,7 +12,6 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; -import org.bukkit.Keyed; import org.bukkit.Location; import org.bukkit.NamespacedKey; import org.bukkit.Sound; @@ -73,7 +73,6 @@ public class EffPlaySound extends Effect { private static final boolean ENTITY_EMITTER_SOUND = Skript.methodExists(Player.class, "playSound", Entity.class, Sound.class, SoundCategory.class, float.class, float.class); private static final boolean ENTITY_EMITTER_STRING = Skript.methodExists(Player.class, "playSound", Entity.class, String.class, SoundCategory.class, float.class, float.class); private static final boolean ENTITY_EMITTER = ENTITY_EMITTER_SOUND || ENTITY_EMITTER_STRING; - private static final boolean SOUND_IS_INTERFACE = Sound.class.isInterface(); public static final Pattern KEY_PATTERN = Pattern.compile("([a-z0-9._-]+:)?([a-z0-9/._-]+)"); @@ -90,26 +89,20 @@ public class EffPlaySound extends Effect { ); } - @SuppressWarnings("NotNullFieldNotInitialized") private Expression sounds; - @Nullable - private Expression category; - @Nullable - private Expression players; + private @Nullable Expression category; - @Nullable - private Expression volume; + private @Nullable Expression players; - @Nullable - private Expression pitch; + private @Nullable Expression volume; - @Nullable - private Expression seed; + private @Nullable Expression pitch; - @Nullable - private Expression emitters; + private @Nullable Expression seed; + + private @Nullable Expression emitters; @Override @SuppressWarnings("unchecked") @@ -151,7 +144,7 @@ protected void execute(Event event) { // validate strings List validSounds = new ArrayList<>(); for (String sound : sounds.getArray(event)) { - NamespacedKey key = getSoundKeyFromEnum(sound); + NamespacedKey key = SoundUtils.getKey(sound); if (key == null) { sound = sound.toLowerCase(Locale.ENGLISH); Matcher keyMatcher = KEY_PATTERN.matcher(sound); @@ -204,12 +197,12 @@ protected void execute(Event event) { } } else if (emitters != null) { for (Object emitter : emitters.getArray(event)) { - if (ENTITY_EMITTER && emitter instanceof Entity) { - SoundReceiver receiver = SoundReceiver.of(((Entity) emitter).getWorld()); + if (ENTITY_EMITTER && emitter instanceof Entity entity) { + SoundReceiver receiver = SoundReceiver.of(entity.getWorld()); for (NamespacedKey sound : validSounds) receiver.playSound(((Entity) emitter), sound, category, volume, pitch, seed); - } else if (emitter instanceof Location) { - SoundReceiver receiver = SoundReceiver.of(((Location) emitter).getWorld()); + } else if (emitter instanceof Location location) { + SoundReceiver receiver = SoundReceiver.of(location.getWorld()); for (NamespacedKey sound : validSounds) receiver.playSound(((Location) emitter), sound, category, volume, pitch, seed); } @@ -239,26 +232,4 @@ public String toString(@Nullable Event event, boolean debug) { return builder.toString(); } - @SuppressWarnings({"deprecation", "unchecked", "rawtypes"}) - private static @Nullable NamespacedKey getSoundKeyFromEnum(String soundString) { - soundString = soundString.toUpperCase(Locale.ENGLISH); - // Sound.class is an Interface (rather than an enum) as of MC 1.21.3 - if (SOUND_IS_INTERFACE) { - try { - Sound sound = Sound.valueOf(soundString); - return sound.getKey(); - } catch (IllegalArgumentException ignore) { - } - } else { - try { - Enum soundEnum = Enum.valueOf((Class) Sound.class, soundString); - if (soundEnum instanceof Keyed) { - return ((Keyed) soundEnum).getKey(); - } - } catch (IllegalArgumentException ignore) { - } - } - return null; - } - } diff --git a/src/main/java/ch/njol/skript/expressions/ExprBlockSound.java b/src/main/java/ch/njol/skript/expressions/ExprBlockSound.java index ef207692089..d946238b513 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprBlockSound.java +++ b/src/main/java/ch/njol/skript/expressions/ExprBlockSound.java @@ -1,6 +1,7 @@ package ch.njol.skript.expressions; import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.bukkitutil.SoundUtils; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; @@ -78,9 +79,7 @@ public Sound getSound(SoundGroup group) { SimplePropertyExpression.register(ExprBlockSound.class, String.class, "(1:break|2:fall|3:hit|4:place|5:step) sound[s]", "blocks/blockdatas/itemtypes"); } - @SuppressWarnings("NotNullFieldNotInitialized") private SoundType soundType; - @SuppressWarnings("NotNullFieldNotInitialized") private Expression objects; @Override @@ -96,7 +95,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye .map(this::convertAndGetSound) .filter(Objects::nonNull) .distinct() - .map(Sound::name) + .map(sound -> SoundUtils.getKey(sound).getKey()) .toArray(String[]::new); } diff --git a/src/main/java/ch/njol/skript/expressions/ExprEntitySound.java b/src/main/java/ch/njol/skript/expressions/ExprEntitySound.java index 4dc05895640..f1327963617 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprEntitySound.java +++ b/src/main/java/ch/njol/skript/expressions/ExprEntitySound.java @@ -2,6 +2,7 @@ import ch.njol.skript.Skript; import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.bukkitutil.SoundUtils; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; @@ -150,7 +151,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye .map(entity -> soundType.getSound(entity, height, item, bigOrSpeedy)) .filter(Objects::nonNull) .distinct() - .map(Sound::name) + .map(sound -> SoundUtils.getKey(sound).getKey()) .toArray(String[]::new); } diff --git a/src/test/skript/tests/syntaxes/expressions/ExprBlockSound.sk b/src/test/skript/tests/syntaxes/expressions/ExprBlockSound.sk index 133ea5a7e7c..e2dd76713e1 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprBlockSound.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprBlockSound.sk @@ -4,29 +4,29 @@ test "block sounds (1.20+)": set {_before} to blockdata of (block at (spawn of world "world")) set {_20} to whether running minecraft "1.20" - set {_empty} to "INTENTIONALLY_EMPTY" if running minecraft "1.20" else "BLOCK_STONE_PLACE" + set {_empty} to "INTENTIONALLY_EMPTY" if running minecraft "1.20" else "BLOCK.STONE.PLACE" # === TESTS === set {_stone::*} to getObjects(stone) - assert break sound of {_stone::*} is "BLOCK_STONE_BREAK" with "break sound of stone wasn't BLOCK_STONE_BREAK" - assert fall sound of {_stone::*} is "BLOCK_STONE_FALL" with "fall sound of stone wasn't BLOCK_STONE_FALL" - assert hit sound of {_stone::*} is "BLOCK_STONE_HIT" with "hit sound of stone wasn't BLOCK_STONE_HIT" - assert place sound of {_stone::*} is "BLOCK_STONE_PLACE" with "place sound of stone wasn't BLOCK_STONE_PLACE" - assert step sound of {_stone::*} is "BLOCK_STONE_STEP" with "step sound of stone wasn't BLOCK_STONE_STEP" + assert break sound of {_stone::*} is "BLOCK.STONE.BREAK" with "break sound of stone wasn't BLOCK.STONE.BREAK" + assert fall sound of {_stone::*} is "BLOCK.STONE.FALL" with "fall sound of stone wasn't BLOCK.STONE.FALL" + assert hit sound of {_stone::*} is "BLOCK.STONE.HIT" with "hit sound of stone wasn't BLOCK.STONE.HIT" + assert place sound of {_stone::*} is "BLOCK.STONE.PLACE" with "place sound of stone wasn't BLOCK.STONE.PLACE" + assert step sound of {_stone::*} is "BLOCK.STONE.STEP" with "step sound of stone wasn't BLOCK.STONE.STEP" set {_wool::*} to getObjects(wool) - assert break sound of {_wool::*} is "BLOCK_WOOL_BREAK" with "break sound of stone wasn't BLOCK_WOOL_BREAK" - assert fall sound of {_wool::*} is "BLOCK_WOOL_FALL" with "fall sound of stone wasn't BLOCK_WOOL_FALL" - assert hit sound of {_wool::*} is "BLOCK_WOOL_HIT" with "hit sound of stone wasn't BLOCK_WOOL_HIT" - assert place sound of {_wool::*} is "BLOCK_WOOL_PLACE" with "place sound of stone wasn't BLOCK_WOOL_PLACE" - assert step sound of {_wool::*} is "BLOCK_WOOL_STEP" with "step sound of stone wasn't BLOCK_WOOL_STEP" - - assert break sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_BREAK") with "break sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" - assert fall sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_FALL") with "fall sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" - assert hit sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_HIT") with "hit sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" - assert place sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_PLACE") with "place sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" - assert step sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK_STONE_STEP") with "step sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" + assert break sound of {_wool::*} is "BLOCK.WOOL.BREAK" with "break sound of stone wasn't BLOCK.WOOL.BREAK" + assert fall sound of {_wool::*} is "BLOCK.WOOL.FALL" with "fall sound of stone wasn't BLOCK.WOOL.FALL" + assert hit sound of {_wool::*} is "BLOCK.WOOL.HIT" with "hit sound of stone wasn't BLOCK.WOOL.HIT" + assert place sound of {_wool::*} is "BLOCK.WOOL.PLACE" with "place sound of stone wasn't BLOCK.WOOL.PLACE" + assert step sound of {_wool::*} is "BLOCK.WOOL.STEP" with "step sound of stone wasn't BLOCK.WOOL.STEP" + + assert break sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK.STONE.BREAK") with "break sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" + assert fall sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK.STONE.FALL") with "fall sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" + assert hit sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK.STONE.HIT") with "hit sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" + assert place sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK.STONE.PLACE") with "place sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" + assert step sound of (water, lava and bubble column) is ("INTENTIONALLY_EMPTY" if {_20} is true else "BLOCK.STONE.STEP") with "step sound of water, lava, or bubble column wasn't INTENTIONALLY_EMPTY" assert break sound of (diamond, diamond sword and {_none}) is not set with "break sound of non-block item shouldn't be set" assert fall sound of (diamond, diamond sword and {_none}) is not set with "fall sound of non-block item shouldn't be set" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprEntitySound.sk b/src/test/skript/tests/syntaxes/expressions/ExprEntitySound.sk index 4a536367a55..d3cb3c0751b 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprEntitySound.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprEntitySound.sk @@ -1,17 +1,17 @@ test "entity sounds": spawn a zombie at (spawn of world "world"): - assert damage sound of entity is "ENTITY_ZOMBIE_HURT" with "damage sound of zombie should return ENTITY_ZOMBIE_HURT" - assert death sound of entity is "ENTITY_ZOMBIE_DEATH" with "death sound of zombie should return ENTITY_ZOMBIE_DEATH" - assert swim sound of entity is "ENTITY_HOSTILE_SWIM" with "swim sound of zombie should return ENTITY_HOSTILE_SWIM" - assert normal fall damage sound of entity is "ENTITY_HOSTILE_SMALL_FALL" with "fall sound of zombie should return ENTITY_HOSTILE_SMALL_FALL" - assert high fall damage sound of entity is "ENTITY_HOSTILE_BIG_FALL" with "high fall sound of zombie should return ENTITY_HOSTILE_BIG_FALL" - assert fall damage sound from a height of 10 of entity is "ENTITY_HOSTILE_BIG_FALL" with "fall sound from height of 10 sound of zombie should return ENTITY_HOSTILE_BIG_FALL" - assert fall damage sound from a height of {_none} of entity is "ENTITY_HOSTILE_SMALL_FALL" with "fall sound from invalid height sound of zombie should return ENTITY_HOSTILE_SMALL_FALL" - assert splash sound of entity is "ENTITY_HOSTILE_SPLASH" with "splash sound of zombie should return ENTITY_HOSTILE_SPLASH" - assert speedy splash sound of entity is "ENTITY_GENERIC_SPLASH" with "speedy splash sound of zombie should return ENTITY_GENERIC_SPLASH" - assert eating sound of entity is "ENTITY_GENERIC_EAT" with "eating sound of zombie should return ENTITY_GENERIC_EAT" - assert eating sound of entity using golden apple is "ENTITY_GENERIC_EAT" with "eating sound of zombie using golden apple should return ENTITY_GENERIC_EAT" - assert drinking sound of entity is "ENTITY_GENERIC_DRINK" with "drinking sound of zombie should return ENTITY_GENERIC_DRINK" - assert drinking sound of entity using potion is "ENTITY_GENERIC_DRINK" with "drinking sound of zombie using potion should return ENTITY_GENERIC_DRINK" - assert ambient sound of entity is "ENTITY_ZOMBIE_AMBIENT" with "ambient sound of zombie should return ENTITY_ZOMBIE_AMBIENT" + assert damage sound of entity is "ENTITY.ZOMBIE.HURT" with "damage sound of zombie should return ENTITY.ZOMBIE.HURT" + assert death sound of entity is "ENTITY.ZOMBIE.DEATH" with "death sound of zombie should return ENTITY.ZOMBIE.DEATH" + assert swim sound of entity is "ENTITY.HOSTILE.SWIM" with "swim sound of zombie should return ENTITY.HOSTILE.SWIM" + assert normal fall damage sound of entity is "ENTITY.HOSTILE.SMALL_FALL" with "fall sound of zombie should return ENTITY.HOSTILE.SMALL_FALL" + assert high fall damage sound of entity is "ENTITY.HOSTILE.BIG_FALL" with "high fall sound of zombie should return ENTITY.HOSTILE.BIG_FALL" + assert fall damage sound from a height of 10 of entity is "ENTITY.HOSTILE.BIG_FALL" with "fall sound from height of 10 sound of zombie should return ENTITY.HOSTILE.BIG_FALL" + assert fall damage sound from a height of {_none} of entity is "ENTITY.HOSTILE.SMALL_FALL" with "fall sound from invalid height sound of zombie should return ENTITY.HOSTILE.SMALL_FALL" + assert splash sound of entity is "ENTITY.HOSTILE.SPLASH" with "splash sound of zombie should return ENTITY.HOSTILE.SPLASH" + assert speedy splash sound of entity is "ENTITY.GENERIC.SPLASH" with "speedy splash sound of zombie should return ENTITY.GENERIC.SPLASH" + assert eating sound of entity is "ENTITY.GENERIC.EAT" with "eating sound of zombie should return ENTITY.GENERIC.EAT" + assert eating sound of entity using golden apple is "ENTITY.GENERIC.EAT" with "eating sound of zombie using golden apple should return ENTITY.GENERIC.EAT" + assert drinking sound of entity is "ENTITY.GENERIC.DRINK" with "drinking sound of zombie should return ENTITY.GENERIC.DRINK" + assert drinking sound of entity using potion is "ENTITY.GENERIC.DRINK" with "drinking sound of zombie using potion should return ENTITY.GENERIC.DRINK" + assert ambient sound of entity is "ENTITY.ZOMBIE.AMBIENT" with "ambient sound of zombie should return ENTITY.ZOMBIE.AMBIENT" delete entity From 0a680d46800b6a2af8e140326078ddccca632f28 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Sat, 9 Nov 2024 16:00:07 -0500 Subject: [PATCH 16/18] fix various boat bugs (#7185) * boats are hoes fix bugs with comparisons, use simple entity data for 1.21.3+ * whoops * .2 * Update skript-aliases * Apply suggestions from code review Co-authored-by: Patrick Miller * requested changes 2 --------- Co-authored-by: Patrick Miller --- skript-aliases | 2 +- .../ch/njol/skript/entity/BoatChestData.java | 88 +++++++------- .../java/ch/njol/skript/entity/BoatData.java | 112 +++++++++--------- .../njol/skript/entity/SimpleEntityData.java | 31 ++++- src/main/resources/lang/default.lang | 7 ++ src/test/skript/tests/misc/boats.sk | 50 ++++++++ 6 files changed, 190 insertions(+), 100 deletions(-) create mode 100644 src/test/skript/tests/misc/boats.sk diff --git a/skript-aliases b/skript-aliases index 16949c28e0d..809c9e1ad95 160000 --- a/skript-aliases +++ b/skript-aliases @@ -1 +1 @@ -Subproject commit 16949c28e0d7bb25ea7c3479c3d6754ff3b5a3e6 +Subproject commit 809c9e1ad95f26f9d62327f8447301a9e7db5379 diff --git a/src/main/java/ch/njol/skript/entity/BoatChestData.java b/src/main/java/ch/njol/skript/entity/BoatChestData.java index 78ab1a4a43f..c4918af95b6 100644 --- a/src/main/java/ch/njol/skript/entity/BoatChestData.java +++ b/src/main/java/ch/njol/skript/entity/BoatChestData.java @@ -1,54 +1,42 @@ package ch.njol.skript.entity; import ch.njol.skript.Skript; +import ch.njol.skript.aliases.ItemData; import ch.njol.skript.aliases.ItemType; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser; import org.bukkit.Material; import org.bukkit.entity.Boat; import org.bukkit.entity.ChestBoat; -import org.bukkit.entity.boat.*; import org.jetbrains.annotations.Nullable; -import java.util.EnumMap; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; import java.util.Random; +// For <1.21.3 compatability only. 1.21.3+ boats are SimpleEntityDatas public class BoatChestData extends EntityData { - private static final boolean IS_RUNNING_1_21_3 = Skript.isRunningMinecraft(1, 21, 3); - private static final EnumMap> typeToClassMap = new EnumMap<>(Boat.Type.class); - private static final Boat.Type[] types = Boat.Type.values(); static { - // This ensures all boats are registered - // As well as in the correct order via 'ordinal' - String[] patterns = new String[types.length + 2]; - patterns[0] = "chest boat"; - patterns[1] = "any chest boat"; - for (Boat.Type boat : types) { - String boatName; - if (boat == Boat.Type.BAMBOO) - boatName = "bamboo chest raft"; - else - boatName = boat.toString().replace("_", " ").toLowerCase(Locale.ENGLISH) + " chest boat"; - patterns[boat.ordinal() + 2] = boatName; - } - - if (IS_RUNNING_1_21_3) { - typeToClassMap.put(Boat.Type.OAK, OakChestBoat.class); - typeToClassMap.put(Boat.Type.SPRUCE, SpruceChestBoat.class); - typeToClassMap.put(Boat.Type.BIRCH, BirchChestBoat.class); - typeToClassMap.put(Boat.Type.JUNGLE, JungleChestBoat.class); - typeToClassMap.put(Boat.Type.ACACIA, AcaciaChestBoat.class); - typeToClassMap.put(Boat.Type.DARK_OAK, DarkOakChestBoat.class); - typeToClassMap.put(Boat.Type.MANGROVE, MangroveChestBoat.class); - typeToClassMap.put(Boat.Type.CHERRY, CherryChestBoat.class); - typeToClassMap.put(Boat.Type.BAMBOO, BambooChestRaft.class); - } + if (!Skript.isRunningMinecraft(1, 21, 2)) { + // This ensures all boats are registered + // As well as in the correct order via 'ordinal' + String[] patterns = new String[types.length + 2]; + patterns[0] = "chest boat"; + patterns[1] = "any chest boat"; + for (Boat.Type boat : types) { + String boatName; + if (boat == Boat.Type.BAMBOO) { + boatName = "bamboo chest raft"; + } else { + boatName = boat.toString().replace("_", " ").toLowerCase(Locale.ENGLISH) + " chest boat"; + } + patterns[boat.ordinal() + 2] = boatName; + } - if (Skript.classExists("org.bukkit.entity.ChestBoat")) { EntityData.register(BoatChestData.class, "chest boat", ChestBoat.class, 0, patterns); } } @@ -92,8 +80,6 @@ protected boolean match(ChestBoat entity) { @Override public Class getType() { - if (IS_RUNNING_1_21_3) - return typeToClassMap.get(types[matchedPattern - 2]); return ChestBoat.class; } @@ -121,21 +107,33 @@ public boolean isSupertypeOf(EntityData entity) { return false; } - public boolean isOfItemType(ItemType itemType) { - int ordinal = -1; - Material material = itemType.getMaterial(); - if (material == Material.OAK_CHEST_BOAT) { - ordinal = 0; - } else { - for (Boat.Type boat : types) { - if (material.name().contains(boat.toString())) { - ordinal = boat.ordinal(); - break; - } + private static final Map materialToType = new HashMap<>(); + static { + materialToType.put(Material.OAK_CHEST_BOAT, Boat.Type.OAK); + materialToType.put(Material.BIRCH_CHEST_BOAT, Boat.Type.BIRCH); + materialToType.put(Material.SPRUCE_CHEST_BOAT, Boat.Type.SPRUCE); + materialToType.put(Material.JUNGLE_CHEST_BOAT, Boat.Type.JUNGLE); + materialToType.put(Material.DARK_OAK_CHEST_BOAT, Boat.Type.DARK_OAK); + materialToType.put(Material.ACACIA_CHEST_BOAT, Boat.Type.ACACIA); + materialToType.put(Material.MANGROVE_CHEST_BOAT, Boat.Type.MANGROVE); + materialToType.put(Material.CHERRY_CHEST_BOAT, Boat.Type.CHERRY); + materialToType.put(Material.BAMBOO_CHEST_RAFT, Boat.Type.BAMBOO); + } + + public boolean isOfItemType(ItemType itemType) { + for (ItemData itemData : itemType.getTypes()) { + int ordinal; + Material material = itemData.getType(); + Boat.Type type = materialToType.get(material); + // material is a boat AND (data matches any boat OR material and data are same) + if (type != null) { + ordinal = type.ordinal(); + if (matchedPattern <= 1 || matchedPattern == ordinal + 2) + return true; } } - return hashCode_i() == ordinal + 2 || (matchedPattern + ordinal == 0) || ordinal == 0; + return false; } } diff --git a/src/main/java/ch/njol/skript/entity/BoatData.java b/src/main/java/ch/njol/skript/entity/BoatData.java index d03628c271b..f173cbec3fc 100644 --- a/src/main/java/ch/njol/skript/entity/BoatData.java +++ b/src/main/java/ch/njol/skript/entity/BoatData.java @@ -1,56 +1,43 @@ package ch.njol.skript.entity; -import java.util.EnumMap; -import java.util.Locale; -import java.util.Random; - import ch.njol.skript.Skript; -import org.bukkit.Material; -import org.bukkit.entity.Boat; -import org.bukkit.entity.boat.*; +import ch.njol.skript.aliases.ItemData; import ch.njol.skript.aliases.ItemType; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; +import org.bukkit.Material; +import org.bukkit.entity.Boat; import org.jetbrains.annotations.Nullable; -public class BoatData extends EntityData { +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Random; - private static final boolean IS_RUNNING_1_21_3 = Skript.isRunningMinecraft(1, 21, 3); - private static final EnumMap> typeToClassMap = new EnumMap<>(Boat.Type.class); +// For <1.21.3 compatability only. 1.21.3+ boats are SimpleEntityDatas +public class BoatData extends EntityData { private static final Boat.Type[] types = Boat.Type.values(); static { - // This ensures all boats are registered - // As well as in the correct order via 'ordinal' - String[] patterns = new String[types.length + 2]; - patterns[0] = "chest boat"; - patterns[1] = "any chest boat"; - for (Boat.Type boat : types) { - String boatName; - if (boat == Boat.Type.BAMBOO) - boatName = "bamboo raft"; - else - boatName = boat.toString().replace("_", " ").toLowerCase(Locale.ENGLISH) + " boat"; - patterns[boat.ordinal() + 2] = boatName; - } - - if (IS_RUNNING_1_21_3) { - typeToClassMap.put(Boat.Type.OAK, OakBoat.class); - typeToClassMap.put(Boat.Type.SPRUCE, SpruceBoat.class); - typeToClassMap.put(Boat.Type.BIRCH, BirchBoat.class); - typeToClassMap.put(Boat.Type.JUNGLE, JungleBoat.class); - typeToClassMap.put(Boat.Type.ACACIA, AcaciaBoat.class); - typeToClassMap.put(Boat.Type.DARK_OAK, DarkOakBoat.class); - typeToClassMap.put(Boat.Type.MANGROVE, MangroveBoat.class); - typeToClassMap.put(Boat.Type.CHERRY, CherryBoat.class); - typeToClassMap.put(Boat.Type.BAMBOO, BambooRaft.class); + if (!Skript.isRunningMinecraft(1, 21, 2)) { + // This ensures all boats are registered + // As well as in the correct order via 'ordinal' + String[] patterns = new String[types.length + 2]; + patterns[0] = "boat"; + patterns[1] = "any boat"; + for (Boat.Type boat : types) { + String boatName; + if (boat == Boat.Type.BAMBOO) { + boatName = "bamboo raft"; + } else { + boatName = boat.toString().replace("_", " ").toLowerCase(Locale.ENGLISH) + " boat"; + } + patterns[boat.ordinal() + 2] = boatName; + } + EntityData.register(BoatData.class, "boat", Boat.class, 0, patterns); } - - EntityData.register(BoatData.class, "boat", Boat.class, 0, patterns); } - - public BoatData(){ this(0); @@ -91,8 +78,6 @@ protected boolean match(Boat entity) { @Override public Class getType() { - if (IS_RUNNING_1_21_3) - return typeToClassMap.get(types[matchedPattern - 2]); return Boat.class; } @@ -119,22 +104,43 @@ public boolean isSupertypeOf(EntityData entity) { return matchedPattern <= 1 || matchedPattern == boatData.matchedPattern; return false; } - - public boolean isOfItemType(ItemType itemType){ - int ordinal = -1; - Material material = itemType.getMaterial(); - if (material == Material.OAK_BOAT) { - ordinal = 0; - } else { - for (Boat.Type boat : types) { - if (material.name().contains(boat.toString())) { - ordinal = boat.ordinal(); - break; - } + private static final Map materialToType = new HashMap<>(); + static { + materialToType.put(Material.OAK_BOAT, Boat.Type.OAK); + materialToType.put(Material.BIRCH_BOAT, Boat.Type.BIRCH); + materialToType.put(Material.SPRUCE_BOAT, Boat.Type.SPRUCE); + materialToType.put(Material.JUNGLE_BOAT, Boat.Type.JUNGLE); + materialToType.put(Material.DARK_OAK_BOAT, Boat.Type.DARK_OAK); + materialToType.put(Material.ACACIA_BOAT, Boat.Type.ACACIA); + materialToType.put(Material.MANGROVE_BOAT, Boat.Type.MANGROVE); + materialToType.put(Material.CHERRY_BOAT, Boat.Type.CHERRY); + materialToType.put(Material.BAMBOO_RAFT, Boat.Type.BAMBOO); + // 'oak chest boat is a boat' should pass + materialToType.put(Material.OAK_CHEST_BOAT, Boat.Type.OAK); + materialToType.put(Material.BIRCH_CHEST_BOAT, Boat.Type.BIRCH); + materialToType.put(Material.SPRUCE_CHEST_BOAT, Boat.Type.SPRUCE); + materialToType.put(Material.JUNGLE_CHEST_BOAT, Boat.Type.JUNGLE); + materialToType.put(Material.DARK_OAK_CHEST_BOAT, Boat.Type.DARK_OAK); + materialToType.put(Material.ACACIA_CHEST_BOAT, Boat.Type.ACACIA); + materialToType.put(Material.MANGROVE_CHEST_BOAT, Boat.Type.MANGROVE); + materialToType.put(Material.CHERRY_CHEST_BOAT, Boat.Type.CHERRY); + materialToType.put(Material.BAMBOO_CHEST_RAFT, Boat.Type.BAMBOO); + } + + public boolean isOfItemType(ItemType itemType) { + for (ItemData itemData : itemType.getTypes()) { + int ordinal; + Material material = itemData.getType(); + Boat.Type type = materialToType.get(material); + // material is a boat AND (data matches any boat OR material and data are same) + if (type != null) { + ordinal = type.ordinal(); + if (matchedPattern <= 1 || matchedPattern == ordinal + 2) + return true; } } - return hashCode_i() == ordinal + 2 || (matchedPattern + ordinal == 0) || ordinal == 0; + return false; } } diff --git a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java index 97957881124..1143d6b9db0 100644 --- a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java +++ b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java @@ -25,6 +25,7 @@ import ch.njol.yggdrasil.Fields; import org.bukkit.World; import org.bukkit.entity.*; +import org.bukkit.entity.boat.*; import org.jetbrains.annotations.Nullable; import java.io.NotSerializableException; @@ -210,8 +211,36 @@ private static void addSuperEntity(String codeName, Class enti addSimpleEntity("bogged", Bogged.class); } - if (Skript.isRunningMinecraft(1,21,3)) { + if (Skript.isRunningMinecraft(1,21,2)) { addSimpleEntity("creaking", Creaking.class); + addSimpleEntity("creaking", Creaking.class); + // boats + addSimpleEntity("oak boat", OakBoat.class); + addSimpleEntity("dark oak boat", DarkOakBoat.class); + addSimpleEntity("pale oak boat", PaleOakBoat.class); + addSimpleEntity("acacia boat", AcaciaBoat.class); + addSimpleEntity("birch boat", BirchBoat.class); + addSimpleEntity("spruce boat", SpruceBoat.class); + addSimpleEntity("jungle boat", JungleBoat.class); + addSimpleEntity("bamboo raft", BambooRaft.class); + addSimpleEntity("mangrove boat", MangroveBoat.class); + addSimpleEntity("cherry boat", CherryBoat.class); + // chest boats + addSimpleEntity("oak chest boat", OakChestBoat.class); + addSimpleEntity("dark oak chest boat", DarkOakChestBoat.class); + addSimpleEntity("pale oak chest boat", PaleOakChestBoat.class); + addSimpleEntity("acacia chest boat", AcaciaChestBoat.class); + addSimpleEntity("birch chest boat", BirchChestBoat.class); + addSimpleEntity("spruce chest boat", SpruceChestBoat.class); + addSimpleEntity("jungle chest boat", JungleChestBoat.class); + addSimpleEntity("bamboo chest raft", BambooChestRaft.class); + addSimpleEntity("mangrove chest boat", MangroveChestBoat.class); + addSimpleEntity("cherry chest boat", CherryChestBoat.class); + // supers + addSuperEntity("boat", Boat.class); + addSuperEntity("any boat", Boat.class); + addSuperEntity("chest boat", ChestBoat.class); + addSuperEntity("any chest boat", ChestBoat.class); } // Register zombie after Husk and Drowned to make sure both work diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index ab1153a6839..c463f236c01 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -502,6 +502,7 @@ entities: bat: name: bat¦s pattern: bat(|1¦s) + # boats! boat: name: boat¦s pattern: boat[1:s] @@ -535,6 +536,9 @@ entities: cherry boat: name: cherry boat¦s pattern: cherry [blossom] boat[1:s] + pale oak boat: + name: pale oak boat¦s + pattern: pale oak boat[1:s] blaze: name: blaze¦s pattern: blaze(|1¦s) @@ -1235,6 +1239,9 @@ entities: bamboo chest raft: name: bamboo chest raft¦s pattern: bamboo chest (raft|boat)[1:s] + pale oak chest boat: + name: pale oak chest boat¦s + pattern: pale oak chest boat[1:s] frog: name: frog¦s pattern: frog(|1¦s)|(4¦)frog (kid(|1¦s)|child(|1¦ren)) diff --git a/src/test/skript/tests/misc/boats.sk b/src/test/skript/tests/misc/boats.sk new file mode 100644 index 00000000000..f2deea133e0 --- /dev/null +++ b/src/test/skript/tests/misc/boats.sk @@ -0,0 +1,50 @@ +test "boats": + set {_boats::*} to "oak boat", "birch boat", "spruce boat", "jungle boat", "acacia boat", "dark oak boat", and "mangrove boat" + parse if running minecraft "1.20.4": + add "cherry boat" and "bamboo raft" to {_boats::*} + # enable when fully released + # parse if running minecraft "1.21.3": + # add "pale oak boat" to {_boats::*} + + loop {_boats::*}: + set {_parse} to loop-value parsed as entity type + if {_parse} is set: + spawn {_parse} at spawn of "world": + set {_boat} to entity + + assert {_boat} is set with "Failed to spawn %loop-value%" + set {_a boat} to "a boat" parsed as entity type + set {_a boat item} to "a boat" parsed as item type + assert {_boat} is {_a boat} with "%loop-value% was not a boat" + assert {_boat} is {_a boat item} with "%loop-value% did not match a boat item" + + clear entity within {_boat} + clear {_boat} + else: + assert false is true with "Failed to parse '%loop-value%' as an entity type", expected loop-value, got {_parse} + + +test "chest boats": + set {_boats::*} to "oak chest boat", "birch chest boat", "spruce chest boat", "jungle chest boat", "acacia chest boat", "dark oak chest boat", and "mangrove chest boat" + parse if running minecraft "1.20.4": + add "cherry chest boat" and "bamboo chest raft" to {_boats::*} + # enable when fully released + # parse if running minecraft "1.21.3": + # add "pale oak chest boat" to {_boats::*} + + loop {_boats::*}: + set {_parse} to loop-value parsed as entity type + if {_parse} is set: + spawn {_parse} at spawn of "world": + set {_boat} to entity + + assert {_boat} is set with "Failed to spawn %loop-value%" + set {_a chest boat} to "a chest boat" parsed as entity type + set {_a chest boat item} to "a chest boat" parsed as item type + assert {_boat} is {_a chest boat} with "%loop-value% was not a chest boat" + assert {_boat} is {_a chest boat item} with "%loop-value% did not match a chest boat item" + + clear entity within {_boat} + clear {_boat} + else: + assert false is true with "Failed to parse '%loop-value%' as an entity type", expected loop-value, got {_parse} From ae710bad926e9673491aed22f3fec858539e070f Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:18:45 +0100 Subject: [PATCH 17/18] Add utility class for easier building of toString (#7168) * init commit * remove option * update javadoc * le fix * update debuggable * add docs * add null check * change to append, add general for objects * add multiple objects to chain * oopsie * add space notice --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/effects/EffBan.java | 54 ++++++++------- .../ch/njol/skript/effects/EffReplace.java | 16 +++-- .../njol/skript/lang/SyntaxStringBuilder.java | 65 +++++++++++++++++++ 3 files changed, 107 insertions(+), 28 deletions(-) create mode 100644 src/main/java/ch/njol/skript/lang/SyntaxStringBuilder.java diff --git a/src/main/java/ch/njol/skript/effects/EffBan.java b/src/main/java/ch/njol/skript/effects/EffBan.java index 88f815c96df..ee5d39cab9e 100644 --- a/src/main/java/ch/njol/skript/effects/EffBan.java +++ b/src/main/java/ch/njol/skript/effects/EffBan.java @@ -18,15 +18,6 @@ */ package ch.njol.skript.effects; -import java.net.InetSocketAddress; -import java.util.Date; - -import org.bukkit.BanList; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; -import org.bukkit.event.Event; - import ch.njol.skript.Skript; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; @@ -35,10 +26,19 @@ import ch.njol.skript.lang.Effect; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.SyntaxStringBuilder; import ch.njol.skript.util.Timespan; import ch.njol.util.Kleenean; +import org.bukkit.BanList; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import java.net.InetSocketAddress; +import java.util.Date; + @Name("Ban") @Description({"Bans or unbans a player or an IP address.", "If a reason is given, it will be shown to the player when they try to join the server while banned.", @@ -54,7 +54,7 @@ "ban and kick player due to \"inappropriate language\" for 2 days"}) @Since("1.4, 2.1.1 (ban reason), 2.5 (timespan), 2.9.0 (kick)") public class EffBan extends Effect { - + static { Skript.registerEffect(EffBan.class, "ban [kick:and kick] %strings/offlineplayers% [(by reason of|because [of]|on account of|due to) %-string%] [for %-timespan%]", @@ -64,18 +64,18 @@ public class EffBan extends Effect { "IP(-| )ban [kick:and kick] %players% [(by reason of|because [of]|on account of|due to) %-string%] [for %-timespan%]", "(IP(-| )unban|un[-]IP[-]ban) %players%"); } - + @SuppressWarnings("null") private Expression players; @Nullable private Expression reason; @Nullable private Expression expires; - + private boolean ban; private boolean ipBan; private boolean kick; - + @SuppressWarnings({"null", "unchecked"}) @Override public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { @@ -87,7 +87,7 @@ public boolean init(final Expression[] exprs, final int matchedPattern, final kick = parseResult.hasTag("kick"); return true; } - + @SuppressWarnings("null") @Override protected void execute(final Event e) { @@ -137,15 +137,23 @@ protected void execute(final Event e) { } } } - + @Override - public String toString(final @Nullable Event event, final boolean debug) { - return (ipBan ? "IP-" : "") + - (this.ban ? "ban " : "unban ") + - (kick ? "and kick " : "") + - this.players.toString(event, debug) + - (this.reason != null ? " on account of " + this.reason.toString(event, debug) : "") + - (expires != null ? " for " + expires.toString(event, debug) : ""); + public String toString(@Nullable Event event, boolean debug) { + SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug); + + if (ipBan) + builder.append("IP"); + builder.append(ban ? "ban" : "unban"); + if (kick) + builder.append("and kick"); + builder.append(players); + if (reason != null) + builder.append("on account of", reason); + if (expires != null) + builder.append("for", expires); + + return builder.toString(); } - + } diff --git a/src/main/java/ch/njol/skript/effects/EffReplace.java b/src/main/java/ch/njol/skript/effects/EffReplace.java index 0bb52d4eb26..ef2faf44282 100644 --- a/src/main/java/ch/njol/skript/effects/EffReplace.java +++ b/src/main/java/ch/njol/skript/effects/EffReplace.java @@ -31,6 +31,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionList; import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.SyntaxStringBuilder; import ch.njol.util.Kleenean; import ch.njol.util.StringUtils; import org.bukkit.event.Event; @@ -140,11 +141,16 @@ private void replace(Event event, Object[] needles, Expression haystackExpr) @Override public String toString(@Nullable Event event, boolean debug) { + SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug); + + builder.append("replace"); if (replaceFirst) - return "replace first " + needles.toString(event, debug) + " in " + haystack.toString(event, debug) + " with " + replacement.toString(event, debug) - + "(case sensitive: " + caseSensitive + ")"; - return "replace " + needles.toString(event, debug) + " in " + haystack.toString(event, debug) + " with " + replacement.toString(event, debug) - + "(case sensitive: " + caseSensitive + ")"; + builder.append("the first"); + builder.append(needles, "in", haystack, "with", replacement); + if (caseSensitive) + builder.append("with case sensitivity"); + + return builder.toString(); } - + } diff --git a/src/main/java/ch/njol/skript/lang/SyntaxStringBuilder.java b/src/main/java/ch/njol/skript/lang/SyntaxStringBuilder.java new file mode 100644 index 00000000000..b9ce40d7b19 --- /dev/null +++ b/src/main/java/ch/njol/skript/lang/SyntaxStringBuilder.java @@ -0,0 +1,65 @@ +package ch.njol.skript.lang; + +import com.google.common.base.Preconditions; +import org.bukkit.event.Event; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.StringJoiner; + +/** + * Utility class to build syntax strings, primarily intended for use + * in {@link Debuggable#toString(Event, boolean)} implementations. + * Spaces are automatically added between the provided objects. + */ +public class SyntaxStringBuilder { + + private final boolean debug; + private final @Nullable Event event; + private final StringJoiner joiner = new StringJoiner(" "); + + /** + * Creates a new SyntaxStringBuilder. + * + * @param event The event to get information from. This is always null if debug == false. + * @param debug If true this should print more information, if false this should print what is shown to the end user + */ + public SyntaxStringBuilder(@Nullable Event event, boolean debug) { + this.event = event; + this.debug = debug; + } + + /** + * Adds an object to the string. + * Spaces are automatically added between the provided objects. + * If the object is a {@link Debuggable} it will be formatted using + * {@link Debuggable#toString(Event, boolean)}. + * + * @param object The object to add. + */ + public void append(@NotNull Object object) { + Preconditions.checkNotNull(object); + if (object instanceof Debuggable debuggable) { + joiner.add(debuggable.toString(event, debug)); + } else { + joiner.add(object.toString()); + } + } + + /** + * Adds multiple objects to the string. + * Spaces are automatically added between the provided objects. + * @param objects The objects to add. + */ + public void append(@NotNull Object... objects) { + for (Object object : objects) { + append(object); + } + } + + @Override + public String toString() { + return joiner.toString(); + } + +} From a45c0b9af9560892d5d4b1fb5ed3fb7d26436392 Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:00:55 +0300 Subject: [PATCH 18/18] Fix EffContinue (#7221) --- .../java/ch/njol/skript/effects/EffContinue.java | 12 ++++++------ .../skript/tests/syntaxes/effects/EffContinue.sk | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffContinue.java b/src/main/java/ch/njol/skript/effects/EffContinue.java index 9dddbc96a16..b83fefec4f2 100644 --- a/src/main/java/ch/njol/skript/effects/EffContinue.java +++ b/src/main/java/ch/njol/skript/effects/EffContinue.java @@ -73,10 +73,6 @@ public class EffContinue extends Effect { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - level = matchedPattern == 0 ? 1 : Integer.parseInt(parseResult.regexes.get(0).group()); - if (level < 1) - return false; - ParserInstance parser = getParser(); int loops = parser.getCurrentSections(LoopSection.class).size(); if (loops == 0) { @@ -84,8 +80,12 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return false; } - // Section.getSections counts from the innermost section, so we need to invert the level - int levels = level == -1 ? 1 : loops - level + 1; + level = matchedPattern == 0 ? loops : Integer.parseInt(parseResult.regexes.get(0).group()); + if (level < 1) + return false; + + // ParserInstance#getSections counts from the innermost section, so we need to invert the level + int levels = loops - level + 1; if (levels <= 0) { Skript.error("Can't continue the " + StringUtils.fancyOrderNumber(level) + " loop as there " + (loops == 1 ? "is only 1 loop" : "are only " + loops + " loops") + " present"); diff --git a/src/test/skript/tests/syntaxes/effects/EffContinue.sk b/src/test/skript/tests/syntaxes/effects/EffContinue.sk index b63bc3c8603..366dd051522 100644 --- a/src/test/skript/tests/syntaxes/effects/EffContinue.sk +++ b/src/test/skript/tests/syntaxes/effects/EffContinue.sk @@ -19,3 +19,10 @@ test "continue effect": assert loop-value-2 is not 15 with "leveled continue failed #2" continue 1st loop if loop-value-1 is 10 assert loop-value is not 10 with "leveled continue failed #3" + + set {_ran} to false + loop 10 times: + loop 10 times: + continue + set {_ran} to true + assert {_ran} is true with "continue in nested loop continued outermost loop"