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

Equippable components #7194

Open
wants to merge 9 commits into
base: dev/feature
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions src/main/java/ch/njol/skript/Skript.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
import org.skriptlang.skript.bukkit.breeding.BreedingModule;
import org.skriptlang.skript.bukkit.displays.DisplayModule;
import org.skriptlang.skript.bukkit.input.InputModule;
import org.skriptlang.skript.bukkit.equippablecomponents.EquippableComponentModule;
import org.skriptlang.skript.lang.comparator.Comparator;
import org.skriptlang.skript.lang.comparator.Comparators;
import org.skriptlang.skript.lang.converter.Converter;
Expand Down Expand Up @@ -537,6 +538,8 @@ public void onEnable() {
BreedingModule.load();
DisplayModule.load();
InputModule.load();
BreedingModule.load();
EquippableComponentModule.load();
} catch (final Exception e) {
exception(e, "Could not load required .class files: " + e.getLocalizedMessage());
setEnabled(false);
Expand Down
111 changes: 111 additions & 0 deletions src/main/java/ch/njol/skript/bukkitutil/NamespacedUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package ch.njol.skript.bukkitutil;

import ch.njol.skript.Skript;
import ch.njol.util.Pair;
import com.google.common.collect.Sets;
import org.apache.commons.lang.ArrayUtils;
import org.bukkit.NamespacedKey;

import java.util.Set;

public class NamespacedUtils {

private static final Set<Character> LEGAL_NAMESPACE_CHARS = Sets.newHashSet(ArrayUtils.toObject("abcdefghijklmnopqrstuvwxyz0123456789._-/".toCharArray()));

/**
* Gets a namespaced key. This method will try to get existing keys first, but if that fails
* it will create the key in Skript's namespace.
*
* @param key the unparsed key
* @return the resulting NamespacedKey
*/
public static NamespacedKey getNamespacedKey(String key) {
NamespacedKey namespacedKey = NamespacedKey.fromString(key, Skript.getInstance());
if (namespacedKey != null)
return namespacedKey;

return createNamespacedKey(key);
}

/**
* Creates a namespaced key in Skript's namespace encoded to avoid the character limitations of a normal key.
* This key will be created in Skript's namespace.
*
* @param key The key to use
* @return a NamespacedKey with the encoded key in corresponding Namespace
*/
public static NamespacedKey createNamespacedKey(String key) {
StringBuilder encodedKeyBuilder = new StringBuilder();
// keys must be all lowercase
key = key.toLowerCase().replace(' ', '_');
int keyLength = key.length();
for (int i = 0; i < keyLength; i++) {
char currentChar = key.charAt(i);
// if this character is legal to use in a namespace key
if (LEGAL_NAMESPACE_CHARS.contains(currentChar)) {
// if the original string had a ".x" in it, we need to escape it
// so decoding doesn't think it's a hex sequence
if (currentChar == '.' && key.charAt(i + 1) == 'x') {
i += 1; // skip the "x"
encodedKeyBuilder.append(".x");
encodedKeyBuilder.append(Integer.toHexString('.'));
encodedKeyBuilder.append(".x");
encodedKeyBuilder.append(Integer.toHexString('x'));
// if we're not at the end and the next char is a legal char, add the trailing dot
// to represent the end of the hex sequence
if (i != (keyLength - 1) && LEGAL_NAMESPACE_CHARS.contains(key.charAt(i + 1)))
encodedKeyBuilder.append('.');
} else {
// we are dealing with a legal character, so we can just append it
encodedKeyBuilder.append(currentChar);
}
} else {
// add ".x(hex code)" to the encoded key
encodedKeyBuilder.append(".x");
encodedKeyBuilder.append(Integer.toHexString(currentChar));
// only add the trailing dot if the next character is a legal character
if (i != (keyLength - 1) && LEGAL_NAMESPACE_CHARS.contains(key.charAt(i + 1)))
encodedKeyBuilder.append('.');
}
}
return NamespacedKey.fromString(encodedKeyBuilder.toString(), Skript.getInstance());
}

/**
* Decodes a NamespacedKey encoded by #getNamespacedKey
*
* @param namespacedKey the namespaced key to decode
* @return a Pair with the first element as the namespace and the second as the decoded key
*/
public static Pair<String, String> decodeNamespacedKey(NamespacedKey namespacedKey) {
String encodedKey = namespacedKey.getKey();
StringBuilder decodedKeyBuilder = new StringBuilder();
int encodedKeyLength = encodedKey.length();
int lastCharIndex = encodedKeyLength - 1;
for (int i = 0; i < encodedKeyLength; i++) {
char currentChar = encodedKey.charAt(i);
// if we are encountering a ".x" hex sequence
if (i != lastCharIndex && currentChar == '.' && encodedKey.charAt(i + 1) == 'x') {
i += 2; // skip the ".x" so it isn't part of our hex string
StringBuilder hexString = new StringBuilder();
// The hex sequence continues until a . is encountered or we reach the end of the string
while (i <= lastCharIndex && encodedKey.charAt(i) != '.') {
hexString.append(encodedKey.charAt(i));
i++;
}
// if the . was the start of another ".x" hex sequence, back up by 1 character
if (i <= lastCharIndex && encodedKey.charAt(i + 1) == 'x')
i--;
// parse the hex sequence to a char
char decodedChar = (char) Long.parseLong(hexString.toString(), 16);
decodedKeyBuilder.append(decodedChar);
} else {
// this is just a normal character, not a hex sequence
// so we can just append it
decodedKeyBuilder.append(currentChar);
}
}
return new Pair<>(namespacedKey.getNamespace(), decodedKeyBuilder.toString());
}

}
23 changes: 17 additions & 6 deletions src/main/java/ch/njol/skript/bukkitutil/SoundUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.Sound;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -18,17 +19,15 @@ public final class SoundUtils {

/**
* Gets the key of a sound, given its enum-name-style name.
* If no sound is found, will create a new namespaced key from the given soundString.
* @param soundString The enum name to use to find the sound.
* @return The key of the sound.
*/
public static @Nullable NamespacedKey getKey(String soundString) {
soundString = soundString.toUpperCase(Locale.ENGLISH);
if (SOUND_IS_INTERFACE) {
try {
//noinspection deprecation
return Sound.valueOf(soundString).getKey();
} catch (IllegalArgumentException ignore) {
}
//noinspection deprecation
return Sound.valueOf(soundString).getKey();
} else {
try {
//noinspection unchecked,rawtypes
Expand All @@ -37,7 +36,7 @@ public final class SoundUtils {
} catch (IllegalArgumentException ignore) {
}
}
return null;
return NamespacedKey.fromString(soundString.toLowerCase(Locale.ENGLISH));
}

/**
Expand All @@ -54,4 +53,16 @@ public final class SoundUtils {
}
}

/**
* returns the sound of a given string.
* @param soundString The sound string to get the sound
* @return The sound if found
*/
public static Sound getSound(String soundString) {
NamespacedKey key = getKey(soundString);
if (key == null)
return null;
return Registry.SOUNDS.get(key);
}

}
30 changes: 1 addition & 29 deletions src/main/java/ch/njol/skript/effects/EffPlaySound.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
import ch.njol.skript.Skript;
import ch.njol.skript.bukkitutil.SoundUtils;
import ch.njol.skript.bukkitutil.sounds.SoundReceiver;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.RequiredPlugins;
import ch.njol.skript.doc.Since;
import ch.njol.skript.doc.*;
import ch.njol.skript.lang.Effect;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
Expand All @@ -23,10 +19,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.OptionalLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Name("Play Sound")
@Description({
Expand Down Expand Up @@ -73,8 +66,6 @@ public class EffPlaySound extends Effect {
private static final boolean ENTITY_EMITTER_SOUND = Skript.methodExists(Player.class, "playSound", Entity.class, Sound.class, SoundCategory.class, float.class, float.class);
private static final boolean ENTITY_EMITTER_STRING = Skript.methodExists(Player.class, "playSound", Entity.class, String.class, SoundCategory.class, float.class, float.class);
private static final boolean ENTITY_EMITTER = ENTITY_EMITTER_SOUND || ENTITY_EMITTER_STRING;

public static final Pattern KEY_PATTERN = Pattern.compile("([a-z0-9._-]+:)?([a-z0-9/._-]+)");

static {
String seedOption = HAS_SEED ? "[[with] seed %-number%] " : "";
Expand Down Expand Up @@ -145,25 +136,6 @@ protected void execute(Event event) {
List<NamespacedKey> validSounds = new ArrayList<>();
for (String sound : sounds.getArray(event)) {
NamespacedKey key = SoundUtils.getKey(sound);
if (key == null) {
sound = sound.toLowerCase(Locale.ENGLISH);
Matcher keyMatcher = KEY_PATTERN.matcher(sound);
if (!keyMatcher.matches())
continue;
try {
String namespace = keyMatcher.group(1);
String keyValue = keyMatcher.group(2);
if (namespace == null) {
key = NamespacedKey.minecraft(keyValue);
} else {
namespace = namespace.substring(0, namespace.length() - 1);
key = new NamespacedKey(namespace, keyValue);
}
} catch (IllegalArgumentException argument) {
// The user input invalid characters
}
}

if (key == null)
continue;
validSounds.add(key);
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/ch/njol/skript/util/slot/EquipmentSlot.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public ItemStack get(final EntityEquipment e) {
public void set(final EntityEquipment e, final @Nullable ItemStack item) {
e.setItemInMainHand(item);
}

@Override
public org.bukkit.inventory.EquipmentSlot getBukkitEquipSlot() {
return org.bukkit.inventory.EquipmentSlot.HAND;
}
},
OFF_HAND(40) {

Expand All @@ -61,6 +66,11 @@ public ItemStack get(EntityEquipment e) {
public void set(EntityEquipment e, @Nullable ItemStack item) {
e.setItemInOffHand(item);
}

@Override
public org.bukkit.inventory.EquipmentSlot getBukkitEquipSlot() {
return org.bukkit.inventory.EquipmentSlot.OFF_HAND;
}

},
HELMET(39) {
Expand All @@ -74,6 +84,11 @@ public ItemStack get(final EntityEquipment e) {
public void set(final EntityEquipment e, final @Nullable ItemStack item) {
e.setHelmet(item);
}

@Override
public org.bukkit.inventory.EquipmentSlot getBukkitEquipSlot() {
return org.bukkit.inventory.EquipmentSlot.HEAD;
}
},
CHESTPLATE(38) {
@Override
Expand All @@ -86,6 +101,11 @@ public ItemStack get(final EntityEquipment e) {
public void set(final EntityEquipment e, final @Nullable ItemStack item) {
e.setChestplate(item);
}

@Override
public org.bukkit.inventory.EquipmentSlot getBukkitEquipSlot() {
return org.bukkit.inventory.EquipmentSlot.CHEST;
}
},
LEGGINGS(37) {
@Override
Expand All @@ -98,6 +118,11 @@ public ItemStack get(final EntityEquipment e) {
public void set(final EntityEquipment e, final @Nullable ItemStack item) {
e.setLeggings(item);
}

@Override
public org.bukkit.inventory.EquipmentSlot getBukkitEquipSlot() {
return org.bukkit.inventory.EquipmentSlot.LEGS;
}
},
BOOTS(36) {
@Override
Expand All @@ -110,6 +135,11 @@ public ItemStack get(final EntityEquipment e) {
public void set(final EntityEquipment e, final @Nullable ItemStack item) {
e.setBoots(item);
}

@Override
public org.bukkit.inventory.EquipmentSlot getBukkitEquipSlot() {
return org.bukkit.inventory.EquipmentSlot.FEET;
}
};

public final int slotNumber;
Expand All @@ -126,6 +156,8 @@ public void set(final EntityEquipment e, final @Nullable ItemStack item) {
public abstract ItemStack get(EntityEquipment e);

public abstract void set(EntityEquipment e, @Nullable ItemStack item);

public abstract org.bukkit.inventory.EquipmentSlot getBukkitEquipSlot();

}

Expand Down Expand Up @@ -204,6 +236,18 @@ public int getIndex() {
return slotIndex != -1 ? slotIndex : slot.slotNumber;
}

public static EquipSlot convertToSkriptEquipSlot(org.bukkit.inventory.EquipmentSlot bukkitSlot) {
return switch (bukkitSlot) {
case HEAD -> EquipSlot.HELMET;
case CHEST -> EquipSlot.CHESTPLATE;
case LEGS -> EquipSlot.LEGGINGS;
case FEET -> EquipSlot.BOOTS;
case HAND -> EquipSlot.TOOL;
case OFF_HAND -> EquipSlot.OFF_HAND;
default -> null;
};
}

@Override
public String toString(@Nullable Event event, boolean debug) {
if (slotToString) // Slot to string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.skriptlang.skript.bukkit.equippablecomponents;

import ch.njol.skript.Skript;
import ch.njol.skript.classes.ClassInfo;
import ch.njol.skript.classes.EnumClassInfo;
import ch.njol.skript.registrations.Classes;
import ch.njol.skript.util.slot.EquipmentSlot.EquipSlot;
import org.bukkit.inventory.meta.components.EquippableComponent;
import org.skriptlang.skript.lang.comparator.Comparator;
import org.skriptlang.skript.lang.comparator.Comparators;
import org.skriptlang.skript.lang.comparator.Relation;

import java.io.IOException;

public class EquippableComponentModule {

public static void load() throws IOException {
if (!Skript.classExists("org.bukkit.inventory.meta.components.EquippableComponent"))
return;

Skript.getAddonInstance().loadClasses("org.skriptlang.skript.bukkit.equippablecomponents", "elements");

Classes.registerClass(new EnumClassInfo<>(EquipSlot.class, "equipmentslot", "equipment slot")
.user("equipment ?slots?")
.name("Equipment Slot")
.description("Represents an equipment slot")
.since("INSERT VERSION")
);

Classes.registerClass(new ClassInfo<>(EquippableComponent.class, "equippablecomponent")
.user("equippable ?components?")
.name("Equippable Components")
.description("Represents an equippable component used for items.")
.requiredPlugins("Minecraft 1.21.2+")
.since("INSERT VERSION")
);

Comparators.registerComparator(EquipSlot.class, EquipSlot.class, new Comparator<EquipSlot, EquipSlot>() {
@Override
public Relation compare(EquipSlot slot1, EquipSlot slot2) {
return Relation.get(slot2.equals(slot1));
}
});

}

}
Loading