2021-05-17 12:34:45 +02:00
|
|
|
package net.minestom.server.tag;
|
|
|
|
|
2021-05-17 17:46:56 +02:00
|
|
|
import org.jetbrains.annotations.ApiStatus;
|
2021-05-28 16:19:58 +02:00
|
|
|
import org.jetbrains.annotations.Contract;
|
2021-05-17 12:34:45 +02:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2021-05-17 12:44:22 +02:00
|
|
|
import org.jetbrains.annotations.Nullable;
|
2021-05-17 12:34:45 +02:00
|
|
|
import org.jglrxavpok.hephaistos.nbt.NBT;
|
|
|
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
2021-06-26 19:52:35 +02:00
|
|
|
import org.jglrxavpok.hephaistos.nbt.NBTException;
|
|
|
|
import org.jglrxavpok.hephaistos.nbt.SNBTParser;
|
2021-05-17 12:34:45 +02:00
|
|
|
|
2021-06-26 19:52:35 +02:00
|
|
|
import java.io.StringReader;
|
2021-06-26 05:08:33 +02:00
|
|
|
import java.util.Objects;
|
2021-05-17 12:34:45 +02:00
|
|
|
import java.util.function.BiConsumer;
|
|
|
|
import java.util.function.Function;
|
2021-05-17 12:44:22 +02:00
|
|
|
import java.util.function.Supplier;
|
2021-05-17 12:34:45 +02:00
|
|
|
|
2021-05-17 15:14:16 +02:00
|
|
|
/**
|
|
|
|
* Represents a key to retrieve or change a value.
|
|
|
|
* <p>
|
|
|
|
* All tags are serializable.
|
|
|
|
*
|
|
|
|
* @param <T> the tag type
|
|
|
|
*/
|
2021-05-17 17:46:56 +02:00
|
|
|
@ApiStatus.NonExtendable
|
|
|
|
public class Tag<T> {
|
2021-05-17 12:34:45 +02:00
|
|
|
|
2021-06-26 05:08:33 +02:00
|
|
|
/**
|
2021-06-26 19:52:35 +02:00
|
|
|
* Handles the snbt of the tag holder.
|
2021-06-26 05:08:33 +02:00
|
|
|
* <p>
|
2021-06-26 19:52:35 +02:00
|
|
|
* Writing will override all tags. Proceed with caution.
|
2021-06-26 05:08:33 +02:00
|
|
|
*/
|
|
|
|
@ApiStatus.Experimental
|
2021-06-26 19:52:35 +02:00
|
|
|
public static final Tag<String> SNBT = new Tag<>(null, NBTCompound::toSNBT, (original, snbt) -> {
|
|
|
|
try {
|
|
|
|
final var updated = new SNBTParser(new StringReader(snbt)).parse();
|
2021-10-22 02:19:38 +02:00
|
|
|
if (!(updated instanceof NBTCompound updatedCompound))
|
2021-06-26 19:52:35 +02:00
|
|
|
throw new IllegalArgumentException("'" + snbt + "' is not a compound!");
|
|
|
|
original.clear();
|
|
|
|
updatedCompound.getKeys().forEach(s ->
|
|
|
|
original.set(s, Objects.requireNonNull(updatedCompound.get(s))));
|
|
|
|
} catch (NBTException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}, null);
|
2021-06-26 05:08:33 +02:00
|
|
|
|
|
|
|
/**
|
2021-06-26 19:52:35 +02:00
|
|
|
* Handles the complete tag holder compound.
|
2021-06-26 05:08:33 +02:00
|
|
|
* <p>
|
2021-06-26 19:44:35 +02:00
|
|
|
* Writing will override all tags. Proceed with caution.
|
2021-06-26 05:08:33 +02:00
|
|
|
*/
|
|
|
|
@ApiStatus.Experimental
|
2021-06-26 19:44:35 +02:00
|
|
|
public static final Tag<NBTCompound> NBT = new Tag<>(null, NBTCompound::deepClone, (original, updated) -> {
|
|
|
|
original.clear();
|
|
|
|
updated.getKeys().forEach(s -> original.set(s, Objects.requireNonNull(updated.get(s))));
|
|
|
|
}, null);
|
2021-06-22 02:51:04 +02:00
|
|
|
|
2021-05-17 12:34:45 +02:00
|
|
|
private final String key;
|
|
|
|
private final Function<NBTCompound, T> readFunction;
|
|
|
|
private final BiConsumer<NBTCompound, T> writeConsumer;
|
2021-05-28 16:19:58 +02:00
|
|
|
|
|
|
|
private final Supplier<T> defaultValue;
|
2021-05-17 12:44:22 +02:00
|
|
|
|
2021-06-26 05:08:33 +02:00
|
|
|
protected Tag(@Nullable String key,
|
2021-05-28 16:19:58 +02:00
|
|
|
@NotNull Function<NBTCompound, T> readFunction,
|
2021-06-26 05:08:33 +02:00
|
|
|
@Nullable BiConsumer<NBTCompound, T> writeConsumer,
|
2021-05-28 16:19:58 +02:00
|
|
|
@Nullable Supplier<T> defaultValue) {
|
2021-05-17 12:34:45 +02:00
|
|
|
this.key = key;
|
|
|
|
this.readFunction = readFunction;
|
2021-06-26 05:08:33 +02:00
|
|
|
this.writeConsumer = Objects.requireNonNullElse(writeConsumer, (compound, t) -> {
|
|
|
|
});
|
2021-05-28 16:19:58 +02:00
|
|
|
this.defaultValue = defaultValue;
|
|
|
|
}
|
|
|
|
|
2021-06-26 05:08:33 +02:00
|
|
|
protected Tag(@Nullable String key,
|
2021-05-28 16:19:58 +02:00
|
|
|
@NotNull Function<NBTCompound, T> readFunction,
|
2021-06-26 05:08:33 +02:00
|
|
|
@Nullable BiConsumer<NBTCompound, T> writeConsumer) {
|
2021-05-28 16:19:58 +02:00
|
|
|
this(key, readFunction, writeConsumer, null);
|
2021-05-17 12:34:45 +02:00
|
|
|
}
|
|
|
|
|
2021-06-26 05:08:33 +02:00
|
|
|
/**
|
|
|
|
* Returns the key used to navigate inside the holder nbt.
|
|
|
|
* <p>
|
|
|
|
* Can be null if unused (e.g. {@link #View(TagSerializer)}, {@link #SNBT} and {@link #NBT}).
|
|
|
|
*
|
|
|
|
* @return the tag key
|
|
|
|
*/
|
|
|
|
public @Nullable String getKey() {
|
2021-05-17 12:34:45 +02:00
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2021-05-28 16:19:58 +02:00
|
|
|
@Contract(value = "_ -> new", pure = true)
|
2021-05-17 12:44:22 +02:00
|
|
|
public Tag<T> defaultValue(@NotNull Supplier<T> defaultValue) {
|
2021-05-28 16:19:58 +02:00
|
|
|
return new Tag<>(key, readFunction, writeConsumer, defaultValue);
|
2021-05-17 12:44:22 +02:00
|
|
|
}
|
|
|
|
|
2021-05-28 16:19:58 +02:00
|
|
|
@Contract(value = "_ -> new", pure = true)
|
2021-05-17 12:44:22 +02:00
|
|
|
public Tag<T> defaultValue(@NotNull T defaultValue) {
|
2021-05-28 16:21:54 +02:00
|
|
|
return defaultValue(() -> defaultValue);
|
2021-05-28 16:19:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Contract(value = "_, _ -> new", pure = true)
|
|
|
|
public <R> Tag<R> map(@NotNull Function<T, R> readMap,
|
|
|
|
@NotNull Function<R, T> writeMap) {
|
2021-06-22 02:51:04 +02:00
|
|
|
return new Tag<>(key,
|
2021-05-28 16:19:58 +02:00
|
|
|
// Read
|
|
|
|
nbtCompound -> {
|
|
|
|
final var old = readFunction.apply(nbtCompound);
|
|
|
|
if (old == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return readMap.apply(old);
|
|
|
|
},
|
|
|
|
// Write
|
|
|
|
(nbtCompound, r) -> {
|
|
|
|
var n = writeMap.apply(r);
|
|
|
|
writeConsumer.accept(nbtCompound, n);
|
|
|
|
},
|
|
|
|
// Default value
|
|
|
|
() -> {
|
|
|
|
if (defaultValue == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var old = defaultValue.get();
|
|
|
|
return readMap.apply(old);
|
|
|
|
});
|
2021-05-17 12:44:22 +02:00
|
|
|
}
|
|
|
|
|
2021-05-17 13:04:00 +02:00
|
|
|
public @Nullable T read(@NotNull NBTCompound nbtCompound) {
|
2021-06-22 02:51:04 +02:00
|
|
|
T result = readFunction.apply(nbtCompound);
|
|
|
|
if (result == null) {
|
2021-05-17 15:17:53 +02:00
|
|
|
final var supplier = defaultValue;
|
2021-06-22 02:51:04 +02:00
|
|
|
result = supplier != null ? supplier.get() : null;
|
2021-05-17 13:04:00 +02:00
|
|
|
}
|
2021-06-22 02:51:04 +02:00
|
|
|
return result;
|
2021-05-17 12:34:45 +02:00
|
|
|
}
|
|
|
|
|
2021-05-17 13:04:00 +02:00
|
|
|
public void write(@NotNull NBTCompound nbtCompound, @Nullable T value) {
|
2021-06-26 05:08:33 +02:00
|
|
|
if (key == null || value != null) {
|
2021-05-17 13:04:00 +02:00
|
|
|
this.writeConsumer.accept(nbtCompound, value);
|
|
|
|
} else {
|
|
|
|
nbtCompound.removeTag(key);
|
|
|
|
}
|
2021-05-17 12:34:45 +02:00
|
|
|
}
|
|
|
|
|
2021-06-22 02:51:04 +02:00
|
|
|
public void writeUnsafe(@NotNull NBTCompound nbtCompound, @Nullable Object value) {
|
|
|
|
write(nbtCompound, (T) value);
|
|
|
|
}
|
|
|
|
|
2021-05-17 12:34:45 +02:00
|
|
|
public static @NotNull Tag<Byte> Byte(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getByte(key),
|
|
|
|
(nbtCompound, value) -> nbtCompound.setByte(key, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NotNull Tag<Short> Short(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getShort(key),
|
|
|
|
(nbtCompound, value) -> nbtCompound.setShort(key, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NotNull Tag<Integer> Integer(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getInt(key),
|
|
|
|
(nbtCompound, integer) -> nbtCompound.setInt(key, integer));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NotNull Tag<Long> Long(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getLong(key),
|
|
|
|
(nbtCompound, value) -> nbtCompound.setLong(key, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NotNull Tag<Float> Float(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getFloat(key),
|
|
|
|
(nbtCompound, value) -> nbtCompound.setFloat(key, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NotNull Tag<Double> Double(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getDouble(key),
|
|
|
|
(nbtCompound, value) -> nbtCompound.setDouble(key, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NotNull Tag<byte[]> ByteArray(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getByteArray(key),
|
|
|
|
(nbtCompound, value) -> nbtCompound.setByteArray(key, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NotNull Tag<String> String(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getString(key),
|
|
|
|
(nbtCompound, value) -> nbtCompound.setString(key, value));
|
|
|
|
}
|
|
|
|
|
2021-06-22 02:51:04 +02:00
|
|
|
public static <T extends NBT> @NotNull Tag<T> NBT(@NotNull String key) {
|
2021-05-17 12:34:45 +02:00
|
|
|
return new Tag<>(key,
|
|
|
|
nbt -> {
|
2021-06-22 02:51:04 +02:00
|
|
|
final var currentNBT = nbt.get(key);
|
2021-05-17 12:34:45 +02:00
|
|
|
// Avoid a NPE when cloning a null variable.
|
|
|
|
if (currentNBT == null) {
|
|
|
|
return null;
|
|
|
|
}
|
2021-06-22 02:51:04 +02:00
|
|
|
return (T) currentNBT.deepClone();
|
2021-05-17 12:34:45 +02:00
|
|
|
},
|
|
|
|
((nbt, value) -> nbt.set(key, value.deepClone())));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NotNull Tag<int[]> IntArray(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getIntArray(key),
|
|
|
|
(nbtCompound, value) -> nbtCompound.setIntArray(key, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NotNull Tag<long[]> LongArray(@NotNull String key) {
|
|
|
|
return new Tag<>(key,
|
|
|
|
nbtCompound -> nbtCompound.getLongArray(key),
|
|
|
|
(nbtCompound, value) -> nbtCompound.setLongArray(key, value));
|
|
|
|
}
|
2021-05-17 14:02:14 +02:00
|
|
|
|
2021-06-22 02:51:04 +02:00
|
|
|
/**
|
|
|
|
* Create a wrapper around a compound.
|
|
|
|
*
|
|
|
|
* @param key the tag key
|
|
|
|
* @param serializer the tag serializer
|
|
|
|
* @param <T> the tag type
|
|
|
|
* @return the created tag
|
|
|
|
*/
|
|
|
|
public static <T> @NotNull Tag<T> Structure(@NotNull String key, @NotNull TagSerializer<T> serializer) {
|
2021-05-17 14:02:14 +02:00
|
|
|
return new Tag<>(key,
|
2021-05-28 16:19:58 +02:00
|
|
|
nbtCompound -> {
|
|
|
|
final var compound = nbtCompound.getCompound(key);
|
|
|
|
if (compound == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return serializer.read(TagReadable.fromCompound(compound));
|
|
|
|
},
|
|
|
|
(nbtCompound, value) -> {
|
|
|
|
var compound = nbtCompound.getCompound(key);
|
|
|
|
if (compound == null) {
|
|
|
|
compound = new NBTCompound();
|
|
|
|
nbtCompound.set(key, compound);
|
|
|
|
}
|
|
|
|
serializer.write(TagWritable.fromCompound(compound), value);
|
|
|
|
});
|
2021-05-17 14:02:14 +02:00
|
|
|
}
|
2021-06-22 02:51:04 +02:00
|
|
|
|
|
|
|
public static <T> @NotNull Tag<T> View(@NotNull TagSerializer<T> serializer) {
|
2021-06-26 05:08:33 +02:00
|
|
|
return new Tag<>(null,
|
2021-06-22 02:51:04 +02:00
|
|
|
nbtCompound -> serializer.read(TagReadable.fromCompound(nbtCompound)),
|
|
|
|
(nbtCompound, value) -> serializer.write(TagWritable.fromCompound(nbtCompound), value));
|
|
|
|
}
|
2021-06-22 03:05:22 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @deprecated use {@link #Structure(String, TagSerializer)} instead
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
public static <T> @NotNull Tag<T> Custom(@NotNull String key, @NotNull TagSerializer<T> serializer) {
|
|
|
|
return Structure(key, serializer);
|
|
|
|
}
|
2021-05-17 12:34:45 +02:00
|
|
|
}
|