From aa71764e4a7be7e990b74292f92647e9793293c2 Mon Sep 17 00:00:00 2001
From: _tud <98935832+UnderscoreTud@users.noreply.github.com>
Date: Sun, 15 Dec 2024 03:24:59 +0300
Subject: [PATCH 1/5] Fix the REMOVE changer of variables (#7163)
* Fix the REMOVE changer of variables
* Fix wording
* Fix tests
* Update Variable.java
---------
Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com>
---
.../java/ch/njol/skript/lang/Variable.java | 47 +++++--------------
...removing from variable skips duplicates.sk | 6 +++
2 files changed, 18 insertions(+), 35 deletions(-)
create mode 100644 src/test/skript/tests/regressions/7162-removing from variable skips duplicates.sk
diff --git a/src/main/java/ch/njol/skript/lang/Variable.java b/src/main/java/ch/njol/skript/lang/Variable.java
index 1b7a55f42b6..173ee1aeea3 100644
--- a/src/main/java/ch/njol/skript/lang/Variable.java
+++ b/src/main/java/ch/njol/skript/lang/Variable.java
@@ -1,23 +1,10 @@
-/**
- * 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.lang;
+import java.lang.reflect.Array;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.function.Function;
+
import ch.njol.skript.Skript;
import ch.njol.skript.SkriptAPIException;
import ch.njol.skript.SkriptConfig;
@@ -55,17 +42,6 @@
import org.skriptlang.skript.lang.script.Script;
import org.skriptlang.skript.lang.script.ScriptWarning;
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.TreeMap;
-import java.util.function.Function;
-
public class Variable implements Expression {
private final static String SINGLE_SEPARATOR_CHAR = ":";
@@ -579,14 +555,15 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) throw
if (mode == ChangeMode.REMOVE) {
if (map == null)
return;
- ArrayList toRemove = new ArrayList<>(); // prevents CMEs
+ Set toRemove = new HashSet<>(); // prevents CMEs
for (Object value : delta) {
for (Entry entry : map.entrySet()) {
+ String key = entry.getKey();
+ if (key == null)
+ continue; // This is NOT a part of list variable
+ if (toRemove.contains(key))
+ continue; // Skip if we've already marked this key to be removed
if (Relation.EQUAL.isImpliedBy(Comparators.compare(entry.getValue(), value))) {
- String key = entry.getKey();
- if (key == null)
- continue; // This is NOT a part of list variable
-
// Otherwise, we'll mark that key to be set to null
toRemove.add(key);
break;
@@ -600,7 +577,7 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) throw
} else if (mode == ChangeMode.REMOVE_ALL) {
if (map == null)
return;
- ArrayList toRemove = new ArrayList<>(); // prevents CMEs
+ Set toRemove = new HashSet<>(); // prevents CMEs
for (Entry i : map.entrySet()) {
for (Object value : delta) {
if (Relation.EQUAL.isImpliedBy(Comparators.compare(i.getValue(), value)))
diff --git a/src/test/skript/tests/regressions/7162-removing from variable skips duplicates.sk b/src/test/skript/tests/regressions/7162-removing from variable skips duplicates.sk
new file mode 100644
index 00000000000..c4fadc84163
--- /dev/null
+++ b/src/test/skript/tests/regressions/7162-removing from variable skips duplicates.sk
@@ -0,0 +1,6 @@
+test "removing from variables skips duplicates":
+ set {_list::*} to 1, 1, 2, 3, 4, 5, 6 and 6
+ remove 1 and 1 from {_list::*}
+ assert {_list::*} is 2, 3, 4, 5, 6 and 6 with "didn't remove all instances of 1 from the list"
+ remove first 6 elements out of {_list::*} from {_list::*}
+ assert {_list::*} is not set with "didn't remove all elements"
From 48f931a9f92dcb3b6486513c862fd788a1ba8038 Mon Sep 17 00:00:00 2001
From: Patrick Miller
Date: Tue, 17 Dec 2024 11:46:12 -0500
Subject: [PATCH 2/5] Fix Improperly Typed Array in ExprXOf (#7268)
---
src/main/java/ch/njol/skript/expressions/ExprXOf.java | 4 +++-
.../skript/tests/regressions/6817-null of x incorrect type.sk | 3 +++
2 files changed, 6 insertions(+), 1 deletion(-)
create mode 100644 src/test/skript/tests/regressions/6817-null of x incorrect type.sk
diff --git a/src/main/java/ch/njol/skript/expressions/ExprXOf.java b/src/main/java/ch/njol/skript/expressions/ExprXOf.java
index 06393a0423a..04dc49c8971 100644
--- a/src/main/java/ch/njol/skript/expressions/ExprXOf.java
+++ b/src/main/java/ch/njol/skript/expressions/ExprXOf.java
@@ -36,6 +36,8 @@
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Array;
+
@Name("X of Item")
@Description("An expression to be able to use a certain amount of items where the amount can be any expression. Please note that this expression is not stable and might be replaced in the future.")
@Examples("give level of player of pickaxes to the player")
@@ -62,7 +64,7 @@ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelaye
protected Object[] get(Event e, Object[] source) {
Number a = amount.getSingle(e);
if (a == null)
- return new Object[0];
+ return (Object[]) Array.newInstance(getReturnType(), 0);
return get(source, o -> {
if (o instanceof ItemStack) {
diff --git a/src/test/skript/tests/regressions/6817-null of x incorrect type.sk b/src/test/skript/tests/regressions/6817-null of x incorrect type.sk
new file mode 100644
index 00000000000..ac93553d712
--- /dev/null
+++ b/src/test/skript/tests/regressions/6817-null of x incorrect type.sk
@@ -0,0 +1,3 @@
+test "null of x incorrect type":
+ # would cause an exception
+ spawn {_null} of pig above {_null}
From 219e358711e3db3172d836d5c86ec54f5b7d2af5 Mon Sep 17 00:00:00 2001
From: Moderocky
Date: Thu, 26 Dec 2024 20:44:35 +0000
Subject: [PATCH 3/5] Add boat data check to prevent error. (#7301)
---
.../java/ch/njol/skript/entity/BoatData.java | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/main/java/ch/njol/skript/entity/BoatData.java b/src/main/java/ch/njol/skript/entity/BoatData.java
index 22b8d58b251..47d647ee61f 100644
--- a/src/main/java/ch/njol/skript/entity/BoatData.java
+++ b/src/main/java/ch/njol/skript/entity/BoatData.java
@@ -55,22 +55,22 @@ public class BoatData extends EntityData {
}
-
+
public BoatData(){
this(0);
}
-
+
public BoatData(@Nullable TreeSpecies type){
this(type != null ? type.ordinal() + 2 : 1);
}
-
+
private BoatData(int type){
matchedPattern = type;
}
-
+
@Override
protected boolean init(Literal>[] exprs, int matchedPattern, ParseResult parseResult) {
-
+
return true;
}
@@ -96,7 +96,7 @@ protected boolean match(Boat entity) {
@Override
public Class extends Boat> getType() {
- if (IS_RUNNING_1_21_3)
+ if (IS_RUNNING_1_21_3 && matchedPattern > 1)
return typeToClassMap.get(TreeSpecies.values()[matchedPattern - 2]);
return Boat.class;
}
@@ -124,7 +124,7 @@ public boolean isSupertypeOf(EntityData> e) {
return matchedPattern <= 1 || matchedPattern == ((BoatData)e).matchedPattern;
return false;
}
-
+
public boolean isOfItemType(ItemType i){
int ordinal = -1;
@@ -142,7 +142,7 @@ else if (type == Material.ACACIA_BOAT)
else if (type == Material.DARK_OAK_BOAT)
ordinal = TreeSpecies.DARK_OAK.ordinal();
return hashCode_i() == ordinal + 2 || (matchedPattern + ordinal == 0) || ordinal == 0;
-
+
}
}
From 3c940632d60327cd019d8ab6d411c0aeb1750f97 Mon Sep 17 00:00:00 2001
From: kyoshuske <65446070+kyoshuske@users.noreply.github.com>
Date: Thu, 26 Dec 2024 21:55:49 +0100
Subject: [PATCH 4/5] Fix BlockIgniteEvent (#7252)
Update BukkitEventValues.java
Co-authored-by: Moderocky
---
.../java/ch/njol/skript/classes/data/BukkitEventValues.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java
index 82fca450068..f40aa638d9e 100644
--- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java
+++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java
@@ -423,7 +423,7 @@ public Player get(final BlockIgniteEvent e) {
@Override
@Nullable
public Block get(final BlockIgniteEvent e) {
- return e.getIgnitingBlock();
+ return e.getBlock();
}
}, 0);
// BlockDispenseEvent
From cca446dd040f5f60ac3cb1517d5aec9ce23bf37f Mon Sep 17 00:00:00 2001
From: _tud <98935832+UnderscoreTud@users.noreply.github.com>
Date: Mon, 30 Dec 2024 09:24:05 +0300
Subject: [PATCH 5/5] Fix expression conversion (#7165)
* Fix expression conversion
* Extract duplicate code into a separate helper method
* improve conversion strategy
* Add .sk to test file
* Simplify conversion usage
We need to use conversion whenever there are multiple return types. If the expression does not accept our supertype, then we can attempt to convert it, which will already handle safety checks for multiple return types
* SimpleExpression: fix possible return type conversion
This fixes SimpleExpression not converting possible return types that are not contained in the desired types array. For example, if an expression can return a Number or a String, and we want an Expression that is a Number or an World, it will now include converters for String->Number and String->World
* Use safety checks of ConvertedExpression
* Remove incorrect converter remake
* Move logic from SimpleExpression to ConvertedExpression
---------
Co-authored-by: APickledWalrus
Co-authored-by: Efnilite <35348263+Efnilite@users.noreply.github.com>
Co-authored-by: Moderocky
---
.../ch/njol/skript/lang/SkriptParser.java | 10 ++---
.../skript/lang/util/ConvertedExpression.java | 40 +++++++++++--------
.../skript/lang/util/SimpleExpression.java | 30 --------------
...7164-expressions sometimes dont convert.sk | 13 ++++++
4 files changed, 42 insertions(+), 51 deletions(-)
create mode 100644 src/test/skript/tests/regressions/7164-expressions sometimes dont convert.sk
diff --git a/src/main/java/ch/njol/skript/lang/SkriptParser.java b/src/main/java/ch/njol/skript/lang/SkriptParser.java
index cefa92b2549..6cffb2a30fd 100644
--- a/src/main/java/ch/njol/skript/lang/SkriptParser.java
+++ b/src/main/java/ch/njol/skript/lang/SkriptParser.java
@@ -326,7 +326,6 @@ private static Variable parseVariable(String expr, Class extends T>[] r
}
}
-
@Nullable
@SuppressWarnings({"unchecked", "rawtypes"})
private Expression extends T> parseSingleExpr(boolean allowUnparsedLiteral, @Nullable LogEntry error, Class extends T>... types) {
@@ -368,9 +367,9 @@ private Expression extends T> parseSingleExpr(boolean allowUnparsedLiteral
if ((flags & PARSE_EXPRESSIONS) != 0) {
Expression> parsedExpression = parseExpression(types, expr);
if (parsedExpression != null) { // Expression/VariableString parsing success
+ Class> parsedReturnType = parsedExpression.getReturnType();
for (Class extends T> type : types) {
- // Check return type against everything that expression accepts
- if (parsedExpression.canReturn(type)) {
+ if (type.isAssignableFrom(parsedReturnType)) {
log.printLog();
return (Expression extends T>) parsedExpression;
}
@@ -540,13 +539,14 @@ private Expression> parseSingleExpr(boolean allowUnparsedLiteral, @Nullable Lo
if ((flags & PARSE_EXPRESSIONS) != 0) {
Expression> parsedExpression = parseExpression(types, expr);
if (parsedExpression != null) { // Expression/VariableString parsing success
+ Class> parsedReturnType = parsedExpression.getReturnType();
for (int i = 0; i < types.length; i++) {
Class> type = types[i];
if (type == null) // Ignore invalid (null) types
continue;
- // Check return type against everything that expression accepts
- if (parsedExpression.canReturn(type)) {
+ // Check return type against the expression's return type
+ if (type.isAssignableFrom(parsedReturnType)) {
if (!exprInfo.isPlural[i] && !parsedExpression.isSingle()) { // Wrong number of arguments
if (context == ParseContext.COMMAND) {
Skript.error(Commands.m_too_many_arguments.toString(exprInfo.classes[i].getName().getIndefiniteArticle(), exprInfo.classes[i].getName().toString()), ErrorQuality.SEMANTIC_ERROR);
diff --git a/src/main/java/ch/njol/skript/lang/util/ConvertedExpression.java b/src/main/java/ch/njol/skript/lang/util/ConvertedExpression.java
index 4d6228112f0..e6150fda79c 100644
--- a/src/main/java/ch/njol/skript/lang/util/ConvertedExpression.java
+++ b/src/main/java/ch/njol/skript/lang/util/ConvertedExpression.java
@@ -24,6 +24,7 @@
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.registrations.Classes;
+import ch.njol.skript.util.Utils;
import ch.njol.util.Checker;
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
@@ -96,24 +97,31 @@ public ConvertedExpression(Expression extends F> source, Class to, Collecti
@Nullable
public static ConvertedExpression newInstance(Expression from, Class... to) {
assert !CollectionUtils.containsSuperclass(to, from.getReturnType());
- // we track a list of converters that may work
- List> converters = new ArrayList<>();
- for (Class type : to) { // REMIND try more converters? -> also change WrapperExpression (and maybe ExprLoopValue)
- assert type != null;
- // casting super ? extends F> to super F> is wrong, but since the converter is only used for values returned by the expression
- // (which are instances of " extends F>") this won't result in any ClassCastExceptions.
- for (Class extends F> checking : from.possibleReturnTypes()) {
- //noinspection unchecked
- ConverterInfo super F, ? extends T> converter = (ConverterInfo super F, ? extends T>) Converters.getConverterInfo(checking, type);
- if (converter != null)
- converters.add(converter);
+
+ // we might be able to cast some (or all) of the possible return types to T
+ // for possible return types that can't be directly cast, regular converters will be used
+ List> infos = new ArrayList<>();
+ for (Class extends F> type : from.possibleReturnTypes()) {
+ if (CollectionUtils.containsSuperclass(to, type)) { // this type is of T, build a converter simply casting
+ // noinspection unchecked - 'type' is a desired type in 'to'
+ Class toType = (Class) type;
+ infos.add(new ConverterInfo<>(type, toType, toType::cast, 0));
+ } else { // this possible return type is not included in 'to'
+ // build all converters for converting the possible return type into any of the types of 'to'
+ for (Class toType : to) {
+ ConverterInfo extends F, T> converter = Converters.getConverterInfo(type, toType);
+ if (converter != null)
+ infos.add(converter);
+ }
}
- int size = converters.size();
- if (size == 1) // if there is only one info, there is no need to wrap it in a list
- return new ConvertedExpression<>(from, type, converters.get(0));
- if (size > 1)
- return new ConvertedExpression<>(from, type, converters, true);
}
+ if (!infos.isEmpty()) { // there are converters for (at least some of) the return types
+ // a note: casting extends F> to super F> is wrong, but since the converter is used only for values
+ // returned by the expression (which are instances of extends F>), this won't result in any CCEs
+ // noinspection rawtypes, unchecked
+ return new ConvertedExpression(from, Utils.getSuperType(infos.stream().map(ConverterInfo::getTo).toArray(Class[]::new)), infos, true);
+ }
+
return null;
}
diff --git a/src/main/java/ch/njol/skript/lang/util/SimpleExpression.java b/src/main/java/ch/njol/skript/lang/util/SimpleExpression.java
index c576cd8d774..eccb3c73ba5 100644
--- a/src/main/java/ch/njol/skript/lang/util/SimpleExpression.java
+++ b/src/main/java/ch/njol/skript/lang/util/SimpleExpression.java
@@ -35,13 +35,10 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.lang.converter.Converter;
-import org.skriptlang.skript.lang.converter.ConverterInfo;
import java.lang.reflect.Array;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
-import java.util.List;
/**
* An implementation of the {@link Expression} interface. You should usually extend this class to make a new expression.
@@ -206,33 +203,6 @@ public Expression extends R> getConvertedExpression(Class... to) {
// check whether this expression is already of type R
if (CollectionUtils.containsSuperclass(to, getReturnType()))
return (Expression extends R>) this;
-
- // we might be to cast some of the possible return types to R
- List> infos = new ArrayList<>();
- for (Class extends T> type : this.possibleReturnTypes()) {
- if (CollectionUtils.containsSuperclass(to, type)) { // this type is of R
- // build a converter that for casting to R
- // safety check is present in the event that we do not get this type at runtime
- final Class toType = (Class) type;
- infos.add(new ConverterInfo<>(getReturnType(), toType, fromObject -> {
- if (toType.isInstance(fromObject))
- return (R) fromObject;
- return null;
- }, 0));
- }
- }
- int size = infos.size();
- if (size == 1) { // if there is only one info, there is no need to wrap it in a list
- ConverterInfo extends T, R> info = infos.get(0);
- //noinspection rawtypes
- return new ConvertedExpression(this, info.getTo(), info);
- }
- if (size > 1) {
- //noinspection rawtypes
- return new ConvertedExpression(this, Utils.getSuperType(infos.stream().map(ConverterInfo::getTo).toArray(Class[]::new)), infos, false);
- }
-
- // attempt traditional conversion with proper converters
return this.getConvertedExpr(to);
}
diff --git a/src/test/skript/tests/regressions/7164-expressions sometimes dont convert.sk b/src/test/skript/tests/regressions/7164-expressions sometimes dont convert.sk
new file mode 100644
index 00000000000..f7954b3a642
--- /dev/null
+++ b/src/test/skript/tests/regressions/7164-expressions sometimes dont convert.sk
@@ -0,0 +1,13 @@
+test "expressions sometimes dont convert":
+ assert formatted ({_foo} + {_bar}) is not set with "formatting nothing shouldn't throw an error"
+
+ set {_foo} to "test"
+ assert formatted ({_foo} + {_bar}) is not set with "formatting string + none shouldn't throw an error"
+
+ set {_foo} to 1
+ set {_bar} to 2
+ assert formatted ({_foo} + {_bar}) is not set with "formatting number + number shouldn't throw an error"
+
+ set {_foo} to "foo"
+ set {_bar} to "bar"
+ assert formatted ({_foo} + {_bar}) is "foobar" with "formatting strings doesn't work"