trackedTypes = new HashMap<>();
+
+ /**
+ * If user had an obscure config option set, don't crash due to missing
+ * Java item types.
+ */
+ private static final boolean noHardExceptions = SkriptConfig.apiSoftExceptions.value();
+ static String itemSingular = "item";
+ static String itemPlural = "items";
+ @Nullable
+ static String itemGender = null;
+ static String blockSingular = "block";
+ static String blockPlural = "blocks";
+ @Nullable
+ static String blockGender = null;
+
+ static {
+ everything.setAll(true);
+ ItemData all = new ItemData(Material.AIR);
+ all.isAnything = true;
+ everything.add(all);
+ }
+
@Nullable
private static ItemType getAlias_i(final String s) {
// Check script aliases first
@@ -71,10 +85,10 @@ private static ItemType getAlias_i(final String s) {
if (aliases != null) {
return aliases.provider.getAlias(s); // Delegates to global provider if needed
}
-
+
return provider.getAlias(s);
}
-
+
/**
* Creates an aliases provider with Skript's default configuration.
* @param expectedCount Expected alias count.
@@ -84,14 +98,14 @@ private static ItemType getAlias_i(final String s) {
private static AliasesProvider createProvider(int expectedCount, @Nullable AliasesProvider parent) {
return new AliasesProvider(expectedCount, parent);
}
-
+
/**
* Creates an aliases parser with Skript's default configuration.
* @return Aliases parser.
*/
private static AliasesParser createParser(AliasesProvider provider) {
AliasesParser parser = new AliasesParser(provider);
-
+
// Register standard conditions
parser.registerCondition("minecraft version", (str) -> {
int orNewer = str.indexOf("or newer"); // For example: 1.12 or newer
@@ -100,14 +114,14 @@ private static AliasesParser createParser(AliasesProvider provider) {
Version ver = new Version(str.substring(0, orNewer - 1));
return Skript.getMinecraftVersion().compareTo(ver) >= 0;
}
-
+
int orOlder = str.indexOf("or older"); // For example: 1.11 or older
if (orOlder != -1) {
@SuppressWarnings("null")
Version ver = new Version(str.substring(0, orOlder - 1));
return Skript.getMinecraftVersion().compareTo(ver) <= 0;
}
-
+
int to = str.indexOf("to"); // For example: 1.11 to 1.12
if (to != -1) {
@SuppressWarnings("null")
@@ -117,39 +131,17 @@ private static AliasesParser createParser(AliasesProvider provider) {
Version current = Skript.getMinecraftVersion();
return current.compareTo(first) >= 0 && current.compareTo(second) <= 0;
}
-
+
return Skript.getMinecraftVersion().equals(new Version(str));
});
-
+
return parser;
}
- static String itemSingular = "item";
- static String itemPlural = "items";
- @Nullable
- static String itemGender = null;
- static String blockSingular = "block";
- static String blockPlural = "blocks";
- @Nullable
- static String blockGender = null;
-
- // this is not an alias!
- private final static ItemType everything = new ItemType();
- static {
- everything.setAll(true);
- ItemData all = new ItemData(Material.AIR);
- all.isAnything = true;
- everything.add(all);
- }
-
- private final static Message m_empty_string = new Message("aliases.empty string");
- private final static ArgsMessage m_invalid_item_type = new ArgsMessage("aliases.invalid item type");
- private final static Message m_outside_section = new Message("aliases.outside section");
-
/**
* Concatenates parts of an alias's name. This currently 'lowercases' the first character of any part if there's no space in front of it. It also replaces double spaces with a
* single one and trims the resulting string.
- *
+ *
* @param parts
*/
static String concatenate(final String... parts) {
@@ -171,7 +163,7 @@ static String concatenate(final String... parts) {
}
return "" + b.toString().replace(" ", " ").trim();
}
-
+
@Nullable
private static MaterialName getMaterialNameData(ItemData type) {
// Check script aliases first
@@ -179,11 +171,11 @@ private static MaterialName getMaterialNameData(ItemData type) {
if (aliases != null) {
return aliases.provider.getMaterialName(type);
}
-
+
// Then global aliases
return provider.getMaterialName(type);
}
-
+
public static String getMaterialName(ItemData type, boolean plural) {
MaterialName name = getMaterialNameData(type);
if (name == null) {
@@ -191,7 +183,7 @@ public static String getMaterialName(ItemData type, boolean plural) {
}
return name.toString(plural);
}
-
+
/**
* @return The item's gender or -1 if no name is found
*/
@@ -201,10 +193,10 @@ public static int getGender(ItemData item) {
return n.gender;
return -1;
}
-
+
/**
* Parses an ItemType to be used as an alias, i.e. it doesn't parse 'all'/'every' and the amount.
- *
+ *
* @param s mixed case string
* @return A new ItemType representing the given value
*/
@@ -216,28 +208,23 @@ public static ItemType parseAlias(final String s) {
}
if (s.equals("*"))
return everything;
-
+
final ItemType t = new ItemType();
-
+
final String[] types = s.split("\\s*,\\s*");
for (final String type : types) {
if (type == null || parseType(type, t, true) == null)
return null;
}
-
+
return t;
}
-
- private final static RegexMessage p_any = new RegexMessage("aliases.any", "", " (.+)", Pattern.CASE_INSENSITIVE);
- private final static RegexMessage p_every = new RegexMessage("aliases.every", "", " (.+)", Pattern.CASE_INSENSITIVE);
- private final static RegexMessage p_of_every = new RegexMessage("aliases.of every", "(\\d+) ", " (.+)", Pattern.CASE_INSENSITIVE);
- private final static RegexMessage p_of = new RegexMessage("aliases.of", "(\\d+) (?:", " )?(.+)", Pattern.CASE_INSENSITIVE);
-
+
/**
* Parses an ItemType.
*
* Prints errors.
- *
+ *
* @param s
* @return The parsed ItemType or null if the input is invalid.
*/
@@ -245,10 +232,10 @@ public static ItemType parseAlias(final String s) {
public static ItemType parseItemType(String s) {
if (s.isEmpty())
return null;
- s = "" + s.trim();
-
+ s = s.trim();
+
final ItemType t = new ItemType();
-
+
Matcher m;
if ((m = p_of_every.matcher(s)).matches()) {
t.setAmount(Utils.parseInt("" + m.group(1)));
@@ -266,11 +253,12 @@ public static ItemType parseItemType(String s) {
if (s.length() != l) // had indefinite article
t.setAmount(1);
}
-
+
String lc = s.toLowerCase(Locale.ENGLISH);
String of = Language.getSpaced("of").toLowerCase();
int c = -1;
- outer: while ((c = lc.indexOf(of, c + 1)) != -1) {
+ outer:
+ while ((c = lc.indexOf(of, c + 1)) != -1) {
ItemType t2 = t.clone();
try (BlockingLogHandler ignored = new BlockingLogHandler().start()) {
if (parseType("" + s.substring(0, c), t2, false) == null)
@@ -287,19 +275,19 @@ public static ItemType parseItemType(String s) {
}
return t2;
}
-
+
if (parseType(s, t, false) == null)
return null;
-
+
if (t.numTypes() == 0)
return null;
-
+
return t;
}
-
+
/**
* Prints errors.
- *
+ *
* @param s The string holding the type, can be either a number or an alias, plus an optional data part. Case does not matter.
* @param t The ItemType to add the parsed ItemData(s) to (i.e. this ItemType will be modified)
* @param isAlias Whether this type is parsed for an alias.
@@ -323,10 +311,10 @@ private static ItemType parseType(final String s, final ItemType t, final boolea
Skript.error(m_invalid_item_type.toString(s));
return null;
}
-
+
/**
* Gets an alias from the aliases defined in the config.
- *
+ *
* @param s The alias to get, case does not matter
* @return A copy of the ItemType represented by the given alias or null if no such alias exists.
*/
@@ -372,14 +360,14 @@ private static ItemType getAlias(final String s) {
}
return null;
}
-
+
/**
* Clears aliases. Make sure to load them after this!
*/
public static void clear() {
provider.clearAliases();
}
-
+
/**
* Loads aliases from Skript's standard locations.
* Exceptions will be logged, but not thrown.
@@ -400,19 +388,41 @@ public static void load() {
private static void loadMissingAliases() {
if (!Skript.methodExists(Material.class, "getKey"))
return;
+ boolean modItemRegistered = false;
for (Material material : Material.values()) {
if (!material.isLegacy() && !provider.hasAliasForMaterial(material)) {
NamespacedKey key = material.getKey();
- String name = key.getKey().replace("_", " ");
- parser.loadAlias(name + "¦s", key.toString());
+ // mod:an_item -> (mod's an item) | (an item from mod)
+ // minecraft:dirt -> dirt
+ if (NamespacedKey.MINECRAFT.equals(key.getNamespace())) {
+ parser.loadAlias(key.getKey().replace("_", " ") + "¦s", key.toString());
+ } else {
+ if (!modItemRegistered) modItemRegistered = true;
+ parser.loadAlias((key.getNamespace() + "'s " + key.getKey() + "¦s").replace("_", " "), key.toString());
+ parser.loadAlias((key.getKey() + "¦s from " + key.getNamespace()).replace("_", " "), key.toString());
+ }
Skript.debug(ChatColor.YELLOW + "Creating temporary alias for: " + key);
}
}
+
+ if (modItemRegistered) {
+ Skript.warning("==============================================================");
+ Skript.warning("Some materials were found that seem to be modded.");
+ Skript.warning("An item that has the id 'mod:item' can be used as 'mod's item' or 'item from mod'.");
+ Skript.warning("WARNING: Skript does not officially support any modded servers.");
+ Skript.warning("Any issues you encounter related to modded items will be your responsibility to fix.");
+ Skript.warning("The server will keep loading after 5 seconds.");
+ Skript.warning("==============================================================");
+ try {
+ Thread.sleep(5000L);
+ } catch (InterruptedException ignored) {
+ }
+ }
}
-
+
private static void loadInternal() throws IOException {
Path dataFolder = Skript.getInstance().getDataFolder().toPath();
-
+
// Load aliases.zip OR aliases from jar (never both)
Path zipPath = dataFolder.resolve("aliases-english.zip");
if (!SkriptConfig.loadDefaultAliases.value()) {
@@ -436,9 +446,9 @@ private static void loadInternal() throws IOException {
} catch (URISyntaxException e) {
assert false;
}
-
+
}
-
+
// Load everything from aliases folder (user aliases)
Path aliasesFolder = dataFolder.resolve("aliases");
if (Files.exists(aliasesFolder)) {
@@ -448,19 +458,19 @@ private static void loadInternal() throws IOException {
// generate aliases from item names for any missing items
loadMissingAliases();
-
+
// Update tracked item types
for (Map.Entry entry : trackedTypes.entrySet()) {
@SuppressWarnings("null") // No null keys in this map
ItemType type = parseItemType(entry.getKey());
if (type == null)
Skript.warning("Alias '" + entry.getKey() + "' is required by Skript, but does not exist anymore. "
- + "Make sure to fix this before restarting the server.");
+ + "Make sure to fix this before restarting the server.");
else
entry.getValue().setTo(type);
}
}
-
+
/**
* Loads aliases from given directory.
* @param dir Directory of aliases.
@@ -484,7 +494,7 @@ else if (name.endsWith(".sk"))
throw e.getCause();
}
}
-
+
/**
* Loads aliases from given path.
* @param f Path of alias file.
@@ -494,7 +504,7 @@ public static void load(Path f) throws IOException {
Config config = new Config(f, false, false, "=");
load(config);
}
-
+
/**
* Loads aliases from configuration.
* @param config Configuration containing the aliases.
@@ -505,7 +515,7 @@ public static void load(Config config) {
Skript.error(m_outside_section.toString());
continue;
}
-
+
parser.load((SectionNode) n);
}
}
@@ -523,7 +533,7 @@ public static String getMinecraftId(ItemData data) {
}
return provider.getMinecraftId(data);
}
-
+
/**
* Gets an entity type related to given item. For example, an armor stand
* item is related with armor stand entity.
@@ -538,22 +548,11 @@ public static EntityData> getRelatedEntity(ItemData data) {
}
return provider.getRelatedEntity(data);
}
-
- /**
- * Go through these whenever aliases are reloaded, and update them.
- */
- private static final Map trackedTypes = new HashMap<>();
-
- /**
- * If user had an obscure config option set, don't crash due to missing
- * Java item types.
- */
- private static final boolean noHardExceptions = SkriptConfig.apiSoftExceptions.value();
-
+
/**
* Gets an item type that matches the given name.
* If it doesn't exist, an exception is thrown instead.
- *
+ *
* Item types provided by this method are updated when aliases are
* reloaded. However, this also means they are tracked by aliases system
* and NOT necessarily garbage-collected.
@@ -580,7 +579,7 @@ public static ItemType javaItemType(String name) {
trackedTypes.put(name, type);
return type;
}
-
+
/**
* Creates an aliases provider to be used by given addon. It can be used to
* register aliases and variations to be used in scripts.
@@ -591,11 +590,11 @@ public static AliasesProvider getAddonProvider(@Nullable SkriptAddon addon) {
if (addon == null) {
throw new IllegalArgumentException("addon needed");
}
-
+
// TODO in future, maybe record and allow unloading addon-provided aliases?
return provider; // For now, just allow loading aliases easily
}
-
+
/**
* Creates script aliases for the provided Script.
* @return Script aliases that are ready to be added to.
diff --git a/src/main/java/ch/njol/skript/aliases/AliasesProvider.java b/src/main/java/ch/njol/skript/aliases/AliasesProvider.java
index 06ae5317f78..1cbaa0c9cfd 100644
--- a/src/main/java/ch/njol/skript/aliases/AliasesProvider.java
+++ b/src/main/java/ch/njol/skript/aliases/AliasesProvider.java
@@ -1,45 +1,24 @@
-/**
- * This file is part of Skript.
- *
- * Skript is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Skript is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Skript. If not, see .
- *
- * Copyright Peter Güttinger, SkriptLang team and contributors
- */
package ch.njol.skript.aliases;
+import ch.njol.skript.Skript;
+import ch.njol.skript.bukkitutil.BukkitUnsafe;
+import ch.njol.skript.bukkitutil.ItemUtils;
+import ch.njol.skript.bukkitutil.block.BlockCompat;
+import ch.njol.skript.bukkitutil.block.BlockValues;
+import ch.njol.skript.entity.EntityData;
+import com.google.gson.Gson;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Set;
import java.util.List;
import java.util.Map;
-
-import ch.njol.skript.Skript;
-import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import java.util.Set;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
-import com.google.gson.Gson;
-
-import ch.njol.skript.bukkitutil.BukkitUnsafe;
-import ch.njol.skript.bukkitutil.ItemUtils;
-import ch.njol.skript.bukkitutil.block.BlockCompat;
-import ch.njol.skript.bukkitutil.block.BlockValues;
-import ch.njol.skript.entity.EntityData;
-
/**
* Provides aliases on Bukkit/Spigot platform.
*/
@@ -47,14 +26,14 @@ public class AliasesProvider {
// not supported on Spigot versions older than 1.18
private static final boolean FASTER_SET_SUPPORTED = Skript.classExists("it.unimi.dsi.fastutil.objects.ObjectOpenHashSet");
-
+
/**
* When an alias is not found, it will requested from this provider.
* Null when this is global aliases provider.
*/
@Nullable
private final AliasesProvider parent;
-
+
/**
* All aliases that are currently loaded by this provider.
*/
@@ -64,112 +43,21 @@ public class AliasesProvider {
* All materials that are currently loaded by this provider.
*/
private final Set materials;
-
+
/**
* Tags are in JSON format. We may need GSON when merging tags
* (which might be done if variations are used).
*/
private final Gson gson;
-
- /**
- * Represents a variation of material. It could, for example, define one
- * more tag or change base id, but keep tag intact.
- */
- public static class Variation {
-
- @Nullable
- private final String id;
- private final int insertPoint;
-
- private final Map tags;
- private final Map states;
-
- public Variation(@Nullable String id, int insertPoint, Map tags, Map states) {
- this.id = id;
- this.insertPoint = insertPoint;
- this.tags = tags;
- this.states = states;
- }
-
- @Nullable
- public String getId() {
- return id;
- }
-
- public int getInsertPoint() {
- return insertPoint;
- }
-
- @Nullable
- public String insertId(@Nullable String inserted) {
- if (inserted == null) // Nothing to insert
- return id;
- inserted = inserted.substring(0, inserted.length() - 1); // Strip out -
- if (id == null) // Inserting to nothing
- return inserted;
-
- String id = this.id;
- assert id != null;
- if (insertPoint == -1) // No place where to insert
- return inserted;
-
- // Insert given string to in middle of our id
- String before = id.substring(0, insertPoint);
- String after = id.substring(insertPoint + 1);
- return before + inserted + after;
- }
-
- public Map getTags() {
- return tags;
- }
-
-
- public Map getBlockStates() {
- return states;
- }
-
-
- public Variation merge(Variation other) {
- // Merge tags and block states
- Map mergedTags = new HashMap<>(other.tags);
- mergedTags.putAll(tags);
- Map mergedStates = new HashMap<>(other.states);
- mergedStates.putAll(states);
-
- // Potentially merge ids
- String id = insertId(other.id);
-
- return new Variation(id, -1, mergedTags, mergedStates);
- }
- }
-
- public static class VariationGroup {
-
- public final List keys;
-
- public final List values;
-
- public VariationGroup() {
- this.keys = new ArrayList<>();
- this.values = new ArrayList<>();
- }
-
- public void put(String key, Variation value) {
- keys.add(key);
- values.add(value);
- }
- }
-
/**
* Contains all variations.
*/
private final Map variations;
-
/**
* Allows looking up aliases based on item datas created runtime.
*/
private final AliasesMap aliasesMap;
-
+
/**
* Constructs a new aliases provider with no data.
*/
@@ -184,20 +72,20 @@ public AliasesProvider(int expectedCount, @Nullable AliasesProvider parent) {
} else {
this.materials = new HashSet<>();
}
-
+
this.gson = new Gson();
}
-
+
/**
* Uses GSON to parse Mojang's JSON format to a map.
* @param raw Raw JSON.
- * @return String,Object map.
+ * @return String, Object map.
*/
@SuppressWarnings({"null", "unchecked"})
public Map parseMojangson(String raw) {
return (Map) gson.fromJson(raw, Object.class);
}
-
+
/**
* Applies given tags to an item stack.
* @param stack Item stack.
@@ -213,10 +101,10 @@ public int applyTags(ItemStack stack, Map tags) {
tags.remove("Damage");
flags |= ItemFlags.CHANGED_DURABILITY;
}
-
+
if (tags.isEmpty()) // No real tags to apply
return flags;
-
+
// Apply random tags using JSON
if (Aliases.USING_ITEM_COMPONENTS) {
String components = (String) tags.get("components"); // only components are supported for modifying a stack
@@ -231,40 +119,10 @@ public int applyTags(ItemStack stack, Map tags) {
BukkitUnsafe.modifyItemStack(stack, json);
}
flags |= ItemFlags.CHANGED_TAGS;
-
+
return flags;
}
-
- /**
- * Name of an alias used by {@link #addAlias(AliasName, String, Map, Map)}
- * for registration.
- */
- public static class AliasName {
-
- /**
- * Singular for of alias name.
- */
- public final String singular;
-
- /**
- * Plural form of alias name.
- */
- public final String plural;
-
- /**
- * Gender of alias name.
- */
- public final int gender;
- public AliasName(String singular, String plural, int gender) {
- super();
- this.singular = singular;
- this.plural = plural;
- this.gender = gender;
- }
-
- }
-
/**
* Adds an alias to this provider.
* @param name Name of alias without any patterns or variation blocks.
@@ -290,19 +148,19 @@ public void addAlias(AliasName name, String id, @Nullable Map ta
datas = typeOfId.getTypes();
} else { // ... but quite often, we just got Vanilla id
// Prepare and modify ItemStack (using somewhat Unsafe methods)
- Material material = BukkitUnsafe.getMaterialFromMinecraftId(id);
+ Material material = BukkitUnsafe.getMaterialFromNamespacedId(id);
if (material == null) { // If server doesn't recognize id, do not proceed
throw new InvalidMinecraftIdException(id);
}
materials.add(material);
-
+
// Hacky: get related entity from block states
String entityName = blockStates.remove("relatedEntity");
if (entityName != null) {
related = EntityData.parse(entityName);
}
-
+
// Apply (NBT) tags to item stack
ItemStack stack = null;
int itemFlags = 0;
@@ -312,14 +170,14 @@ public void addAlias(AliasName name, String id, @Nullable Map ta
itemFlags = applyTags(stack, new HashMap<>(tags));
}
}
-
+
// Parse block state to block values
BlockValues blockValues = BlockCompat.INSTANCE.createBlockValues(material, blockStates, stack, itemFlags);
-
+
ItemData data = stack != null ? new ItemData(stack, blockValues) : new ItemData(material, blockValues);
data.isAlias = true;
data.itemFlags = itemFlags;
-
+
// Deduplicate item data if this has been loaded before
if (deduplicate) {
AliasesMap.Match canonical = aliasesMap.exactMatch(data);
@@ -329,10 +187,10 @@ public void addAlias(AliasName name, String id, @Nullable Map ta
data = aliasData.getItem();
}
}
-
+
datas = Collections.singletonList(data);
}
-
+
// If this is first time we're defining an item, store additional data about it
if (typeOfId == null) {
ItemData data = datas.get(0);
@@ -351,7 +209,8 @@ public void addAlias(AliasName name, String id, @Nullable Map ta
aliases.put(name.plural, type); // Plural form
type.addAll(datas);
} else { // There is already an item type with this name, we need to *only* add new data
- newDataLoop: for (ItemData newData : datas) {
+ newDataLoop:
+ for (ItemData newData : datas) {
for (ItemData existingData : type.getTypes()) {
if (newData == existingData || newData.matchAlias(existingData).isAtLeast(MatchQuality.EXACT)) // Don't add this data, the item type already contains it!
continue newDataLoop;
@@ -360,11 +219,11 @@ public void addAlias(AliasName name, String id, @Nullable Map ta
}
}
}
-
+
public void addVariationGroup(String name, VariationGroup group) {
variations.put(name, group);
}
-
+
@Nullable
public VariationGroup getVariationGroup(String name) {
return variations.get(name);
@@ -378,7 +237,7 @@ public ItemType getAlias(String alias) {
}
return item;
}
-
+
public AliasesMap.@Nullable AliasData getAliasData(ItemData item) {
AliasesMap.AliasData data = aliasesMap.matchAlias(item).getData();
if (data == null && parent != null) {
@@ -395,7 +254,7 @@ public String getMinecraftId(ItemData item) {
}
return null;
}
-
+
@Nullable
public MaterialName getMaterialName(ItemData item) {
AliasesMap.AliasData data = getAliasData(item);
@@ -404,7 +263,7 @@ public MaterialName getMaterialName(ItemData item) {
}
return null;
}
-
+
@Nullable
public EntityData> getRelatedEntity(ItemData item) {
AliasesMap.AliasData data = getAliasData(item);
@@ -434,4 +293,123 @@ public boolean hasAliasForMaterial(Material material) {
return materials.contains(material);
}
+ /**
+ * Represents a variation of material. It could, for example, define one
+ * more tag or change base id, but keep tag intact.
+ */
+ public static class Variation {
+
+ @Nullable
+ private final String id;
+ private final int insertPoint;
+
+ private final Map tags;
+ private final Map states;
+
+ public Variation(@Nullable String id, int insertPoint, Map tags, Map states) {
+ this.id = id;
+ this.insertPoint = insertPoint;
+ this.tags = tags;
+ this.states = states;
+ }
+
+ @Nullable
+ public String getId() {
+ return id;
+ }
+
+ public int getInsertPoint() {
+ return insertPoint;
+ }
+
+ @Nullable
+ public String insertId(@Nullable String inserted) {
+ if (inserted == null) // Nothing to insert
+ return id;
+ inserted = inserted.substring(0, inserted.length() - 1); // Strip out -
+ if (id == null) // Inserting to nothing
+ return inserted;
+
+ String id = this.id;
+ assert id != null;
+ if (insertPoint == -1) // No place where to insert
+ return inserted;
+
+ // Insert given string to in middle of our id
+ String before = id.substring(0, insertPoint);
+ String after = id.substring(insertPoint + 1);
+ return before + inserted + after;
+ }
+
+ public Map getTags() {
+ return tags;
+ }
+
+
+ public Map getBlockStates() {
+ return states;
+ }
+
+
+ public Variation merge(Variation other) {
+ // Merge tags and block states
+ Map mergedTags = new HashMap<>(other.tags);
+ mergedTags.putAll(tags);
+ Map mergedStates = new HashMap<>(other.states);
+ mergedStates.putAll(states);
+
+ // Potentially merge ids
+ String id = insertId(other.id);
+
+ return new Variation(id, -1, mergedTags, mergedStates);
+ }
+ }
+
+ public static class VariationGroup {
+
+ public final List keys;
+
+ public final List values;
+
+ public VariationGroup() {
+ this.keys = new ArrayList<>();
+ this.values = new ArrayList<>();
+ }
+
+ public void put(String key, Variation value) {
+ keys.add(key);
+ values.add(value);
+ }
+ }
+
+ /**
+ * Name of an alias used by {@link #addAlias(AliasName, String, Map, Map)}
+ * for registration.
+ */
+ public static class AliasName {
+
+ /**
+ * Singular for of alias name.
+ */
+ public final String singular;
+
+ /**
+ * Plural form of alias name.
+ */
+ public final String plural;
+
+ /**
+ * Gender of alias name.
+ */
+ public final int gender;
+
+ public AliasName(String singular, String plural, int gender) {
+ super();
+ this.singular = singular;
+ this.plural = plural;
+ this.gender = gender;
+ }
+
+ }
+
}
diff --git a/src/main/java/ch/njol/skript/bukkitutil/BukkitUnsafe.java b/src/main/java/ch/njol/skript/bukkitutil/BukkitUnsafe.java
index f47a3f5d8d7..e7e255f1e72 100644
--- a/src/main/java/ch/njol/skript/bukkitutil/BukkitUnsafe.java
+++ b/src/main/java/ch/njol/skript/bukkitutil/BukkitUnsafe.java
@@ -1,76 +1,75 @@
-/**
- * This file is part of Skript.
- *
- * Skript is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Skript is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Skript. If not, see .
- *
- * Copyright Peter Güttinger, SkriptLang team and contributors
- */
package ch.njol.skript.bukkitutil;
+import ch.njol.skript.Skript;
+import ch.njol.util.EnumTypeAdapter;
+import com.google.common.io.ByteStreams;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.io.InputStream;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
-
import org.bukkit.Bukkit;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import org.bukkit.UnsafeValues;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
-import com.google.common.io.ByteStreams;
-import com.google.gson.GsonBuilder;
-import com.google.gson.reflect.TypeToken;
-import ch.njol.util.EnumTypeAdapter;
-import ch.njol.skript.Skript;
-import ch.njol.skript.util.Version;
-
/**
* Contains helpers for Bukkit's not so safe stuff.
*/
@SuppressWarnings("deprecation")
public class BukkitUnsafe {
-
+
/**
* Bukkit's UnsafeValues allows us to do stuff that would otherwise
* require NMS. It has existed for a long time, too, so 1.9 support is
* not particularly hard to achieve.
- *
+ *
* UnsafeValues' existence and behavior is not guaranteed across future versions.
*/
@Nullable
private static final UnsafeValues unsafe = Bukkit.getUnsafe();
+ /**
+ * Maps pre 1.12 ids to materials for variable conversions.
+ */
+ private static @Nullable Map idMappings;
+
static {
if (unsafe == null)
throw new Error("UnsafeValues are not available.");
}
/**
- * Maps pre 1.12 ids to materials for variable conversions.
+ * Get a material from a minecraft id.
+ *
+ * @param id Namespaced ID with or without a namespace. IDs without a namespace will be treated
+ * as minecraft namespaced IDs. ('minecraft:dirt' and 'dirt' are equivalent.)
+ * @return The Material which the id represents, or null if no material can be matched.
+ * @deprecated Prefer {@link BukkitUnsafe#getMaterialFromNamespacedId(String)} for including modded item support
*/
- @Nullable
- private static Map idMappings;
+ @Deprecated
+ public static @Nullable Material getMaterialFromMinecraftId(String id) {
+ return getMaterialFromNamespacedId(id);
+ }
- @Nullable
- public static Material getMaterialFromMinecraftId(String id) {
- return Material.matchMaterial(id);
+ /**
+ * Get a material from a namespaced ID.
+ * For example, 'minecraft:iron_ingot' -> Material.IRON_INGOT; 'mod:an_item' -> Material.MOD_AN_ITEM
+ *
+ * @param id Namespaced ID with or without a namespace. IDs without a namespace will be treated
+ * as minecraft namespaced IDs. ('minecraft:dirt' and 'dirt' are equivalent.)
+ * @return The Material which the id represents, or null if no material can be matched.
+ */
+ public static @Nullable Material getMaterialFromNamespacedId(String id) {
+ return Material.matchMaterial(id.toLowerCase().startsWith(NamespacedKey.MINECRAFT + ":")
+ ? id
+ : id.replace(":", "_") //For Hybrid Server
+ );
}
public static void modifyItemStack(ItemStack stack, String arguments) {
@@ -78,19 +77,20 @@ public static void modifyItemStack(ItemStack stack, String arguments) {
throw new IllegalStateException("modifyItemStack could not be performed as UnsafeValues are not available.");
unsafe.modifyItemStack(stack, arguments);
}
-
+
private static void initIdMappings() {
try (InputStream is = Skript.getInstance().getResource("materials/ids.json")) {
if (is == null) {
throw new AssertionError("missing id mappings");
}
String data = new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8);
-
- Type type = new TypeToken