Skip to content

Commit

Permalink
Add support for custom tags in item meta
Browse files Browse the repository at this point in the history
  • Loading branch information
PseudoKnight committed Sep 19, 2023
1 parent 91618b7 commit 1a9de89
Show file tree
Hide file tree
Showing 8 changed files with 484 additions and 0 deletions.
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();

/**
* 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

0 comments on commit 1a9de89

Please sign in to comment.