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 all commits
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
12 changes: 12 additions & 0 deletions src/main/java/com/laytonsmith/abstraction/Convertor.java
Original file line number Diff line number Diff line change
Expand Up @@ -269,4 +269,16 @@ public interface Convertor {
* @return The username
*/
String GetUser(Environment env);

/**
* Returns a Minecraft namespaced key object from a string.
* The key can only alphanumeric characters, dots, underscores, and dashes.
* A preceding namespace can be delimited with a single colon, which can also have forward slashes.
* Example: "path/commandhelper:my_tag".
* If no namespace is given, it will default to "commandhelper".
*
* @param key a string formatted key
* @return a key object
*/
MCNamespacedKey GetNamespacedKey(String key);
}
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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.laytonsmith.abstraction;

public interface MCNamespacedKey extends AbstractionObject {
}
65 changes: 65 additions & 0 deletions src/main/java/com/laytonsmith/abstraction/MCTagContainer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.laytonsmith.abstraction;

import com.laytonsmith.abstraction.enums.MCTagType;

import java.util.Set;

/**
* 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 namespaced keys for each tag that exists in this container. (e.g. "namespace:key")
* @return a set of keys
*/
Set<MCNamespacedKey> 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(MCNamespacedKey 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(MCNamespacedKey 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(MCNamespacedKey key, MCTagType type, Object value);

/**
* Deletes the tag with the given key from this container.
* @param key the tag key
*/
void remove(MCNamespacedKey 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 @@ -18,6 +18,7 @@
import com.laytonsmith.abstraction.MCItemStack;
import com.laytonsmith.abstraction.MCLocation;
import com.laytonsmith.abstraction.MCMetadataValue;
import com.laytonsmith.abstraction.MCNamespacedKey;
import com.laytonsmith.abstraction.MCNote;
import com.laytonsmith.abstraction.MCPattern;
import com.laytonsmith.abstraction.MCPlugin;
Expand Down Expand Up @@ -871,4 +872,9 @@ public String GetUser(Environment env) {
return name;
}
}

@Override
public MCNamespacedKey GetNamespacedKey(String key) {
return new BukkitMCNamespacedKey(NamespacedKey.fromString(key, CommandHelperPlugin.self));
}
}
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,33 @@
package com.laytonsmith.abstraction.bukkit;

import com.laytonsmith.abstraction.MCNamespacedKey;
import org.bukkit.NamespacedKey;

public class BukkitMCNamespacedKey implements MCNamespacedKey {

NamespacedKey nsk;

public BukkitMCNamespacedKey(NamespacedKey nsk) {
this.nsk = nsk;
}

@Override
public Object getHandle() {
return this.nsk;
}

@Override
public String toString() {
return this.nsk.toString();
}

@Override
public boolean equals(Object obj) {
return obj instanceof MCNamespacedKey && this.nsk.equals(((MCNamespacedKey) obj).getHandle());
}

@Override
public int hashCode() {
return this.nsk.hashCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.laytonsmith.abstraction.bukkit;

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

import java.util.HashSet;
import java.util.Set;

public class BukkitMCTagContainer implements MCTagContainer {

PersistentDataContainer pdc;

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

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

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

@Override
public Set<MCNamespacedKey> getKeys() {
Set<MCNamespacedKey> keys = new HashSet<>();
for(NamespacedKey key : this.pdc.getKeys()) {
keys.add(new BukkitMCNamespacedKey(key));
}
return keys;
}

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

@Override
public Object get(MCNamespacedKey key, MCTagType type) {
PersistentDataType bukkitType = GetPersistentDataType(type);
Object value = this.pdc.get((NamespacedKey) key.getHandle(), 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(MCNamespacedKey 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;
}
this.pdc.set((NamespacedKey) key.getHandle(), bukkitType, value);
}

@Override
public void remove(MCNamespacedKey key) {
this.pdc.remove((NamespacedKey) key.getHandle());
}

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 this.pdc;
}

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

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

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