Files
purpur-heights/src/main/java/games/dmg/spigotheights/StatureBrewing.java
T
dmg df44bba9c6
Release / release (push) Successful in 6m27s
CI / build (push) Successful in 2m8s
fix(heights): repair native stature potion brewing
2026-09-11 22:16:48 -04:00

90 lines
3.6 KiB
Java

package games.dmg.spigotheights;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.BrewEvent;
/** Completes authenticated stature conversions at the native brewing transaction boundary. */
public final class StatureBrewing implements Listener {
private final PotionRecipes potions;
public StatureBrewing(PotionRecipes potions) {
this.potions = potions;
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void detachResults(BrewEvent event) {
if (event.isCancelled()) {
return;
}
try {
// Native no-op results can mirror the input itself. Detach before normal result modifiers
// run, otherwise an in-place result edit also changes the inventory despite cancellation.
for (int slot = 0; slot < Math.min(3, event.getResults().size()); slot++) {
ItemStack input = event.getContents().getItem(slot);
if (potions.identify(input) != null && input.equals(event.getResults().get(slot))) {
event.getResults().set(slot, input.clone());
}
}
} catch (RuntimeException exception) {
event.setCancelled(true);
throw exception;
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void brew(BrewEvent event) {
try {
convert(event);
} catch (RuntimeException exception) {
// Paper logs listener exceptions and continues dispatch: cancel first to prevent item loss.
event.setCancelled(true);
throw exception;
}
}
private void convert(BrewEvent event) {
ItemStack ingredient = event.getContents().getIngredient();
if (event.isCancelled() || ingredient == null) {
return;
}
Material source;
Material target;
if (ingredient.getType() == Material.GUNPOWDER) {
source = Material.POTION;
target = Material.SPLASH_POTION;
} else if (ingredient.getType() == Material.DRAGON_BREATH) {
source = Material.SPLASH_POTION;
target = Material.LINGERING_POTION;
} else {
return;
}
for (int slot = 0; slot < 3; slot++) {
ItemStack input = event.getContents().getItem(slot);
StaturePotion kind = potions.identify(input);
if (kind != null && input.getType() == source) {
if (slot >= event.getResults().size() || input.getAmount() != 1) {
event.setCancelled(true);
return;
}
ItemStack result = event.getResults().get(slot);
if (result != null && result.getType() == target
&& result.getAmount() == 1 && potions.identify(result) == kind) {
continue;
}
if (!input.equals(result)) {
// Do not overwrite another plugin's output or spend ingredients on a failed conversion.
event.setCancelled(true);
return;
}
// 2618 recognizes custom recipes but mix() returns base-less potions unchanged.
// Correct only that no-op; leave inventory and ingredient accounting to the server.
event.getResults().set(slot, potions.create(kind, target));
}
}
}
}