Skip to content

1.19.4 inventory error message when using newest IF version #823

@JExcellence

Description

@JExcellence

Is there an existing issue for this?

  • I have searched the existing issues

🐛 Describe the bug

on my current code

`package com.raindropcentral.rdq.view.bounty;

import com.raindropcentral.rdq.RDQ;
import com.raindropcentral.rdq.database.entity.bounty.Bounty;
import com.raindropcentral.rdq.database.entity.bounty.BountyReward;
import com.raindropcentral.rdq.reward.ItemReward;
import com.raindropcentral.rdq.reward.Reward;
import com.raindropcentral.rplatform.utility.heads.view.Proceed;
import com.raindropcentral.rplatform.utility.unified.UnifiedBuilderFactory;
import com.raindropcentral.rplatform.view.APaginatedView;
import de.jexcellence.jextranslate.i18n.I18n;
import me.devnatan.inventoryframework.component.BukkitItemComponentBuilder;
import me.devnatan.inventoryframework.context.CloseContext;
import me.devnatan.inventoryframework.context.Context;
import me.devnatan.inventoryframework.context.RenderContext;
import me.devnatan.inventoryframework.context.SlotClickContext;
import me.devnatan.inventoryframework.state.MutableState;
import me.devnatan.inventoryframework.state.State;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.concurrent.CompletableFuture;

public class BountyRewardView extends APaginatedView {

private final State<RDQ>                                rdq              = initialState("plugin");
private final MutableState<Optional<OfflinePlayer>>     target           = initialState("target");
private final MutableState<List<BountyReward>>             rewards      = initialState("rewards");
private final State<Optional<Bounty>>                  bounty           = initialState("bounty");
private final State<Map<UUID, Map<Integer, ItemStack>>> insertedItems    = initialState("insertedItems");

private boolean isReturning;

public BountyRewardView() {
	super(BountyCreationView.class);
}

@Override
protected String getKey() {
	
	return "bounty_reward_ui";
}

@Override
protected String[] getLayout() {
	
	return new String[]{
		"    t    ",
		"< OOOOO >",
		"         ",
		" xxxxxxx ",
		"         ",
		"b       c"
	};
}

@Override
protected CompletableFuture<List<BountyReward>> getAsyncPaginationSource(
	final @NotNull Context context
) {
	
	if (
		this.bounty.get(context).isEmpty() ||
		this.bounty.get(context).get().getRewards().isEmpty()
	) {
		BountyReward pseudoItem = new BountyReward(
				new ItemReward(this.buildPane(
						Material.GRAY_STAINED_GLASS_PANE,
						context.getPlayer(),
						"pseudo.name",
						"pseudo.lore"
				),
				0
				),
				UUID.randomUUID()
		);

		return CompletableFuture.completedFuture(List.of(
			pseudoItem,
			pseudoItem,
			pseudoItem,
			pseudoItem,
			pseudoItem
		));
	}
	return CompletableFuture.completedFuture(
		this.bounty.get(context).get().getRewards().stream().filter(bountyReward -> bountyReward.getReward().getType().equals(Reward.Type.ITEM)).toList()
	);
}

@Override
protected void renderEntry(
	final @NotNull Context context,
	final @NotNull BukkitItemComponentBuilder builder,
	final int index,
	final @NotNull BountyReward bountyReward
) {
	ItemReward itemReward = ((ItemReward) bountyReward.getReward());
	
	if (
		this.bounty.get(context).isEmpty()
	) {
		builder
			.renderWith(() -> UnifiedBuilderFactory.item(
					itemReward.getItem()
			).build())
			.updateOnStateChange(this.bounty);
		return;
	}
	this
		.splitToMaxStacks(itemReward)
		.forEach(
			item -> builder
				        .renderWith(
					        () -> UnifiedBuilderFactory
						              .item(item.clone())
						              .setName(item.clone().displayName())
						              .setLore(
							              this.i18n(
								                  "reward_item.lore",
								                  context.getPlayer()
							                  )
							                  .build()
							                  .children()
						              )
						              .addLoreLines(
							              item.lore() == null ?
							              new ArrayList<>() :
							              Objects.requireNonNull(item.lore())
						              )
						              .build()
				        )
				        .updateOnStateChange(this.bounty));
}

@Override
protected void onPaginatedRender(
	final @NotNull RenderContext render,
	final @NotNull Player player
) {

	final OfflinePlayer target = this.target.get(render).orElse(null);

	render
		.layoutSlot(
			'x',
			buildPane(
				Material.GREEN_STAINED_GLASS_PANE,
				player,
				"input_slot.name",
				"input_slot.lore"
			)
		)
		.onClick(this::handleSlotClick);
	
	if (
		this.insertedItems.get(render).containsKey(player.getUniqueId()) &&
		! this.insertedItems.get(render).get(player.getUniqueId()).isEmpty()
	) {
		this.insertedItems
			.get(render)
			.get(player.getUniqueId())
			.forEach(
				(slot, item) -> render.slot(
					slot,
					item
				).onClick(this::handleSlotClick)
			)
		;
	}
	
	render.layoutSlot(
		't',
		UnifiedBuilderFactory
			.unifiedHead(target.getPlayer())
			.setDisplayName(
				(net.kyori.adventure.text.Component) this.i18n(
					    "target.name",
					    player
				    ).withPlaceholder("target_name",
					    target.getName() == null ?
					    "" :
					    target.getName()
				    )
				    .build()
				    .component()
			)
			.setLore(
				this.i18n(
					"target.lore",
					player
				).withPlaceholders(
					Map.of(
						"target_name",
						target.getName() == null ?
						"" :
						target.getName()
					)
				).build().children()
			)
			.build()
	);
	
	render
		.layoutSlot(
			'c',
			new Proceed().getHead(player)
		)
		.updateOnStateChange(this.insertedItems)
		.onClick(clickContext -> {
			Map<Integer, ItemStack> playerSlots = this.insertedItems.get(render).get(player.getUniqueId());
			
			if (
				playerSlots != null &&
				! playerSlots.isEmpty()
			) {
				List<BountyReward> newRewards = new ArrayList<>();
				for (
					ItemStack stack : playerSlots.values()
				) {
					// Create a clean ItemStack with amount=1 for the ItemReward constructor
					ItemStack template = stack.clone();
					int originalAmount = template.getAmount();
					
					// Ensure the template has amount=1 before passing to constructor
					template.setAmount(1);
					
					// Create the ItemReward - constructor will handle amount properly
					ItemReward itemReward = new ItemReward(template, originalAmount);
					// The constructor should set the amount correctly, but let's be explicit
					itemReward.setAmount(originalAmount);
					
					newRewards.add(new BountyReward(
							itemReward,
							player.getUniqueId()
					));
				}
				
				// Only add the new rewards that were just inserted, don't accumulate with existing ones
				// The existing rewards are already in the bounty, we just want to add the new ones
				this.rewards.get(clickContext).clear();
				this.rewards.get(clickContext).addAll(newRewards);
				this.isReturning = true;
				
				clickContext.openForPlayer(
					BountyCreationView.class,
					Map.of(
							"plugin",
							rdq.get(clickContext),
							"target",
							this.target.get(render),
							"rewards",
							this.rewards.get(clickContext),
							"bounty",
							bounty.get(clickContext),
							"insertedItems",
							insertedItems.get(clickContext)
					)
				);
			} else {
				this.i18n(
					"no_new_items_inserted",
					player
				).includePrefix().build().sendMessage();
			}
		});
}

@Override
public void onClick(
	final @NotNull SlotClickContext click
) {
	if (
		click.isShiftClick() &&
		click.getClickedContainer().isEntityContainer()
	) {
		this.handleShiftClick(click);
		return;
	}

	if (
		! click.isShiftClick() &&
		click.getClickedContainer().isEntityContainer()
	) {
		click.setCancelled(false);
	}
}

@Override
public void onClose(
	final @NotNull CloseContext close
) {
	
	if (
		this.isReturning
	) {
		return;
	}
	
	if (
		this.insertedItems.get(close).containsKey(close.getPlayer().getUniqueId())
	) {
		refundInsertedItems(
			close.getPlayer(),
			this.insertedItems.get(close).get(close.getPlayer().getUniqueId()).values()
		);
		
		this.insertedItems.get(close).remove(close.getPlayer().getUniqueId());
	}
	
	this.rdq
		.get(close)
		.getViewFrame()
		.open(
			BountyCreationView.class,
			close.getPlayer(),
				Map.of(
						"plugin",
						rdq.get(close),
						"target",
						this.target.get(close),
						"rewards",
						this.rewards.get(close),
						"bounty",
						bounty.get(close),
						"insertedItems",
						insertedItems.get(close)
				)
		)
	;
}

private void handleSlotClick(
	final @NotNull SlotClickContext clickContext
) {
	
	final ItemStack cursorItem      = clickContext.getClickOrigin().getCursor();
	final int       clickedSlot     = clickContext.getClickedSlot();
	final ItemStack currentSlotItem = clickContext.getClickOrigin().getCurrentItem();
	
	boolean isSlotEmptyOrGreenPane =
		currentSlotItem == null
		|| currentSlotItem.getType() == Material.AIR
		|| currentSlotItem.getType() == Material.GREEN_STAINED_GLASS_PANE;
	
	Map<Integer, ItemStack> playerSlots = this.insertedItems.get(clickContext).computeIfAbsent(
		clickContext
			.getPlayer()
			.getUniqueId(),
		k -> new HashMap<>()
	);
	
	if (
		clickContext.getClickedContainer().isEntityContainer() &&
		clickContext.isShiftClick()
	) {
		clickContext.setCancelled(true);
		return;
	}
	
	if (
		clickContext.isLeftClick()
	) {
		if (
			isSlotEmptyOrGreenPane &&
			cursorItem.getType() != Material.AIR
		) {
			clickContext.getClickOrigin().setCursor(null);
			playerSlots.put(
				clickedSlot,
				cursorItem.clone()
			);
			clickContext
				.getClickedContainer()
				.renderItem(
					clickedSlot,
					cursorItem
				);
		}
		return;
	}
	
	if (
		clickContext.isRightClick()
	) {
		if (
			! isSlotEmptyOrGreenPane &&
			currentSlotItem.getType() != Material.AIR
		) {
			ItemStack removed = playerSlots.remove(clickedSlot);
			if (
				removed != null
			) {
				refundInsertedItems(
					clickContext.getPlayer(),
					List.of(removed)
				);
			}
			clickContext
				.getClickedContainer()
				.renderItem(
					clickedSlot,
					buildPane(
						Material.GREEN_STAINED_GLASS_PANE,
						clickContext.getPlayer(),
						"input_slot.name",
						"input_slot.lore"
					)
				);
		}
	}
}

private void handleShiftClick(
	final @NotNull SlotClickContext click
) {
	
	final Player    player      = click.getPlayer();
	final ItemStack clickedItem = click.getClickOrigin().getCurrentItem();
	
	if (
		clickedItem != null &&
		clickedItem.getType() != Material.AIR
	) {
		Inventory guiInv = player.getOpenInventory().getTopInventory();
		int targetSlot = findFirstPaneSlot(
			guiInv,
			Set.of(
				Material.LIME_STAINED_GLASS_PANE,
				Material.GREEN_STAINED_GLASS_PANE
			)
		);
		if (
			targetSlot != - 1
		) {
			player
				.getInventory()
				.removeItem(clickedItem);
			guiInv.setItem(
				targetSlot,
				clickedItem.clone()
			);
			this.insertedItems
				.get(click)
				.computeIfAbsent(
					player.getUniqueId(),
					k -> new HashMap<>()
				)
				.put(
					targetSlot,
					clickedItem.clone()
				)
			;
			click.setCancelled(true);
			return;
		}
	}
	click.setCancelled(true);
}

private int findFirstPaneSlot(
	final @NotNull Inventory inv,
	final @NotNull Set<Material> paneTypes
) {
	
	for (
		int i = 0; i < inv.getSize(); i++
	) {
		ItemStack slotItem = inv.getItem(i);
		if (slotItem != null && paneTypes.contains(slotItem.getType())) {
			return i;
		}
	}
	return - 1;
}

private ItemStack buildPane(
	final @NotNull Material paneType,
	final @NotNull Player player,
	final @NotNull String nameKey,
	final @NotNull String loreKey
) {
	
	return UnifiedBuilderFactory
		       .item(paneType)
		       .setName(
					   new I18n.Builder(nameKey, player).build().component()
		       )
		       .setLore(
			       new I18n.Builder(loreKey, player).build().children()
		       )
		       .addItemFlags(ItemFlag.HIDE_ATTRIBUTES)
		       .build();
}

private List<ItemStack> splitToMaxStacks(
	final @NotNull ItemReward rewardItem
) {
	
	List<ItemStack> result   = new ArrayList<>();
	ItemStack       base     = rewardItem.getItem();
	int             total    = rewardItem.getAmount(); // Use the reward amount, not the ItemStack amount
	int             maxStack = base.getMaxStackSize();
	
	while (
		total > 0
	) {
		int stackAmount = Math.min(
			total,
			maxStack
		);
		ItemStack stack = base.clone();
		stack.setAmount(stackAmount);
		result.add(stack);
		total -= stackAmount;
	}
	return result;
}

private void refundInsertedItems(
	final @NotNull Player player,
	final @NotNull Collection<ItemStack> items
) {
	
	if (
		items.isEmpty()
	) {
		return;
	}
	player
		.getInventory()
		.addItem(items.toArray(new ItemStack[0]))
		.forEach((i, item) -> player
			                      .getWorld()
			                      .dropItem(
				                      player
					                      .getLocation()
					                      .clone()
					                      .add(
						                      0,
						                      0.5,
						                      0
					                      ),
				                      item
			                      ))
	;
	
	this.i18n(
		    "left_overs",
		    player
	    )
	    .includePrefix()
	    .build().sendMessage();
	;
}

}`

