Minestom/src/main/java/net/minestom/server/tag/Tag.java

216 lines
7.2 KiB
Java
Raw Normal View History

2021-05-17 12:34:45 +02:00
package net.minestom.server.tag;
2022-03-23 06:22:48 +01:00
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
2022-03-20 01:47:57 +01:00
import net.minestom.server.item.ItemStack;
2021-05-17 17:46:56 +02:00
import org.jetbrains.annotations.ApiStatus;
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;
2022-03-20 01:47:57 +01:00
import org.jglrxavpok.hephaistos.nbt.*;
2021-12-13 16:41:30 +01:00
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
2021-05-17 12:34:45 +02:00
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> {
2022-03-23 06:22:48 +01:00
private static final Object2IntOpenHashMap<String> INDEX_MAP = new Object2IntOpenHashMap<>();
private static int lastIndex = 0; // Synchronized on INDEX_MAP
static {
INDEX_MAP.defaultReturnValue(-1);
}
2021-06-22 02:51:04 +02:00
2021-05-17 12:34:45 +02:00
private final String key;
2022-03-20 01:47:57 +01:00
private final Function<NBT, T> readFunction;
private final Function<T, NBT> writeFunction;
private final Supplier<T> defaultValue;
2021-05-17 12:44:22 +02:00
2022-03-20 01:47:57 +01:00
final int index;
protected Tag(@NotNull String key,
@NotNull Function<NBT, T> readFunction,
@NotNull Function<T, NBT> writeFunction,
@Nullable Supplier<T> defaultValue) {
2021-05-17 12:34:45 +02:00
this.key = key;
this.readFunction = readFunction;
2022-03-20 01:47:57 +01:00
this.writeFunction = writeFunction;
this.defaultValue = defaultValue;
2022-03-20 01:47:57 +01:00
2022-03-23 06:22:48 +01:00
// Potentially very hot code!
// First try to get the index from the cpu cache
// If it's not there, synchronization is required
int index = INDEX_MAP.getInt(key);
if (index == -1) {
synchronized (INDEX_MAP) {
index = INDEX_MAP.getInt(key);
if (index == -1) {
index = lastIndex++;
INDEX_MAP.put(key, index);
}
}
}
this.index = index;
}
2022-03-20 01:47:57 +01:00
static <T, N extends NBT> Tag<T> tag(@NotNull String key,
@NotNull Class<N> nbtClass,
@NotNull Function<N, T> readFunction,
@NotNull Function<T, N> writeFunction) {
return new Tag<T>(key, (Function<NBT, T>) readFunction, (Function<T, NBT>) writeFunction, 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.
*
* @return the tag key
*/
2022-03-20 01:47:57 +01:00
public @NotNull String getKey() {
2021-05-17 12:34:45 +02:00
return key;
}
@Contract(value = "_ -> new", pure = true)
2021-05-17 12:44:22 +02:00
public Tag<T> defaultValue(@NotNull Supplier<T> defaultValue) {
2022-03-20 01:47:57 +01:00
return new Tag<>(key, readFunction, writeFunction, defaultValue);
2021-05-17 12:44:22 +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);
}
@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,
// Read
2022-03-20 01:47:57 +01:00
readFunction.andThen(t -> {
if (t == null) return null;
return readMap.apply(t);
}),
// Write
2022-03-20 01:47:57 +01:00
writeMap.andThen(writeFunction),
// Default value
2022-03-20 01:47:57 +01:00
() -> readMap.apply(createDefault()));
2021-05-17 12:44:22 +02:00
}
2022-03-20 01:47:57 +01:00
public @Nullable T read(@NotNull NBTCompoundLike nbt) {
final String key = this.key;
if (key.isEmpty()) {
// Special handling for view tag
return convertToValue(nbt.toCompound());
2021-05-17 13:04:00 +02:00
}
2022-03-20 01:47:57 +01:00
final NBT subTag = nbt.get(key);
return convertToValue(subTag);
}
T createDefault() {
final var supplier = defaultValue;
return supplier != null ? supplier.get() : null;
2021-05-17 12:34:45 +02:00
}
2021-12-13 16:41:30 +01:00
public void write(@NotNull MutableNBTCompound nbtCompound, @Nullable T value) {
2022-03-20 01:47:57 +01:00
final String key = this.key;
if (value != null) {
final NBT nbt = writeFunction.apply(value);
if (key.isEmpty()) nbtCompound.copyFrom((NBTCompoundLike) nbt);
else nbtCompound.set(key, nbt);
2021-05-17 13:04:00 +02:00
} else {
2022-03-20 01:47:57 +01:00
if (key.isEmpty()) nbtCompound.clear();
else nbtCompound.remove(key);
2021-05-17 13:04:00 +02:00
}
2021-05-17 12:34:45 +02:00
}
2021-12-13 16:41:30 +01:00
public void writeUnsafe(@NotNull MutableNBTCompound nbtCompound, @Nullable Object value) {
//noinspection unchecked
2021-06-22 02:51:04 +02:00
write(nbtCompound, (T) value);
}
2022-03-20 01:47:57 +01:00
T convertToValue(NBT nbt) {
final T result;
try {
if (nbt == null || (result = readFunction.apply(nbt)) == null)
return createDefault();
return result;
} catch (ClassCastException e) {
return createDefault();
}
}
NBT convertToNbt(T value) {
return writeFunction.apply(value);
}
2021-05-17 12:34:45 +02:00
public static @NotNull Tag<Byte> Byte(@NotNull String key) {
2022-03-20 01:47:57 +01:00
return tag(key, NBTByte.class, NBTByte::getValue, NBT::Byte);
2021-05-17 12:34:45 +02:00
}
public static @NotNull Tag<Short> Short(@NotNull String key) {
2022-03-20 01:47:57 +01:00
return tag(key, NBTShort.class, NBTShort::getValue, NBT::Short);
2021-05-17 12:34:45 +02:00
}
public static @NotNull Tag<Integer> Integer(@NotNull String key) {
2022-03-20 01:47:57 +01:00
return tag(key, NBTInt.class, NBTInt::getValue, NBT::Int);
2021-05-17 12:34:45 +02:00
}
public static @NotNull Tag<Long> Long(@NotNull String key) {
2022-03-20 01:47:57 +01:00
return tag(key, NBTLong.class, NBTLong::getValue, NBT::Long);
2021-05-17 12:34:45 +02:00
}
public static @NotNull Tag<Float> Float(@NotNull String key) {
2022-03-20 01:47:57 +01:00
return tag(key, NBTFloat.class, NBTFloat::getValue, NBT::Float);
2021-05-17 12:34:45 +02:00
}
public static @NotNull Tag<Double> Double(@NotNull String key) {
2022-03-20 01:47:57 +01:00
return tag(key, NBTDouble.class, NBTDouble::getValue, NBT::Double);
2021-05-17 12:34:45 +02:00
}
public static @NotNull Tag<String> String(@NotNull String key) {
2022-03-20 01:47:57 +01:00
return tag(key, NBTString.class, NBTString::getValue, NBT::String);
2021-05-17 12:34:45 +02:00
}
2021-06-22 02:51:04 +02:00
public static <T extends NBT> @NotNull Tag<T> NBT(@NotNull String key) {
//noinspection unchecked
2022-03-20 01:47:57 +01:00
return tag(key, NBT.class, nbt -> (T) nbt, t -> t);
2021-05-17 12:34:45 +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) {
2022-03-20 01:47:57 +01:00
return tag(key, NBTCompound.class,
nbt -> serializer.read(TagHandler.fromCompound(nbt)),
(value) -> {
TagHandler handler = TagHandler.newHandler();
serializer.write(handler, value);
return handler.asCompound();
});
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) {
2022-03-20 01:47:57 +01:00
return tag("", NBTCompound.class,
nbt -> serializer.read(TagHandler.fromCompound(nbt)),
(value) -> {
TagHandler handler = TagHandler.newHandler();
serializer.write(handler, value);
return handler.asCompound();
});
}
2022-03-20 01:47:57 +01:00
public static @NotNull Tag<ItemStack> ItemStack(@NotNull String key) {
return tag(key, NBTCompound.class, ItemStack::fromItemNBT, ItemStack::toItemNBT);
2021-06-22 03:05:22 +02:00
}
2021-05-17 12:34:45 +02:00
}