Skip to content
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
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArt
group = "world.bentobox" // From <groupId>

// Base properties from <properties>
val buildVersion = "3.14.2"
val buildVersion = "3.15.0"
val buildNumberDefault = "-LOCAL" // Local build identifier
val snapshotSuffix = "-SNAPSHOT" // Indicates development/snapshot version

Expand Down
54 changes: 49 additions & 5 deletions src/main/java/world/bentobox/bentobox/blueprints/Blueprint.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@

import org.bukkit.Material;
import org.bukkit.util.Vector;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;

import com.google.gson.annotations.Expose;

import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
import world.bentobox.bentobox.util.ItemParser;

/**
* Stores all details of a blueprint
Expand All @@ -20,15 +22,22 @@
*/
public class Blueprint {

private static final String DEFAULT_ICON = "PAPER";

/**
* Unique name for this blueprint. The filename will be this plus the blueprint suffix
*/
@Expose
private @NonNull String name = "";
@Expose
private String displayName;
/**
* Icon of the blueprint. Supports plain material names (e.g. "DIAMOND"),
* vanilla namespaced materials (e.g. "minecraft:diamond"), and custom
* item model keys (e.g. "myserver:island_tropical").
*/
@Expose
private @NonNull Material icon = Material.PAPER;
private String icon = DEFAULT_ICON;
@Expose
private List<String> description;
@Expose
Expand Down Expand Up @@ -77,17 +86,52 @@ public Blueprint setDisplayName(String displayName) {
return this;
}
/**
* @return the icon
* Returns the base Material for this blueprint's icon.
* Resolves plain names ("DIAMOND") and vanilla namespaced keys ("minecraft:diamond")
* via {@link Material#matchMaterial}. For custom item-model keys that are not
* valid vanilla materials (e.g. "myserver:island_tropical"), returns {@link Material#PAPER}
* as the base item — use {@link #getIconItemStack()} to get the full item with model data.
* @return the icon material, never null
*/
public @NonNull Material getIcon() {
return icon;
return ItemParser.parseIconMaterial(icon);
}

/**
* Returns an {@link ItemStack} representing this blueprint's icon.
* <ul>
* <li>Plain material name (e.g. {@code "DIAMOND"}) → {@code new ItemStack(Material.DIAMOND)}</li>
* <li>Vanilla namespaced material (e.g. {@code "minecraft:diamond"}) → same as above</li>
* <li>Custom item-model key (e.g. {@code "myserver:island_tropical"}) → PAPER base item
* with the model key set via {@link ItemMeta#setItemModel}</li>
* </ul>
* @return ItemStack for this blueprint's icon, never null
* @since 3.0.0
*/
public @NonNull ItemStack getIconItemStack() {
return ItemParser.parseIconItemStack(icon);
}

/**
* @param icon the icon to set
* Sets the icon from a Material (backward-compatible setter).
* @param icon the icon material to set; if null, defaults to {@link Material#PAPER}
* @return blueprint
*/
public Blueprint setIcon(Material icon) {
this.icon = icon;
this.icon = icon != null ? icon.name() : DEFAULT_ICON;
return this;
}

/**
* Sets the icon from a string. Accepts plain material names (e.g. {@code "DIAMOND"}),
* vanilla namespaced materials (e.g. {@code "minecraft:diamond"}), and custom item-model
* keys (e.g. {@code "myserver:island_tropical"}).
* @param icon the icon string; if null, defaults to {@code "PAPER"}
* @return blueprint
* @since 3.0.0
*/
public Blueprint setIcon(String icon) {
this.icon = icon != null ? icon : DEFAULT_ICON;
return this;
}
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;

import com.google.gson.annotations.Expose;

import world.bentobox.bentobox.blueprints.Blueprint;
import world.bentobox.bentobox.database.objects.DataObject;
import world.bentobox.bentobox.util.ItemParser;

/**
* Represents a bundle of three {@link Blueprint}s.
Expand All @@ -21,16 +23,20 @@
*/
public class BlueprintBundle implements DataObject {

private static final String DEFAULT_ICON = "PAPER";

/**
* The unique id of this bundle
*/
@Expose
private String uniqueId;
/**
* Icon of the bundle
* Icon of the bundle. Supports plain material names (e.g. "DIAMOND"),
* vanilla namespaced materials (e.g. "minecraft:diamond"), and custom
* item model keys (e.g. "myserver:island_tropical").
*/
@Expose
private Material icon = Material.PAPER;
private String icon = DEFAULT_ICON;
/**
* Name on the icon
*/
Expand Down Expand Up @@ -97,16 +103,49 @@ public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
}
/**
* @return the icon
* Returns the base Material for this bundle's icon.
* Resolves plain names ("DIAMOND") and vanilla namespaced keys ("minecraft:diamond")
* via {@link Material#matchMaterial}. For custom item-model keys that are not
* valid vanilla materials (e.g. "myserver:island_tropical"), returns {@link Material#PAPER}
* as the base item — use {@link #getIconItemStack()} to get the full item with model data.
* @return the icon material, never null
*/
public Material getIcon() {
return icon;
return ItemParser.parseIconMaterial(icon);
}

/**
* Returns an {@link ItemStack} representing this bundle's icon.
* <ul>
* <li>Plain material name (e.g. {@code "DIAMOND"}) → {@code new ItemStack(Material.DIAMOND)}</li>
* <li>Vanilla namespaced material (e.g. {@code "minecraft:diamond"}) → same as above</li>
* <li>Custom item-model key (e.g. {@code "myserver:island_tropical"}) → PAPER base item
* with the model key set via {@link ItemMeta#setItemModel}</li>
* </ul>
* @return ItemStack for this bundle's icon, never null
* @since 3.0.0
*/
public ItemStack getIconItemStack() {
return ItemParser.parseIconItemStack(icon);
}

/**
* @param icon the icon to set
* Sets the icon from a Material (backward-compatible setter).
* @param icon the icon material to set; if null, defaults to {@link Material#PAPER}
*/
public void setIcon(Material icon) {
this.icon = icon;
this.icon = icon != null ? icon.name() : DEFAULT_ICON;
}

/**
* Sets the icon from a string. Accepts plain material names (e.g. {@code "DIAMOND"}),
* vanilla namespaced materials (e.g. {@code "minecraft:diamond"}), and custom item-model
* keys (e.g. {@code "myserver:island_tropical"}).
* @param icon the icon string; if null, defaults to {@code "PAPER"}
* @since 3.0.0
*/
public void setIcon(String icon) {
this.icon = icon != null ? icon : DEFAULT_ICON;
}
/**
* @return the displayName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public void openPanel() {
PanelItem item = new PanelItemBuilder()
.name(bb.getDisplayName())
.description(t("edit"), t("rename"))
.icon(bb.getIcon())
.icon(bb.getIconItemStack())
.clickHandler((panel, u, clickType, s) -> {
u.closeInventory();
if (clickType.equals(ClickType.RIGHT)) {
Expand Down Expand Up @@ -350,7 +350,7 @@ protected PanelItem getBundleIcon(BlueprintBundle bb) {
return new PanelItemBuilder()
.name(t("edit-description"))
.description(bb.getDescription())
.icon(bb.getIcon())
.icon(bb.getIconItemStack())
.clickHandler((panel, u, clickType, slot) -> {
u.closeInventory();
// Description conversation
Expand Down Expand Up @@ -458,7 +458,7 @@ protected PanelItem getBlueprintItem(GameModeAddon addon, int pos, BlueprintBund
return new PanelItemBuilder()
.name(blueprint.getDisplayName() == null ? blueprint.getName() : blueprint.getDisplayName())
.description(desc)
.icon(blueprint.getIcon())
.icon(blueprint.getIconItemStack())
.glow(selected != null && pos == selected.getKey())
.clickHandler((panel, u, clickType, slot) -> {
// Handle the world squares
Expand Down
20 changes: 16 additions & 4 deletions src/main/java/world/bentobox/bentobox/panels/IconChanger.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.bukkit.Sound;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.meta.ItemMeta;

import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
Expand Down Expand Up @@ -48,15 +49,26 @@ public void onInventoryClick(User user, InventoryClickEvent event) {
Entry<Integer, Blueprint> selected = blueprintManagementPanel.getSelected();
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
if (selected == null) {
// Change the Bundle Icon
bb.setIcon(icon);
// Change the Bundle Icon — prefer item model key over plain material so that
// datapacked items (e.g. paper[item_model="myserver:island_tropical"]) are stored correctly.
ItemMeta meta = event.getCurrentItem().getItemMeta();
if (meta != null && meta.hasItemModel()) {
bb.setIcon(meta.getItemModel().toString());
} else {
bb.setIcon(icon);
}
// Save it
plugin.getBlueprintsManager().saveBlueprintBundle(addon, bb);

} else {
// Change the Blueprint icon
// Change the Blueprint icon — same model-key detection as for bundles
Blueprint bp = selected.getValue();
bp.setIcon(icon);
ItemMeta bpMeta = event.getCurrentItem().getItemMeta();
if (bpMeta != null && bpMeta.hasItemModel()) {
bp.setIcon(bpMeta.getItemModel().toString());
} else {
bp.setIcon(icon);
}
// Save it
plugin.getBlueprintsManager().saveBlueprint(addon, bp);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ private void applyTemplate(PanelItemBuilder builder, ItemTemplateRecord template
}
else
{
builder.icon(bundle.getIcon());
builder.icon(bundle.getIconItemStack());
}

if (template.title() != null)
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/world/bentobox/bentobox/util/ItemParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.bukkit.Bukkit;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.banner.Pattern;
import org.bukkit.block.banner.PatternType;
import org.bukkit.inventory.ItemStack;
Expand Down Expand Up @@ -43,6 +44,61 @@ public class ItemParser {
private static final int MAX_AMOUNT = 99;

private ItemParser() {} // private constructor to hide the implicit public one.

/**
* Resolves an icon string to a {@link Material}.
* Accepts plain material names (e.g. {@code "DIAMOND"}), vanilla namespaced keys
* (e.g. {@code "minecraft:diamond"}), and custom item-model keys
* (e.g. {@code "myserver:island_tropical"}). Custom model keys that are not
* recognised vanilla materials fall back to {@link Material#PAPER}.
* @param icon the icon string, may be null
* @return resolved Material, never null
* @since 3.0.0
*/
public static Material parseIconMaterial(@Nullable String icon) {
if (icon == null) {
return Material.PAPER;
}
Material m = Material.matchMaterial(icon);
return m != null ? m : Material.PAPER;
}

/**
* Resolves an icon string to an {@link ItemStack}.
* <ul>
* <li>Plain material name or vanilla namespaced key → {@code new ItemStack(material)}</li>
* <li>Custom item-model key (namespace:key not matching any vanilla material) →
* PAPER base item with the model applied via {@link ItemMeta#setItemModel(NamespacedKey)}</li>
* </ul>
* @param icon the icon string, may be null
* @return resolved ItemStack, never null
* @since 3.0.0
*/
public static ItemStack parseIconItemStack(@Nullable String icon) {
if (icon == null) {
return new ItemStack(Material.PAPER);
}
Material m = Material.matchMaterial(icon);
if (m != null) {
return new ItemStack(m);
}
// Contains a colon but isn't a vanilla material → treat as a custom item model key
if (icon.contains(":")) {
ItemStack item = new ItemStack(Material.PAPER);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
String[] parts = icon.split(":", 2);
try {
meta.setItemModel(new NamespacedKey(parts[0], parts[1]));
item.setItemMeta(meta);
} catch (IllegalArgumentException ignored) {
// Invalid namespace/key format — return plain PAPER
}
}
return item;
}
return new ItemStack(Material.PAPER);
}
/**
* Parse given string to ItemStack.
* @param text String value of item stack.
Expand Down
Loading
Loading