Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ADD and REMOVE changers to ExprMetadata #7113

Open
wants to merge 5 commits into
base: dev/feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 85 additions & 74 deletions src/main/java/ch/njol/skript/expressions/ExprMetadata.java
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*
* 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<T> extends SimpleExpression<T> {

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<String> values;
@Nullable
private Expression<Metadatable> holders;
private Class<? extends T>[] types;
private Class<T> superType;
private final ExprMetadata<?> source;
private final Class<? extends T>[] types;
private final Class<T> superType;

private @UnknownNullability Expression<String> keys;
private @UnknownNullability Expression<Metadatable> holders;

public ExprMetadata() {
//noinspection unchecked
this(null, (Class<? extends T>) Object.class);
}

@SafeVarargs
private ExprMetadata(ExprMetadata<?> source, Class<? extends T>... 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<T>) 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<Metadatable>) exprs[matchedPattern ^ 1];
values = (Expression<String>) exprs[matchedPattern];
keys = (Expression<String>) exprs[matchedPattern];
return true;
}

@Override
@Nullable
protected T[] get(Event e) {
protected T @Nullable [] get(Event event) {
List<Object> values = new ArrayList<>();
for (String value : this.values.getArray(e)) {
for (Metadatable holder : holders.getArray(e)) {
List<MetadataValue> metadata = holder.getMetadata(value);
String[] keys = this.keys.getArray(event);
for (Metadatable holder : holders.getArray(event)) {
for (String key : keys) {
List<MetadataValue> 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<MetadataValue> metadata = holder.getMetadata(key);
Object value;
OperationInfo<?, ?, ?> info;
if (metadata.isEmpty() || (value = metadata.get(metadata.size() - 1).value()) == null) {
info = Arithmetics.getOperationInfo(operator, delta[0].getClass(), delta[0].getClass());
UnderscoreTud marked this conversation as resolved.
Show resolved Hide resolved
if (info == null)
continue;
value = Arithmetics.getDefaultValue(info.getLeft());
if (value == null)
continue;
} else {
info = Arithmetics.getOperationInfo(operator, value.getClass(), delta[0].getClass());
if (info == 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
Expand All @@ -141,7 +151,8 @@ public Class<? extends T> getReturnType() {
}

@Override
public <R> Expression<? extends R> getConvertedExpression(Class<R>... to) {
@SafeVarargs
public final <R> Expression<? extends R> getConvertedExpression(Class<R>... to) {
return new ExprMetadata<>(this, to);
}

Expand All @@ -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);
}

}
}
21 changes: 21 additions & 0 deletions src/test/skript/tests/syntaxes/expressions/ExprMetadata.sk
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
test "metadata value":
UnderscoreTud marked this conversation as resolved.
Show resolved Hide resolved
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 "number" to metadata "number" of entity
assert metadata "number" of entity is 12 with "added incompatible type to 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
Loading