Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4ae40bf
Use modern plain message API
pxlarified Aug 23, 2026
8c402fe
Use component item display names
pxlarified Aug 23, 2026
7ffa0db
Use component item lore
pxlarified Aug 23, 2026
5c8e913
Use component broadcasts for debug logs
pxlarified Aug 23, 2026
4bd847a
Use component player locale
pxlarified Aug 23, 2026
8c3cf56
Use component inventory titles
pxlarified Aug 23, 2026
13286fe
Use damageable armor metadata
pxlarified Aug 23, 2026
b272c74
Use inventory views for cursor updates
pxlarified Aug 23, 2026
a7de1be
Use typed falling block spawning
pxlarified Aug 23, 2026
778dfbf
Use precise target block lookup
pxlarified Aug 23, 2026
00c05e8
Use fireball acceleration API
pxlarified Aug 23, 2026
568f7da
Use recipe choice APIs
pxlarified Aug 23, 2026
7ba53d1
Use event-owned dispatch
pxlarified Aug 23, 2026
e70ff52
Use cross-version custom item names
pxlarified Aug 23, 2026
f9fc117
Use cross-version custom model data
pxlarified Aug 23, 2026
d37a54f
Use damage source API for energy blasts
pxlarified Aug 23, 2026
639cd02
Use Paper item damage APIs
pxlarified Aug 23, 2026
4aaa0ad
Use registry sound lookup
pxlarified Aug 23, 2026
b6f778e
Add variable rule to AGENTS.md
pxlarified Aug 24, 2026
67263c7
Add backpack cosmetic armor stand support
pxlarified Aug 26, 2026
26ccc43
Make armor stand backpack settings configurable
pxlarified Aug 26, 2026
b64bc27
Remove Orbs setup
pxlarified Aug 26, 2026
0d225c4
Bump version to 1.220.0
pxlarified Aug 27, 2026
7c25f24
Handle null recipes in crafting permission checks
pxlarified Aug 27, 2026
050b375
Use Paper events for furniture item picking
pxlarified Aug 29, 2026
a9126f7
Refactor armor stand backpack display handling
pxlarified Aug 29, 2026
31994fe
Add smithing, anvil and grindstone recipe builders
pxlarified Aug 30, 2026
8f4ed6b
Fix workstation recipe builder inventory handling
pxlarified Aug 31, 2026
f4bd76f
Update recipe builder titles and messages
pxlarified Sep 1, 2026
d2e9103
Add support for picking custom blocks
pxlarified Aug 30, 2026
9c29efe
Add support for picking shaped blocks
pxlarified Aug 30, 2026
49619a5
Fix custom item picking behavior
pxlarified Aug 31, 2026
22e1893
Register furniture block location serialization
pxlarified Sep 1, 2026
e3a9223
Merge 1.219.0 into 1.220.0
pxlarified Sep 1, 2026
b256a04
Remove unnecessary NMS abstractions
pxlarified Sep 1, 2026
b497bd1
Add death protection item component support
pxlarified Sep 2, 2026
1a2e9bf
Fix death protection component handling
pxlarified Sep 2, 2026
40e0913
Fix death protection consume effects
pxlarified Sep 2, 2026
e48af77
Prevent client-side noteblock stack flicker
pxlarified Sep 4, 2026
a1e18c0
Prevent adjacent vanilla block flicker
pxlarified Sep 4, 2026
4a8ef51
Block restricted Oraxen items in crafters
pxlarified Sep 5, 2026
96bf4d9
Add lowercase item section migration
pxlarified Sep 5, 2026
cba18a2
Use lowercase item configuration sections
pxlarified Sep 5, 2026
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 gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pluginVersion=1.219.0
pluginVersion=1.220.0

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/main/java/io/th0rgal/oraxen/OraxenPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.th0rgal.oraxen.hud.HudManager;
import io.th0rgal.oraxen.items.ItemUpdater;
import io.th0rgal.oraxen.mechanics.MechanicsManager;
import io.th0rgal.oraxen.mechanics.provided.gameplay.CustomBlockPickItemListener;
import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureFactory;
import io.th0rgal.oraxen.nms.NMSHandlers;
import io.th0rgal.oraxen.pack.dispatch.PackLoadingManager;
Expand Down Expand Up @@ -134,6 +135,9 @@ public void onEnable() {
if (CustomBlockMiningListener.isSupported()) {
Bukkit.getPluginManager().registerEvents(new CustomBlockMiningListener(), this);
}
if (VersionUtil.atOrAbove("1.21.5")) {
Bukkit.getPluginManager().registerEvents(new CustomBlockPickItemListener(), this);
}
NMSHandlers.setup();

// Auto-update Paper config for block updates (noteblock, tripwire, chorus)
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/io/th0rgal/oraxen/commands/RecipesCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ private OraxenCommand getBuilderCommand() {
.withSubcommand(getCampfireBuilderCommand())
.withSubcommand(getSmokingBuilderCommand())
.withSubcommand(getStonecuttingBuilderCommand())
.withSubcommand(getSmithingBuilderCommand())
.withSubcommand(getAnvilBuilderCommand())
.withSubcommand(getGrindstoneBuilderCommand())
.executes((sender, args) -> {
if (sender instanceof Player player) {
final RecipeBuilder recipe = RecipeBuilder.get(player.getUniqueId());
Expand Down Expand Up @@ -182,6 +185,50 @@ private OraxenCommand getStonecuttingBuilderCommand() {
});
}

private OraxenCommand getSmithingBuilderCommand() {
return new OraxenCommand("smithing")
.withPermission("oraxen.command.recipes.builder")
.executes((sender, args) -> {
if (sender instanceof Player player) {
final RecipeBuilder recipe = RecipeBuilder.get(player.getUniqueId());
(recipe != null ? recipe : new SmithingBuilder(player)).open();
} else
Message.NOT_PLAYER.send(sender);
});
}

private OraxenCommand getAnvilBuilderCommand() {
return new OraxenCommand("anvil")
.withPermission("oraxen.command.recipes.builder")
.withArguments(new IntegerArgument("experience_cost"))
.executes((sender, args) -> {
if (sender instanceof Player player) {
RecipeBuilder recipe = RecipeBuilder.get(player.getUniqueId());
recipe = recipe != null ? recipe : new AnvilBuilder(player);
if (recipe instanceof AnvilBuilder anvil)
anvil.setExperienceCost((Integer) args.get("experience_cost"));
recipe.open();
} else
Message.NOT_PLAYER.send(sender);
});
}

private OraxenCommand getGrindstoneBuilderCommand() {
return new OraxenCommand("grindstone")
.withPermission("oraxen.command.recipes.builder")
.withArguments(new IntegerArgument("experience"))
.executes((sender, args) -> {
if (sender instanceof Player player) {
RecipeBuilder recipe = RecipeBuilder.get(player.getUniqueId());
recipe = recipe != null ? recipe : new GrindstoneBuilder(player);
if (recipe instanceof GrindstoneBuilder grindstone)
grindstone.setExperience((Integer) args.get("experience"));
recipe.open();
} else
Message.NOT_PLAYER.send(sender);
});
}

private OraxenCommand getSaveCommand() {
return new OraxenCommand("save")
.withPermission("oraxen.command.recipes.builder")
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/io/th0rgal/oraxen/configs/ConfigsManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,9 @@ public void assignAllUsedModelDatas() {
ConfigurationSection itemSection = configuration.getConfigurationSection(key);
if (itemSection == null)
continue;
ConfigurationSection packSection = OraxenYaml.getConfigurationSection(itemSection, "Pack");
ItemMigrator migrator = new ItemMigrator(itemSection);
fileChanged |= migrator.configUpdated();
ConfigurationSection packSection = itemSection.getConfigurationSection("pack");
Material material = OraxenYaml.getMaterial(itemSection.getString("material", ""));
if (packSection == null || material == null)
continue;
Expand Down Expand Up @@ -852,7 +854,7 @@ public void parseAllItemTemplates() {
if (itemSection == null || !itemSection.isBoolean("template")) continue;

ItemMigrator migrator = new ItemMigrator(itemSection);
ConfigurationSection mechanicsSection = OraxenYaml.getConfigurationSection(itemSection, "Mechanics");
ConfigurationSection mechanicsSection = itemSection.getConfigurationSection("mechanics");
if (mechanicsSection != null) migrator.migrateLegacyBlockMechanics(mechanicsSection);
configUpdated |= migrator.configUpdated();
blockConfigMigrated |= migrator.blockConfigMigrated();
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/io/th0rgal/oraxen/configs/ResourcesManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,18 @@ private void extractVersionSpecificItemConfig(ZipEntry entry) {
ConfigurationSection itemSection = itemYaml.getConfigurationSection(itemId);
if (itemSection == null) continue;

ConfigurationSection mechanicSection = itemSection.getConfigurationSection("Mechanics");
ConfigurationSection mechanicSection = itemSection.getConfigurationSection("mechanics");
if (mechanicSection == null) continue;

ConfigurationSection componentSection = itemSection.getConfigurationSection("Components");
if (componentSection == null) componentSection = itemSection.createSection("Components");
ConfigurationSection componentSection = itemSection.getConfigurationSection("components");
if (componentSection == null) componentSection = itemSection.createSection("components");

Object durability = mechanicSection.get("durability.value");
mechanicSection.set("durability", null);
componentSection.set("durability", durability);

if (mechanicSection.getKeys(false).isEmpty()) itemSection.set("Mechanics", null);
if (componentSection.getKeys(false).isEmpty()) itemSection.set("Components", null);
if (mechanicSection.getKeys(false).isEmpty()) itemSection.set("mechanics", null);
if (componentSection.getKeys(false).isEmpty()) itemSection.set("components", null);
}
File itemFile = plugin.getDataFolder().toPath().resolve(entry.getName()).toFile();

Expand Down
26 changes: 24 additions & 2 deletions src/main/java/io/th0rgal/oraxen/items/ItemBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ public class ItemBuilder {

// 1.21.2+ properties
@Nullable
private Object deathProtectionComponent;
@Nullable
private EquippableComponent equippableComponent;
@Nullable
private Boolean isGlider;
Expand Down Expand Up @@ -619,6 +621,20 @@ public <V> ItemBuilder setConsumableComponent(@Nullable V consumableComponent) {
return this;
}

public boolean hasDeathProtectionComponent() {
return VersionUtil.atOrAbove("1.21.2") && deathProtectionComponent != null;
}

@Nullable
public Object getDeathProtectionComponent() {
return deathProtectionComponent;
}

public <V> ItemBuilder setDeathProtectionComponent(@Nullable V deathProtectionComponent) {
this.deathProtectionComponent = deathProtectionComponent;
return this;
}

public boolean hasToolComponent() {
return VersionUtil.atOrAbove("1.20.5") && toolComponent != null;
}
Expand Down Expand Up @@ -863,6 +879,7 @@ public ItemBuilder clone() {
ItemBuilder clonedBuilder = new ItemBuilder(itemStack.clone());
clonedBuilder.genericComponents.putAll(genericComponents);
clonedBuilder.paintingVariant = paintingVariant;
clonedBuilder.deathProtectionComponent = deathProtectionComponent;
clonedBuilder.attributeEntries.clear();
clonedBuilder.attributeEntries.addAll(attributeEntries);
if (legacyAttributeModifiers != null) {
Expand Down Expand Up @@ -899,6 +916,7 @@ public synchronized ItemBuilder regen() {
// Build into a local and publish once so concurrent readers never see
// an intermediate stack.
ItemStack built = applyConsumableComponent(itemStack);
built = applyDeathProtectionComponent(built);
built = applyPaintingVariantComponent(built);
built = applyGenericComponents(built);
finalItemStack = built;
Expand Down Expand Up @@ -1098,6 +1116,10 @@ private ItemStack applyConsumableComponent(ItemStack itemStack) {
return NMSHandlers.getHandler().consumableComponent(itemStack, consumableComponent);
}

private ItemStack applyDeathProtectionComponent(ItemStack itemStack) {
return NMSHandlers.getHandler().deathProtectionComponent(itemStack, deathProtectionComponent);
}

private ItemStack applyGenericComponents(ItemStack itemStack) {
if (genericComponents.isEmpty()) return itemStack;
return NMSHandlers.getHandler().applyGenericComponents(itemStack, genericComponents);
Expand Down Expand Up @@ -1156,9 +1178,9 @@ public void save() {
yamlConfiguration.set(itemId + ".ItemFlags",
this.itemFlags.stream().map(ItemFlag::name).toList());
if (hasEquippableComponent()) {
yamlConfiguration.set(itemId + ".Components.equippable.slot",
yamlConfiguration.set(itemId + ".components.equippable.slot",
this.equippableComponent.getSlot().name());
yamlConfiguration.set(itemId + ".Components.equippable.model",
yamlConfiguration.set(itemId + ".components.equippable.model",
this.equippableComponent.getModel().toString());
}
try {
Expand Down
23 changes: 18 additions & 5 deletions src/main/java/io/th0rgal/oraxen/items/ItemComponents.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.th0rgal.oraxen.api.OraxenItems;
import io.th0rgal.oraxen.compatibilities.provided.ecoitems.WrappedEcoItem;
import io.th0rgal.oraxen.compatibilities.provided.mythiccrucible.WrappedCrucibleItem;
import io.th0rgal.oraxen.nms.NMSHandler;
import io.th0rgal.oraxen.nms.NMSHandlers;
import io.th0rgal.oraxen.utils.AdventureUtils;
import io.th0rgal.oraxen.utils.OraxenYaml;
Expand All @@ -20,6 +19,7 @@
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.components.EquippableComponent;
import org.bukkit.inventory.meta.components.FoodComponent;
import org.bukkit.inventory.meta.components.JukeboxPlayableComponent;
import org.bukkit.inventory.meta.components.ToolComponent;
import org.bukkit.inventory.meta.components.UseCooldownComponent;
Expand Down Expand Up @@ -47,7 +47,7 @@ private void parseDataComponents(final ItemBuilder item, final ConfigurationSect
else if (section.contains("displayname"))
applyItemName(item, section, "displayname");

final ConfigurationSection components = OraxenYaml.getConfigurationSection(section, "Components");
final ConfigurationSection components = section.getConfigurationSection("components");
applyRemainingComponents(item, components);
}

Expand Down Expand Up @@ -104,9 +104,8 @@ private void handleLegacyComponents(final ItemBuilder item, final ConfigurationS
if (OraxenYaml.contains(components, "enchantment_glint_override"))
item.setEnchantmentGlintOverride(OraxenYaml.getBoolean(components, "enchantment_glint_override"));

final NMSHandler nmsHandler = NMSHandlers.getHandler();
Optional.ofNullable(OraxenYaml.getConfigurationSection(components, "food"))
.ifPresent(food -> nmsHandler.foodComponent(item, food));
.ifPresent(food -> parseFoodComponent(item, food));

Optional.ofNullable(OraxenYaml.getConfigurationSection(components, "tool"))
.ifPresent(toolSection -> parseToolComponent(item, toolSection));
Expand Down Expand Up @@ -149,6 +148,10 @@ private void handleLegacyComponents(final ItemBuilder item, final ConfigurationS

if (!VersionUtil.atOrAbove("1.21.2"))
return;

Optional.ofNullable(OraxenYaml.getConfigurationSection(components, "death_protection"))
.ifPresent(deathProtection -> NMSHandlers.getHandler().deathProtectionComponent(item, deathProtection));

Optional.ofNullable(OraxenYaml.getConfigurationSection(components, "equippable"))
.ifPresent(equippable -> parseEquippableComponent(item, equippable));

Expand Down Expand Up @@ -179,7 +182,16 @@ private void handleLegacyComponents(final ItemBuilder item, final ConfigurationS
.ifPresent(item::setItemModel);

Optional.ofNullable(OraxenYaml.getConfigurationSection(components, "consumable"))
.ifPresent(consumableSection -> nmsHandler.consumableComponent(item, consumableSection));
.ifPresent(consumableSection -> NMSHandlers.getHandler().consumableComponent(item, consumableSection));
}

@SuppressWarnings("UnstableApiUsage")
private void parseFoodComponent(final ItemBuilder item, final ConfigurationSection foodSection) {
final FoodComponent foodComponent = new ItemStack(item.getType()).getItemMeta().getFood();
foodComponent.setNutrition(Math.max(foodSection.getInt("nutrition"), 0));
foodComponent.setSaturation(Math.max((float) foodSection.getDouble("saturation", 0.0), 0f));
foodComponent.setCanAlwaysEat(foodSection.getBoolean("can_always_eat", false));
item.setFoodComponent(foodComponent);
}

private boolean isLegacyComponent(final String key) {
Expand All @@ -193,6 +205,7 @@ private boolean isLegacyComponent(final String key) {
normalizedKey.equals("tool") ||
normalizedKey.equals("painting_variant") ||
normalizedKey.equals("jukebox_playable") ||
normalizedKey.equals("death_protection") ||
normalizedKey.equals("equippable") ||
normalizedKey.equals("use_cooldown") ||
normalizedKey.equals("use_remainder") ||
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/io/th0rgal/oraxen/items/ItemLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ public final class ItemLoader {
private final OraxenMeta oraxenMeta;
private final ConfigurationSection section;
private final Material type;
private final ItemMigrator migrator;
private WrappedMMOItem mmoItem;
private WrappedCrucibleItem crucibleItem;
private WrappedEcoItem ecoItem;
private ItemLoader templateItem;

public ItemLoader(final ConfigurationSection section) {
this.section = section;
migrator = new ItemMigrator(section);

if (section.isString("template"))
templateItem = ItemTemplate.getLoaderTemplate(section.getString("template"));
Expand All @@ -51,15 +53,17 @@ else if (mmoSection != null)
// Each item gets its own OraxenMeta: templates are merged in by value, never shared
// by reference, so sibling items cannot overwrite each other's pack info.
oraxenMeta = new OraxenMeta();
final ConfigurationSection mergedPackSection =
OraxenYaml.getConfigurationSection(mergeWithTemplateSection(), "Pack");
final ConfigurationSection mergedSection = mergeWithTemplateSection();
final ConfigurationSection mergedPackSection = mergedSection != null
? mergedSection.getConfigurationSection("pack")
: null;
if (mergedPackSection != null)
oraxenMeta.setPackInfos(mergedPackSection);

// Only an explicitly configured custom_model_data on the item itself is registered.
// Template children deliberately do not inherit the template's number, otherwise every
// sibling would resolve to the same one; they get an automatically assigned id instead.
final ConfigurationSection packSection = OraxenYaml.getConfigurationSection(section, "Pack");
final ConfigurationSection packSection = section.getConfigurationSection("pack");
if (packSection != null && packSection.isInt("custom_model_data"))
MODEL_DATAS_BY_ID.put(section.getName(),
new ModelData(type, oraxenMeta.getModelName(), packSection.getInt("custom_model_data")));
Expand Down Expand Up @@ -99,7 +103,7 @@ else if (usesEcoItems())
}

private ItemValidator validator() {
return new ItemValidator(section, mergeWithTemplateSection(), type, oraxenMeta, MODEL_DATAS_BY_ID);
return new ItemValidator(section, mergeWithTemplateSection(), type, oraxenMeta, MODEL_DATAS_BY_ID, migrator);
}

private ConfigurationSection mergeWithTemplateSection() {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/io/th0rgal/oraxen/items/ItemMechanics.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public ItemMechanics(final ConfigurationSection section, final ItemMigrator migr
}

public void apply(final ItemBuilder item, final ConfigurationSection mergedSection) {
final ConfigurationSection mechanicsSection = OraxenYaml.getConfigurationSection(mergedSection, "Mechanics");
final ConfigurationSection mechanicsSection = mergedSection.getConfigurationSection("mechanics");
if (mechanicsSection == null)
return;

Expand All @@ -36,8 +36,8 @@ public void apply(final ItemBuilder item, final ConfigurationSection mergedSecti
final MechanicFactory factory = MechanicsManager.getMechanicFactory(mechanicID);
if (factory == null) {
if (LEGACY_BLOCK_MECHANIC_IDS.contains(mechanicID.toLowerCase(Locale.ROOT)))
Logs.logWarning("Item " + section.getName() + " uses legacy Mechanics." + mechanicID
+ "; migrate it to Mechanics.block or this mechanic will be ignored.");
Logs.logWarning("Item " + section.getName() + " uses legacy mechanics." + mechanicID
+ "; migrate it to mechanics.block or this mechanic will be ignored.");
continue;
}

Expand Down
Loading