Resolve grouping of unresolved types that likely should result in none > unsupported type#121
Open
R00tB33rMan wants to merge 1 commit into
Open
Resolve grouping of unresolved types that likely should result in none > unsupported type#121R00tB33rMan wants to merge 1 commit into
R00tB33rMan wants to merge 1 commit into
Conversation
…e > unsupported type
Owner
|
Hi, can you share a small example GUI that can be used to reproduce this issue? This exception would mean that the client sends the ContainerClickPacket with a different containerId. Depending on why this is it might make more sense to handle the click normally. |
Author
Sure! Here's an example: /*
* RootBeer
*
* Copyright [2016] - [2026] RootBeer
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains the property of RootBeer.
* The intellectual and technical concepts contained herein are proprietary to RootBeer
* and may be covered by applicable patents, patents in process, and are protected by
* trade secret or copyright law. Dissemination of this information or reproduction of
* this material is strictly forbidden unless prior written permission is obtained from
* RootBeer.
*/
package com.rootbeer.magnolia.module.shop.menu;
import com.rootbeer.magnolia.module.shop.config.ShopConfig;
import com.rootbeer.magnolia.module.shop.config.ShopMessages;
import com.rootbeer.magnolia.module.shop.loader.ShopLoader;
import com.rootbeer.magnolia.module.shop.manager.TransactionManager;
import com.rootbeer.magnolia.module.shop.object.ShopCategory;
import com.rootbeer.magnolia.module.shop.object.ShopItem;
import com.rootbeer.magnolia.util.menu.MenuUtil;
import com.xcodiq.localization.component.Components;
import com.xcodiq.paper.util.itemstack.ItemStackBuilder;
import com.xcodiq.paper.menu.item.ConfigurableItem;
import com.xcodiq.paper.menu.parameter.MenuParameter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import com.rootbeer.magnolia.menu.MagnoliaConfigurableMenu;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
public final class ShopAmountMenu extends MagnoliaConfigurableMenu {
private static final char ITEM_MARKER = 'i';
private final ShopCategory category;
private final ShopLoader shopLoader;
private final TransactionManager transactionManager;
private final ShopItem shopItem;
private final int stackSize;
private final int maxAmount;
private final int amount;
private final boolean selling;
public ShopAmountMenu(@NotNull ShopCategory category,
@NotNull ShopLoader shopLoader,
@NotNull TransactionManager transactionManager,
@NotNull ShopItem shopItem,
int amount,
boolean selling) {
super(selling
? "module/shop/menu/sell_amount_menu.yml"
: "module/shop/menu/amount_menu.yml", false);
this.category = category;
this.shopLoader = shopLoader;
this.transactionManager = transactionManager;
this.shopItem = shopItem;
this.stackSize = this.shopItem.item().getMaxStackSize();
this.selling = selling;
final int maxStacks = Math.min(ShopConfig.AMOUNT_MENU_MAX_STACKS.toInt(),
ShopConfig.AMOUNT_MENU_BUY_LIMIT_STACKS.toInt());
this.maxAmount = this.stackSize * maxStacks;
this.amount = Math.clamp(amount, 1, this.maxAmount);
this.structure.addIngredient(ITEM_MARKER, ConfigurableItem.builder()
.key("item_preview")
.withItem(_ -> {
final ItemStack preview = this.shopItem.displayItem().clone();
preview.setAmount(Math.min(this.amount, this.stackSize));
appendPreviewLore(preview);
MenuUtil.tag(preview);
return () -> preview;
})
.async()
.build());
registerStackButtons();
this.initialize();
this.feedbackButton("confirm",
player -> this.selling || this.transactionManager.canAfford(player, this.shopItem, this.amount),
this::confirm,
_ -> ShopMessages.BUY_FEEDBACK_REASON.toStr().replace("%price%", this.formatPriceForMode(this.amount)));
}
private void registerStackButtons() {
final int maxStacks = Math.min(ShopConfig.AMOUNT_MENU_MAX_STACKS.toInt(),
ShopConfig.AMOUNT_MENU_BUY_LIMIT_STACKS.toInt());
final String markers = ShopConfig.AMOUNT_MENU_STACKS_MARKERS.toStr();
final String nameTemplate = ShopConfig.AMOUNT_MENU_STACKS_NAME.toStr();
final List<String> loreTemplate = ShopConfig.AMOUNT_MENU_STACKS_LORE.toStringList();
final String materialOverride = ShopConfig.AMOUNT_MENU_STACKS_MATERIAL.toStr();
final Material overrideMaterial = materialOverride.equalsIgnoreCase("%item%")
? null
: Material.matchMaterial(materialOverride);
final boolean glint = ShopConfig.AMOUNT_MENU_STACKS_GLINT.toBoolean();
for (int s = 0; s < markers.length() && s < maxStacks; s++) {
final int stackCount = s + 1;
final int totalAmount = this.stackSize * stackCount;
final String formattedPrice = this.formatPriceForMode(totalAmount);
this.structure.addIngredient(markers.charAt(s), ConfigurableItem.builder()
.key("stacks_" + stackCount)
.withItem(_ -> {
final ItemStack display;
if (overrideMaterial != null) {
display = new ItemStack(overrideMaterial, stackCount);
} else {
display = this.shopItem.displayItem().clone();
display.setAmount(stackCount);
}
final List<String> loreLines = new ArrayList<>();
for (final String line : loreTemplate) {
loreLines.add(Components.prepareLine(line,
"%stacks%", String.valueOf(stackCount),
"%amount%", String.valueOf(totalAmount),
"%price%", formattedPrice));
}
final ItemStack result = ItemStackBuilder.of(display)
.apply(builder -> {
if (glint) {
builder.transformMeta(m -> m.setEnchantmentGlintOverride(true));
}
})
.name(Components.prepareLine(nameTemplate,
"%stacks%", String.valueOf(stackCount)))
.clearLore()
.lore(loreLines)
.build();
MenuUtil.tag(result);
return () -> result;
})
.onClick((player, _) -> {
if (MenuUtil.unacquireClick(player.getUniqueId())) {
return;
}
final int newAmount = this.stackSize * stackCount;
final int clamped = clamp(newAmount);
if (clamped == this.amount) {
MenuUtil.releaseClick(player.getUniqueId());
return;
}
openWithAmount(player, clamped);
})
.async()
.build());
}
}
@Override
public @NotNull MenuParameter[] setupMenuParameters() {
final int stacks = (int) Math.ceil((double) this.amount / this.stackSize);
return new MenuParameter[] {
MenuParameter.of("item", formatItemName(this.shopItem.item())),
MenuParameter.of("amount", String.valueOf(this.amount)),
MenuParameter.of("stacks", String.valueOf(stacks)),
MenuParameter.of("max_stack", String.valueOf(this.stackSize)),
MenuParameter.of("price", this.formatPriceForMode(this.amount))
};
}
/**
* Handles quantity selection and buy/sell confirmation.
* Supports preset amounts (1, 16, 32, 64) and custom input.
*/
@Override
public BiConsumer<Player, String> setupClick() {
return (player, key) -> {
if (MenuUtil.unacquireClick(player.getUniqueId())) {
return;
}
if (key.equals("cancel")) {
this.plugin.scheduler().now(player, () -> {
new ShopCategoryMenu(this.category, this.shopLoader, this.transactionManager)
.open(player);
MenuUtil.releaseClick(player.getUniqueId());
});
return;
}
final int newAmount = resolveAmount(key);
if (newAmount < 0 || clamp(newAmount) == this.amount) {
MenuUtil.releaseClick(player.getUniqueId());
return;
}
openWithAmount(player, newAmount);
};
}
@NotNull
private String formatPriceForMode(int amount) {
if (this.selling) {
return this.transactionManager.formatPrice(this.shopItem,
this.transactionManager.effectiveSellPrice(this.shopItem, amount));
}
return this.transactionManager.formatPrice(this.shopItem,
this.transactionManager.effectiveBuyPrice(this.shopItem, amount));
}
private int resolveAmount(@NotNull String key) {
if (key.equals("set_max")) {
return this.stackSize;
}
if (key.startsWith("set_")) {
final int value = parseTrailingInt(key, "set_");
if (value > 0) {
return value;
}
}
if (key.startsWith("add_")) {
final int value = parseTrailingInt(key, "add_");
if (value > 0) {
return this.amount + value;
}
}
if (key.startsWith("remove_")) {
final int value = parseTrailingInt(key, "remove_");
if (value > 0) {
return this.amount - value;
}
}
if (key.startsWith("stacks_")) {
final int value = parseTrailingInt(key, "stacks_");
if (value > 0) {
return this.stackSize * value;
}
}
return -1;
}
private int clamp(int value) {
return Math.clamp(value, 1, this.maxAmount);
}
private static int parseTrailingInt(@NotNull String key, @NotNull String prefix) {
try {
return Integer.parseInt(key.substring(prefix.length()));
} catch (NumberFormatException e) {
return -1;
}
}
private void appendPreviewLore(@NotNull ItemStack item) {
final int stacks = (int) Math.ceil((double) this.amount / this.stackSize);
final List<String> lines = new ArrayList<>();
lines.add("");
lines.add(Components.prepareLine(ShopConfig.AMOUNT_MENU_LORE_AMOUNT.toStr(),
"%amount%", String.valueOf(this.amount)));
if (stacks > 1) {
lines.add(Components.prepareLine(ShopConfig.AMOUNT_MENU_LORE_STACKS.toStr(),
"%stacks%", String.valueOf(stacks)));
}
lines.add(Components.prepareLine(ShopConfig.AMOUNT_MENU_LORE_PRICE.toStr(),
"%price%", this.formatPriceForMode(this.amount)));
final ItemStack result = ItemStackBuilder.of(item).lore(lines).build();
item.setItemMeta(result.getItemMeta());
}
private void confirm(@NotNull Player player) {
if (this.selling) {
this.plugin.scheduler().now(player, () ->
this.transactionManager.handleSell(player, this.shopItem, this.amount));
} else {
this.plugin.scheduler().now(player, () ->
this.transactionManager.handleBuy(player, this.shopItem, this.amount));
}
openWithAmount(player, this.amount);
}
private void openWithAmount(@NotNull Player player, int newAmount) {
this.plugin.scheduler().now(player, () -> {
new ShopAmountMenu(this.category, this.shopLoader,
this.transactionManager, this.shopItem, newAmount, this.selling)
.open(player);
MenuUtil.releaseClick(player.getUniqueId());
});
}
@NotNull
private static String formatItemName(@NotNull ItemStack item) {
if (item.hasItemMeta() && item.getItemMeta().hasDisplayName()) {
final Component displayName = item.getItemMeta().displayName();
if (displayName != null) {
return PlainTextComponentSerializer.plainText().serialize(displayName);
}
}
final String[] parts = item.getType().name().toLowerCase().split("_");
final StringBuilder name = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
if (i > 0) {
name.append(" ");
}
name.append(Character.toUpperCase(parts[i].charAt(0)));
name.append(parts[i].substring(1));
}
return name.toString();
}
} |
Owner
|
Please send a small, standalone, code snippet that I can run alongside steps to reproduce the exception. I can't run the code above as it depends on other classes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I ran into an obscure issue that I "best resolved" by setting these types to "none" when upgrading from Minecraft 26.1 -> 26.2 (or from InvUI 2.1.1 -> 2.2.0).
Example warning: https://mclo.gs/ebZ1Oke (I confirmed this patch DOES minimize the trip).
The action processed correctly (changing an item stack with a customary ShopGUIPlus-like module via adding or removing items). I can't see specific use cases that should be natively defined; thus, I went ahead and proceeded down this route. Let me know if this is wrong or if you'd like this done differently!