I get following issue when doing shift click (after checking for the inventory)

[04:16:14 INFO]: java.lang.IncompatibleClassChangeError: Found class org.bukkit.inventory.InventoryView, but interface was expected [04:16:14 INFO]: at RDQ-6.0.0-Alpha-Build-1-Premium.jar//com.raindropcentral.rdq.view.bounty.BountyRewardView.handleShiftClick(BountyRewardView.java:431) [04:16:14 INFO]: at RDQ-6.0.0-Alpha-Build-1-Premium.jar//com.raindropcentral.rdq.view.bounty.BountyRewardView.onClick(BountyRewardView.java:283) [04:16:14 INFO]: at RDQ-6.0.0-Alpha-Build-1-Premium.jar//com.raindropcentral.rdq.view.bounty.BountyRewardView.onClick(BountyRewardView.java:30) [04:16:14 INFO]: at de.jexcellence.remapped.me.devnatan.inventoryframework.pipeline.GlobalClickInterceptor.intercept(GlobalClickInterceptor.java:25) [04:16:14 INFO]: at de.jexcellence.remapped.me.devnatan.inventoryframework.pipeline.GlobalClickInterceptor.intercept(GlobalClickInterceptor.java:14) [04:16:14 INFO]: at de.jexcellence.remapped.me.devnatan.inventoryframework.pipeline.PipelineContext.loop(PipelineContext.java:43) [04:16:14 INFO]: at de.jexcellence.remapped.me.devnatan.inventoryframework.pipeline.PipelineContext.proceed(PipelineContext.java:53) [04:16:14 INFO]: at de.jexcellence.remapped.me.devnatan.inventoryframework.pipeline.PipelineContext.execute(PipelineContext.java:59) [04:16:14 INFO]: at de.jexcellence.remapped.me.devnatan.inventoryframework.pipeline.Pipeline.execute(Pipeline.java:118) [04:16:14 INFO]: at de.jexcellence.remapped.me.devnatan.inventoryframework.IFInventoryListener.onInventoryClick(IFInventoryListener.java:75) [04:16:14 INFO]: at com.destroystokyo.paper.event.executor.MethodHandleEventExecutor.execute(MethodHandleEventExecutor.java:40) [04:16:14 INFO]: at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:81) [04:16:14 INFO]: at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) [04:16:14 INFO]: at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) [04:16:14 INFO]: at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:126) [04:16:14 INFO]: at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:615) [04:16:14 INFO]: at net.minecraft.server.network.PlayerConnection.a(PlayerConnection.java:3196) [04:16:14 INFO]: at net.minecraft.network.protocol.game.PacketPlayInWindowClick.a(PacketPlayInWindowClick.java:58) [04:16:14 INFO]: at net.minecraft.network.protocol.game.PacketPlayInWindowClick.a(PacketPlayInWindowClick.java:23) [04:16:14 INFO]: at net.minecraft.network.protocol.PlayerConnectionUtils.lambda$ensureRunningOnSameThread$0(PlayerConnectionUtils.java:51) [04:16:14 INFO]: at net.minecraft.server.TickTask.run(TickTask.java:18) [04:16:14 INFO]: at net.minecraft.util.thread.IAsyncTaskHandler.d(IAsyncTaskHandler.java:153) [04:16:14 INFO]: at net.minecraft.util.thread.IAsyncTaskHandlerReentrant.d(IAsyncTaskHandlerReentrant.java:24) [04:16:14 INFO]: at net.minecraft.server.MinecraftServer.b(MinecraftServer.java:1342) [04:16:14 INFO]: at net.minecraft.server.MinecraftServer.d(MinecraftServer.java:197) [04:16:14 INFO]: at net.minecraft.util.thread.IAsyncTaskHandler.x(IAsyncTaskHandler.java:126) [04:16:14 INFO]: at net.minecraft.server.MinecraftServer.bi(MinecraftServer.java:1319) [04:16:14 INFO]: at net.minecraft.server.MinecraftServer.x(MinecraftServer.java:1312) [04:16:14 INFO]: at net.minecraft.util.thread.IAsyncTaskHandler.c(IAsyncTaskHandler.java:136) [04:16:14 INFO]: at net.minecraft.server.MinecraftServer.i_(MinecraftServer.java:1290) [04:16:14 INFO]: at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:1178) [04:16:14 INFO]: at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:320) [04:16:14 INFO]: at java.base/java.lang.Thread.run(Thread.java:1447)

