diff --git a/src/main/java/net/knarcraft/knarlib/util/MaterialHelper.java b/src/main/java/net/knarcraft/knarlib/util/MaterialHelper.java
new file mode 100644
index 0000000..3723e62
--- /dev/null
+++ b/src/main/java/net/knarcraft/knarlib/util/MaterialHelper.java
@@ -0,0 +1,98 @@
+package net.knarcraft.knarlib.util;
+
+import net.knarcraft.knarlib.formatting.StringFormatter;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Tag;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A helper class for dealing with and parsing materials
+ */
+@SuppressWarnings("unused")
+public final class MaterialHelper {
+
+ private MaterialHelper() {
+
+ }
+
+ /**
+ * Loads the materials specified in the given list
+ *
+ * @param materials
The list of material strings to load
+ * @param logger The logger to use for logging parsing errors
+ */
+ public static @NotNull Set loadMaterialList(@NotNull List> materials, @NotNull Logger logger) {
+ Set parsedMaterials = new HashSet<>();
+ for (Object value : materials) {
+ if (!(value instanceof String string)) {
+ continue;
+ }
+
+ parsedMaterials.addAll(loadMaterialString(string, logger));
+ }
+ return parsedMaterials;
+ }
+
+ /**
+ * Parses a string representing a material or a material tag
+ *
+ * @param materialString The material string to parse
+ * @param logger The logger to use for logging parsing errors
+ * @return The materials defined by the material string, or an empty list if none were found
+ */
+ public static @NotNull Set loadMaterialString(@NotNull String materialString, @NotNull Logger logger) {
+ Set parsedMaterials = new HashSet<>();
+ // Try to parse a material tag first
+ if (parseMaterialTag(parsedMaterials, materialString, logger)) {
+ return parsedMaterials;
+ }
+
+ // Try to parse a material name
+ Material matched = Material.matchMaterial(materialString);
+ if (matched != null) {
+ parsedMaterials.add(matched);
+ } else {
+ logger.log(Level.WARNING, StringFormatter.replacePlaceholder("Unable to parse material: {material}",
+ "{material}", materialString));
+ }
+ return parsedMaterials;
+ }
+
+ /**
+ * Tries to parse the material tag in the specified material name
+ *
+ * @param targetSet The set all parsed materials should be added to
+ * @param materialName The material name that might be a material tag
+ * @param logger The logger to use for logging parsing errors
+ * @return True if a tag was found
+ */
+ private static boolean parseMaterialTag(@NotNull Set targetSet, @NotNull String materialName,
+ @NotNull Logger logger) {
+ Pattern pattern = Pattern.compile("^\\+([a-zA-Z_]+)");
+ Matcher matcher = pattern.matcher(materialName);
+ if (matcher.find()) {
+ // The material is a material tag
+ Tag tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, NamespacedKey.minecraft(
+ matcher.group(1).toLowerCase()), Material.class);
+ if (tag != null) {
+ targetSet.addAll(tag.getValues());
+ } else {
+ logger.log(Level.WARNING, StringFormatter.replacePlaceholder(
+ "Unable to parse material: {material}", "{material}", materialName));
+ }
+ return true;
+ }
+ return false;
+ }
+
+}