From ea5fc23f985cc2e59d106f63627822e29a3eb0b4 Mon Sep 17 00:00:00 2001 From: _tud Date: Thu, 5 Oct 2023 16:14:19 +0300 Subject: [PATCH 1/6] Copy Effect --- .../java/ch/njol/skript/effects/EffCopy.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/main/java/ch/njol/skript/effects/EffCopy.java diff --git a/src/main/java/ch/njol/skript/effects/EffCopy.java b/src/main/java/ch/njol/skript/effects/EffCopy.java new file mode 100644 index 00000000000..37cb27e820b --- /dev/null +++ b/src/main/java/ch/njol/skript/effects/EffCopy.java @@ -0,0 +1,104 @@ +/** + * 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.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.*; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.Variable; +import ch.njol.skript.registrations.Classes; +import ch.njol.skript.variables.Variables; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; + +import java.util.Map; + +@Name("Copy") +@Description("Copies objects into a variable. When copying a list over to another list, the source list and its sublists are also copied over.") +@Examples("") +@Since("INSERT VERSION") +@Keywords({"copy", "clone"}) +public class EffCopy extends Effect { + + static { + Skript.registerEffect(EffCopy.class, "copy %~objects% [in]to %~objects%"); + } + + private Expression source; + private Variable destination; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + if (!(exprs[1] instanceof Variable)) { + Skript.error("You can only copy objects into variables"); + return false; + } + source = exprs[0]; + destination = (Variable) exprs[1]; + if (!source.isSingle() && destination.isSingle()) { + Skript.error("Cannot copy multiple objects into a single variable"); + return false; + } + return true; + } + + @Override + @SuppressWarnings("unchecked") + protected void execute(Event event) { + if (!(source instanceof Variable) || source.isSingle()) { + ChangeMode mode = ChangeMode.SET; + Object[] clone = (Object[]) Classes.clone(source.getArray(event)); + if (clone.length == 0) + mode = ChangeMode.DELETE; + destination.change(event, clone, mode); + return; + } + destination.change(event, null, ChangeMode.DELETE); + Map source = (Map) ((Variable) this.source).getRaw(event); + if (source == null) + return; + String target = destination.getName().getSingle(event); + copy(event, source, target.substring(0, target.length() - 3), destination.isLocal()); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "copy " + source.toString(event, debug) + " into " + destination.toString(event, debug); + } + + @SuppressWarnings("unchecked") + private static void copy(Event event, Map source, String targetName, boolean local) { + for (Map.Entry entry : source.entrySet()) { + if (entry.getKey() == null) + continue; + String node = targetName + Variable.SEPARATOR + entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Map) { + copy(event, (Map) value, node, local); + return; + } + Variables.setVariable(node, value, event, local); + } + } + +} From a7cbe7798600127a7dee237c5ba8636ee00eee71 Mon Sep 17 00:00:00 2001 From: _tud Date: Thu, 5 Oct 2023 16:38:15 +0300 Subject: [PATCH 2/6] Add example --- src/main/java/ch/njol/skript/effects/EffCopy.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/effects/EffCopy.java b/src/main/java/ch/njol/skript/effects/EffCopy.java index 37cb27e820b..0dfe9f9177f 100644 --- a/src/main/java/ch/njol/skript/effects/EffCopy.java +++ b/src/main/java/ch/njol/skript/effects/EffCopy.java @@ -35,7 +35,14 @@ @Name("Copy") @Description("Copies objects into a variable. When copying a list over to another list, the source list and its sublists are also copied over.") -@Examples("") +@Examples({ + "set {_foo::bar} to 1", + "set {_foo::sublist::foobar} to \"hey\"", + "copy {_foo::*} to {_copy::*}", + "broadcast indices of {_copy::*} # bar, sublist", + "broadcast {_copy::bar} # 1", + "broadcast {_copy::sublist::foobar} # \"hey!\"" +}) @Since("INSERT VERSION") @Keywords({"copy", "clone"}) public class EffCopy extends Effect { From 8d42e50ea63a043bb3a881d4b05bcd23969da7a4 Mon Sep 17 00:00:00 2001 From: _tud Date: Sat, 7 Oct 2023 19:44:00 +0300 Subject: [PATCH 3/6] Requested Changes --- .../java/ch/njol/skript/effects/EffCopy.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffCopy.java b/src/main/java/ch/njol/skript/effects/EffCopy.java index 0dfe9f9177f..c819d68f431 100644 --- a/src/main/java/ch/njol/skript/effects/EffCopy.java +++ b/src/main/java/ch/njol/skript/effects/EffCopy.java @@ -20,7 +20,11 @@ import ch.njol.skript.Skript; import ch.njol.skript.classes.Changer.ChangeMode; -import ch.njol.skript.doc.*; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Keywords; +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; @@ -34,7 +38,10 @@ import java.util.Map; @Name("Copy") -@Description("Copies objects into a variable. When copying a list over to another list, the source list and its sublists are also copied over.") +@Description({ + "Copies objects into a variable. When copying a list over to another list, the source list and its sublists are also copied over.", + "Note: Copying a value into a variable/list will overwrite the existing data." +}) @Examples({ "set {_foo::bar} to 1", "set {_foo::sublist::foobar} to \"hey\"", @@ -44,7 +51,7 @@ "broadcast {_copy::sublist::foobar} # \"hey!\"" }) @Since("INSERT VERSION") -@Keywords({"copy", "clone"}) +@Keywords({"clone", "variable", "list"}) public class EffCopy extends Effect { static { @@ -85,7 +92,8 @@ protected void execute(Event event) { if (source == null) return; String target = destination.getName().getSingle(event); - copy(event, source, target.substring(0, target.length() - 3), destination.isLocal()); + target = target.substring(0, target.length() - (Variable.SEPARATOR + "*").length()); // Strip the '::*' part from the name + copy(event, source, target, destination.isLocal()); } @Override @@ -104,7 +112,7 @@ private static void copy(Event event, Map source, String targetN copy(event, (Map) value, node, local); return; } - Variables.setVariable(node, value, event, local); + Variables.setVariable(node, Classes.clone(value), event, local); } } From ff6c7adec7ce8f8e6eae689aa8a0c360c017e6be Mon Sep 17 00:00:00 2001 From: _tud Date: Mon, 9 Oct 2023 17:16:50 +0300 Subject: [PATCH 4/6] Improvements --- .../java/ch/njol/skript/effects/EffCopy.java | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffCopy.java b/src/main/java/ch/njol/skript/effects/EffCopy.java index c819d68f431..9dfe680908d 100644 --- a/src/main/java/ch/njol/skript/effects/EffCopy.java +++ b/src/main/java/ch/njol/skript/effects/EffCopy.java @@ -35,6 +35,7 @@ import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; +import java.util.HashMap; import java.util.Map; @Name("Copy") @@ -87,13 +88,18 @@ protected void execute(Event event) { destination.change(event, clone, mode); return; } + + Map source = copyMap((Map) ((Variable) this.source).getRaw(event)); destination.change(event, null, ChangeMode.DELETE); - Map source = (Map) ((Variable) this.source).getRaw(event); if (source == null) return; + + // If we're copying {_foo::*} we don't want to also copy {_foo} + source.remove(null); + String target = destination.getName().getSingle(event); - target = target.substring(0, target.length() - (Variable.SEPARATOR + "*").length()); // Strip the '::*' part from the name - copy(event, source, target, destination.isLocal()); + target = target.substring(0, target.length() - (Variable.SEPARATOR + "*").length()); + set(event, target, source, this.destination.isLocal()); } @Override @@ -102,18 +108,30 @@ public String toString(@Nullable Event event, boolean debug) { } @SuppressWarnings("unchecked") - private static void copy(Event event, Map source, String targetName, boolean local) { - for (Map.Entry entry : source.entrySet()) { - if (entry.getKey() == null) - continue; - String node = targetName + Variable.SEPARATOR + entry.getKey(); - Object value = entry.getValue(); + private static @Nullable Map copyMap(@Nullable Map map) { + if (map == null) + return null; + Map copy = new HashMap<>(map.size()); + map.forEach((key, value) -> { + if (value instanceof Map) { + copy.put(key, copyMap((Map) value)); + return; + } + copy.put(key, Classes.clone(value)); + }); + return copy; + } + + @SuppressWarnings("unchecked") + private static void set(Event event, String targetName, Map source, boolean local) { + source.forEach((key, value) -> { + String node = targetName + (key == null ? "" : Variable.SEPARATOR + key); if (value instanceof Map) { - copy(event, (Map) value, node, local); + set(event, node, (Map) value, local); return; } - Variables.setVariable(node, Classes.clone(value), event, local); - } + Variables.setVariable(node, value, event, local); + }); } } From f2b568142cee1022e2179f4a63851a70228fe70b Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Sun, 31 Dec 2023 03:23:33 +0300 Subject: [PATCH 5/6] Add support to copy a source into multiple destinations --- .../java/ch/njol/skript/effects/EffCopy.java | 69 +++++++++++++------ 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffCopy.java b/src/main/java/ch/njol/skript/effects/EffCopy.java index 9dfe680908d..b979fee0fbd 100644 --- a/src/main/java/ch/njol/skript/effects/EffCopy.java +++ b/src/main/java/ch/njol/skript/effects/EffCopy.java @@ -27,6 +27,7 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Effect; import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionList; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.Variable; import ch.njol.skript.registrations.Classes; @@ -35,8 +36,7 @@ import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; +import java.util.*; @Name("Copy") @Description({ @@ -60,19 +60,27 @@ public class EffCopy extends Effect { } private Expression source; - private Variable destination; + private Expression rawDestination; + private List> destinations; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!(exprs[1] instanceof Variable)) { + source = exprs[0]; + rawDestination = exprs[1]; + if (exprs[1] instanceof Variable) { + destinations = Collections.singletonList((Variable) exprs[1]); + } else if (exprs[1] instanceof ExpressionList) { + destinations = unwrapExpressionList((ExpressionList) exprs[1]); + } + if (destinations == null) { Skript.error("You can only copy objects into variables"); return false; } - source = exprs[0]; - destination = (Variable) exprs[1]; - if (!source.isSingle() && destination.isSingle()) { - Skript.error("Cannot copy multiple objects into a single variable"); - return false; + for (Variable destination : destinations) { + if (!source.isSingle() && destination.isSingle()) { + Skript.error("Cannot copy multiple objects into a single variable"); + return false; + } } return true; } @@ -85,30 +93,36 @@ protected void execute(Event event) { Object[] clone = (Object[]) Classes.clone(source.getArray(event)); if (clone.length == 0) mode = ChangeMode.DELETE; - destination.change(event, clone, mode); - return; + for (Variable dest : destinations) + dest.change(event, clone, mode); + return; } Map source = copyMap((Map) ((Variable) this.source).getRaw(event)); - destination.change(event, null, ChangeMode.DELETE); - if (source == null) - return; // If we're copying {_foo::*} we don't want to also copy {_foo} - source.remove(null); + if (source != null) + source.remove(null); - String target = destination.getName().getSingle(event); - target = target.substring(0, target.length() - (Variable.SEPARATOR + "*").length()); - set(event, target, source, this.destination.isLocal()); + for (Variable destination : destinations) { + destination.change(event, null, ChangeMode.DELETE); + if (source == null) + continue; + + String target = destination.getName().getSingle(event); + target = target.substring(0, target.length() - (Variable.SEPARATOR + "*").length()); + set(event, target, source, destination.isLocal()); + } } @Override public String toString(@Nullable Event event, boolean debug) { - return "copy " + source.toString(event, debug) + " into " + destination.toString(event, debug); + return "copy " + source.toString(event, debug) + " into " + rawDestination.toString(event, debug); } @SuppressWarnings("unchecked") - private static @Nullable Map copyMap(@Nullable Map map) { + @Nullable + private static Map copyMap(@Nullable Map map) { if (map == null) return null; Map copy = new HashMap<>(map.size()); @@ -134,4 +148,19 @@ private static void set(Event event, String targetName, Map sour }); } + private static List> unwrapExpressionList(ExpressionList expressionList) { + Expression[] expressions = expressionList.getExpressions(); + List> destinations = new ArrayList<>(); + for (Expression expression : expressions) { + if (expression instanceof Variable) { + destinations.add((Variable) expression); + continue; + } + if (!(expression instanceof ExpressionList)) + return null; + destinations.addAll(unwrapExpressionList((ExpressionList) expression)); + } + return destinations; + } + } From 1dd800ee5c0948a9fd0ca2a15faa8a514741c7ff Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Mon, 1 Jan 2024 14:35:13 +0300 Subject: [PATCH 6/6] Add test and change element's name --- .../java/ch/njol/skript/effects/EffCopy.java | 2 +- .../skript/tests/syntaxes/effects/EffCopy.sk | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/test/skript/tests/syntaxes/effects/EffCopy.sk diff --git a/src/main/java/ch/njol/skript/effects/EffCopy.java b/src/main/java/ch/njol/skript/effects/EffCopy.java index b979fee0fbd..896fdafa0a3 100644 --- a/src/main/java/ch/njol/skript/effects/EffCopy.java +++ b/src/main/java/ch/njol/skript/effects/EffCopy.java @@ -38,7 +38,7 @@ import java.util.*; -@Name("Copy") +@Name("Copy Into Variable") @Description({ "Copies objects into a variable. When copying a list over to another list, the source list and its sublists are also copied over.", "Note: Copying a value into a variable/list will overwrite the existing data." diff --git a/src/test/skript/tests/syntaxes/effects/EffCopy.sk b/src/test/skript/tests/syntaxes/effects/EffCopy.sk new file mode 100644 index 00000000000..88c6eec9abb --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffCopy.sk @@ -0,0 +1,30 @@ +test "list copy": + set {_foo} to "should not be copied" + set {_foo::a} to "foo" + set {_foo::a::b} to "bar" + set {_foo::c::*} to "hello" and "world" + + set {_bar::*} to "should", "be" and "removed" + copy {_foo::*} into {_bar::*} + + assert {_bar} is not set with "Copied value of base variable" + assert {_bar::1} is not set with "Didn't override existing data 1" + assert {_bar::a} is "foo" with "Didn't copy {_foo::a}" + assert {_bar::a::b} is "bar" with "Didn't copy {_foo::a::b}" + assert {_bar::c::*} is "hello" or "world" with "Didn't copy {_foo::c::*}" + + set {_var} to 10 + copy {_var} into {_bar::*} + assert {_bar::1} is 10 with "Didn't copy single value" + assert {_bar::a} is not set with "Didn't override existing data 2" + + copy {_none} into {_bar::*} + assert {_bar::*} doesn't exist with "Copying nothing didn't delete existing data" + +test "single copy": + set {_var} to 10 + copy {_var} into {_foo} + assert {_foo} is 10 with "Didn't copy single value" + + copy {_none} into {_foo} + assert {_foo} is not set with "Copying nothing didn't delete variable"