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 1 commit
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
135 changes: 82 additions & 53 deletions src/main/java/ch/njol/skript/expressions/ExprMetadata.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,8 @@
*/
UnderscoreTud marked this conversation as resolved.
Show resolved Hide resolved
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;
Expand All @@ -38,101 +28,139 @@
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser;
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) {
UnderscoreTud marked this conversation as resolved.
Show resolved Hide resolved
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) {
UnderscoreTud marked this conversation as resolved.
Show resolved Hide resolved
info = Arithmetics.getOperationInfo(operator, delta[0].getClass(), delta[0].getClass());
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 +169,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 +180,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);
}

}
UnderscoreTud marked this conversation as resolved.
Show resolved Hide resolved
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