The error happens on the method
`
private void handleShiftClick(
final @NotNull SlotClickContext click
) {

	final Player    player      = click.getPlayer();
	final ItemStack clickedItem = click.getClickOrigin().getCurrentItem();
	
	if (
		clickedItem != null &&
		clickedItem.getType() != Material.AIR
	) {
		Inventory guiInv = player.getOpenInventory().getTopInventory();
		int targetSlot = findFirstPaneSlot(
			guiInv,
			Set.of(
				Material.LIME_STAINED_GLASS_PANE,
				Material.GREEN_STAINED_GLASS_PANE
			)
		);
		if (
			targetSlot != - 1
		) {
			player
				.getInventory()
				.removeItem(clickedItem);
			guiInv.setItem(
				targetSlot,
				clickedItem.clone()
			);
			this.insertedItems
				.get(click)
				.computeIfAbsent(
					player.getUniqueId(),
					k -> new HashMap<>()
				)
				.put(
					targetSlot,
					clickedItem.clone()
				)
			;
			click.setCancelled(true);
			return;
		}
	}
	click.setCancelled(true);
}

`

✔️ Expected behavior

No response

👣 Steps to Reproduce

Inventory guiInv = player.getOpenInventory().getTopInventory();

💻 Platform

  • Bukkit
  • Sponge
  • Other

⭐ Server Version

1.19.4

📚 Library Version

3.7.1

✍️ Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions