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

Add support for custom tags in item meta #1366

Merged
Merged
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
4 changes: 4 additions & 0 deletions src/main/java/com/laytonsmith/abstraction/MCItemMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,8 @@ public interface MCItemMeta extends AbstractionObject {
List<MCAttributeModifier> getAttributeModifiers();

void setAttributeModifiers(List<MCAttributeModifier> modifiers);

boolean hasCustomTags();

MCTagContainer getCustomTags();
}
67 changes: 67 additions & 0 deletions src/main/java/com/laytonsmith/abstraction/MCTagContainer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.laytonsmith.abstraction;

import com.laytonsmith.abstraction.enums.MCTagType;

import java.util.Collection;

/**
* Minecraft NBT containers that can be used to read and modify tags in supported game objects.
* This includes item meta, entities, block entities, chunks, worlds, etc.
*/
public interface MCTagContainer extends AbstractionObject {

/**
* Returns whether the tag container does not contain any tags.
* @return whether container is empty
*/
boolean isEmpty();

/**
* Gets a set of key objects for each tag that exists in this container.
* These are minecraft formatted namespaced keys. (e.g. "namespace:key")
* These key objects can be passed to other methods in this class.
* @return a set of keys
*/
Collection getKeys();
PseudoKnight marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns the tag type with the given key.
* MCTagType can be used to convert tags to and from MethodScript constructs.
* Returns null if a tag with that key does not exist.
* @param key the tag key
* @return the type for the tag
*/
MCTagType getType(Object key);

/**
* Returns the tag value with the given key and tag type.
* Returns null if a tag with that key and type does not exist.
* @param key the tag key
* @param type the tag type
* @return the value for the tag
*/
Object get(Object key, MCTagType type);

/**
* Sets the tag value with the given key and tag type.
* Throws an IllegalArgumentException if the type and value do not match.
* @param key the tag key
* @param type the tag type
* @param value the tag value
*/
void set(Object key, MCTagType type, Object value);

/**
* Deletes the tag with the given key from this container.
* @param key the tag key
*/
void remove(Object key);

/**
* Creates a new tag container from this container context.
* This can then be used to nest a tag container with the {@link #set} method.
* @return a new tag container
*/
MCTagContainer newContainer();

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.laytonsmith.abstraction.MCAttributeModifier;
import com.laytonsmith.abstraction.MCEnchantment;
import com.laytonsmith.abstraction.MCItemMeta;
import com.laytonsmith.abstraction.MCTagContainer;
import com.laytonsmith.abstraction.blocks.MCBlockData;
import com.laytonsmith.abstraction.blocks.MCMaterial;
import com.laytonsmith.abstraction.bukkit.blocks.BukkitMCBlockData;
Expand Down Expand Up @@ -225,4 +226,13 @@ public void setAttributeModifiers(List<MCAttributeModifier> modifiers) {
}
im.setAttributeModifiers(map);
}

@Override
public boolean hasCustomTags() {
return !im.getPersistentDataContainer().isEmpty();
}

public MCTagContainer getCustomTags() {
return new BukkitMCTagContainer(im.getPersistentDataContainer());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package com.laytonsmith.abstraction.bukkit;

import com.laytonsmith.abstraction.MCTagContainer;
import com.laytonsmith.abstraction.enums.MCTagType;
import com.laytonsmith.commandhelper.CommandHelperPlugin;
import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;

import java.util.Collection;

public class BukkitMCTagContainer implements MCTagContainer {

PersistentDataContainer pdc;

public BukkitMCTagContainer(PersistentDataContainer pdc) {
this.pdc = pdc;
}

@Override
public MCTagContainer newContainer() {
return new BukkitMCTagContainer(pdc.getAdapterContext().newPersistentDataContainer());
}

@Override
public boolean isEmpty() {
return this.pdc.isEmpty();
}

@Override
public Collection getKeys() {
return pdc.getKeys();
}

@Override
public MCTagType getType(Object key) {
NamespacedKey namespacedKey = NamespacedKey(key);
// Check tag types in order of most frequently used
if(pdc.has(namespacedKey, PersistentDataType.STRING)) {
return MCTagType.STRING;
} else if(pdc.has(namespacedKey, PersistentDataType.INTEGER)) {
return MCTagType.INTEGER;
} else if(pdc.has(namespacedKey, PersistentDataType.BYTE)) {
return MCTagType.BYTE;
} else if(pdc.has(namespacedKey, PersistentDataType.DOUBLE)) {
return MCTagType.DOUBLE;
} else if(pdc.has(namespacedKey, PersistentDataType.LONG)) {
return MCTagType.LONG;
} else if(pdc.has(namespacedKey, PersistentDataType.FLOAT)) {
return MCTagType.FLOAT;
} else if(pdc.has(namespacedKey, PersistentDataType.TAG_CONTAINER)) {
return MCTagType.TAG_CONTAINER;
} else if(pdc.has(namespacedKey, PersistentDataType.BYTE_ARRAY)) {
return MCTagType.BYTE_ARRAY;
} else if(pdc.has(namespacedKey, PersistentDataType.SHORT)) {
return MCTagType.SHORT;
} else if(pdc.has(namespacedKey, PersistentDataType.INTEGER_ARRAY)) {
return MCTagType.INTEGER_ARRAY;
} else if(pdc.has(namespacedKey, PersistentDataType.LONG_ARRAY)) {
return MCTagType.LONG_ARRAY;
} else if(pdc.has(namespacedKey, PersistentDataType.TAG_CONTAINER_ARRAY)) {
return MCTagType.TAG_CONTAINER_ARRAY;
}
return null;
}

@Override
public Object get(Object key, MCTagType type) {
PersistentDataType bukkitType = GetPersistentDataType(type);
Object value = pdc.get(NamespacedKey(key), bukkitType);
if(value instanceof PersistentDataContainer) {
return new BukkitMCTagContainer((PersistentDataContainer) value);
} else if(value instanceof PersistentDataContainer[] concreteContainers) {
MCTagContainer[] abstractContainers = new MCTagContainer[concreteContainers.length];
for(int i = 0; i < concreteContainers.length; i++) {
abstractContainers[i] = new BukkitMCTagContainer(concreteContainers[i]);
}
return abstractContainers;
}
return value;
}

@Override
public void set(Object key, MCTagType type, Object value) {
PersistentDataType bukkitType = GetPersistentDataType(type);
if(value instanceof MCTagContainer) {
value = ((MCTagContainer) value).getHandle();
} else if(value instanceof MCTagContainer[] abstractContainers) {
PersistentDataContainer[] concreteContainers = new PersistentDataContainer[abstractContainers.length];
for(int i = 0; i < abstractContainers.length; i++) {
concreteContainers[i] = (PersistentDataContainer) abstractContainers[i].getHandle();
}
value = concreteContainers;
}
pdc.set(NamespacedKey(key), bukkitType, value);
}

@Override
public void remove(Object key) {
pdc.remove(NamespacedKey(key));
}

private static NamespacedKey NamespacedKey(Object key) {
if(key instanceof NamespacedKey) {
return (NamespacedKey) key;
} else if(key instanceof String) {
NamespacedKey namespacedKey = NamespacedKey.fromString((String) key, CommandHelperPlugin.self);
if(namespacedKey != null) {
return namespacedKey;
}
}
throw new IllegalArgumentException("Invalid namespaced key.");
}

private static PersistentDataType GetPersistentDataType(MCTagType type) {
switch(type) {
case BYTE:
return PersistentDataType.BYTE;
case BYTE_ARRAY:
return PersistentDataType.BYTE_ARRAY;
case DOUBLE:
return PersistentDataType.DOUBLE;
case FLOAT:
return PersistentDataType.FLOAT;
case INTEGER:
return PersistentDataType.INTEGER;
case INTEGER_ARRAY:
return PersistentDataType.INTEGER_ARRAY;
case LONG:
return PersistentDataType.LONG;
case LONG_ARRAY:
return PersistentDataType.LONG_ARRAY;
case SHORT:
return PersistentDataType.SHORT;
case STRING:
return PersistentDataType.STRING;
case TAG_CONTAINER:
return PersistentDataType.TAG_CONTAINER;
case TAG_CONTAINER_ARRAY:
return PersistentDataType.TAG_CONTAINER_ARRAY;
}
throw new IllegalArgumentException("Invalid persistent data type: " + type.name());
}

@Override
public Object getHandle() {
return pdc;
}

@Override
public boolean equals(Object o) {
return o instanceof MCTagContainer && this.pdc.equals(((MCTagContainer) o).getHandle());
}

@Override
public int hashCode() {
return pdc.hashCode();
}

@Override
public String toString() {
return pdc.toString();
}
}
Loading
Loading