package org.bukkit.craftbukkit.persistence; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import org.apache.commons.lang.Validate; import org.bukkit.NamespacedKey; import org.bukkit.craftbukkit.util.CraftNBTTagConfigSerializer; import org.bukkit.persistence.PersistentDataAdapterContext; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; public final class CraftPersistentDataContainer implements PersistentDataContainer { private static final Callback EMPTY = () -> { }; private final Map customDataTags = new HashMap<>(); private final CraftPersistentDataTypeRegistry registry; private final CraftPersistentDataAdapterContext adapterContext; private Callback callback = EMPTY; public CraftPersistentDataContainer(Map customTags, CraftPersistentDataTypeRegistry registry) { this(registry); this.customDataTags.putAll(customTags); } public CraftPersistentDataContainer(CraftPersistentDataTypeRegistry registry) { this.registry = registry; this.adapterContext = new CraftPersistentDataAdapterContext(this.registry); } public void setCallback(Callback callback) { if (callback == null) { this.callback = EMPTY; return; } this.callback = callback; } @Override public void set(NamespacedKey key, PersistentDataType type, Z value) { Validate.notNull(key, "The provided key for the custom value was null"); Validate.notNull(type, "The provided type for the custom value was null"); Validate.notNull(value, "The provided value for the custom value was null"); this.customDataTags.put(key.toString(), registry.wrap(type.getPrimitiveType(), type.toPrimitive(value, adapterContext))); callback.onValueChange(); } @Override public boolean has(NamespacedKey key, PersistentDataType type) { Validate.notNull(key, "The provided key for the custom value was null"); Validate.notNull(type, "The provided type for the custom value was null"); NBTBase value = this.customDataTags.get(key.toString()); if (value == null) { return false; } return registry.isInstanceOf(type.getPrimitiveType(), value); } @Override public Z get(NamespacedKey key, PersistentDataType type) { Validate.notNull(key, "The provided key for the custom value was null"); Validate.notNull(type, "The provided type for the custom value was null"); NBTBase value = this.customDataTags.get(key.toString()); if (value == null) { return null; } return type.fromPrimitive(registry.extract(type.getPrimitiveType(), value), adapterContext); } @Override public Z getOrDefault(NamespacedKey key, PersistentDataType type, Z defaultValue) { Z z = get(key, type); return z != null ? z : defaultValue; } @Override public Set getKeys() { Set keys = new HashSet<>(); this.customDataTags.keySet().forEach(key -> { String[] keyData = key.split(":", 2); if (keyData.length == 2) { keys.add(new NamespacedKey(keyData[0], keyData[1])); } }); return keys; } @Override public void remove(NamespacedKey key) { Validate.notNull(key, "The provided key for the custom value was null"); this.customDataTags.remove(key.toString()); callback.onValueChange(); } @Override public boolean isEmpty() { return this.customDataTags.isEmpty(); } @Override public PersistentDataAdapterContext getAdapterContext() { return this.adapterContext; } @Override public boolean equals(Object obj) { if (!(obj instanceof CraftPersistentDataContainer)) { return false; } Map myRawMap = getRaw(); Map theirRawMap = ((CraftPersistentDataContainer) obj).getRaw(); return Objects.equals(myRawMap, theirRawMap); } public NBTTagCompound toTagCompound() { NBTTagCompound tag = new NBTTagCompound(); for (Entry entry : this.customDataTags.entrySet()) { tag.put(entry.getKey(), entry.getValue()); } return tag; } public void put(String key, NBTBase base) { this.customDataTags.put(key, base); callback.onValueChange(); } public void putAll(Map map) { this.customDataTags.putAll(map); callback.onValueChange(); } public void putAll(NBTTagCompound compound) { for (String key : compound.getAllKeys()) { this.customDataTags.put(key, compound.get(key)); } callback.onValueChange(); } public Map getRaw() { return this.customDataTags; } public CraftPersistentDataTypeRegistry getDataTagTypeRegistry() { return registry; } @Override public int hashCode() { int hashCode = 3; hashCode += this.customDataTags.hashCode(); // We will simply add the maps hashcode return hashCode; } public Map serialize() { return (Map) CraftNBTTagConfigSerializer.serialize(toTagCompound()); } @FunctionalInterface public interface Callback { void onValueChange(); } }