add support for non-supported bags/packs. If there is no compat layer yet for a bag/pack item from a mod, then attempt to place that bag into your hotbar and simulate a right click to open it. Prefer empty slots, non-tool slots, non-food slots if it needs to swap for something currently on your hotbar.

This commit is contained in:
trunksbomb
2026-03-23 03:43:29 -04:00
parent 274e4f6dec
commit 6271417381
6 changed files with 526 additions and 2 deletions

View File

@@ -21,7 +21,7 @@ public final class BagAccess {
BagIdentityData.ensureBagId(stack);
}
BagCompat.BagHandler handler = BagCompat.findHandler(stack);
if (handler != null) {
if (handler != null || BagCompat.canShowInTabs(stack)) {
bags.add(new BagEntry(slot, stack.copy(), handler, BagCompat.getIdentity(stack)));
}
}

View File

@@ -50,6 +50,10 @@ public final class BagCompat {
return !stack.isEmpty() && (findHandler(stack) != null || looksLikeInventoryItem(stack));
}
public static boolean canShowInTabs(ItemStack stack) {
return canName(stack);
}
public static BagIdentity getIdentity(ItemStack stack) {
if (!canName(stack)) {
return null;

View File

@@ -2,13 +2,27 @@ package com.trunksbomb.bagtabs.network;
import com.trunksbomb.bagtabs.BagTabs;
import com.trunksbomb.bagtabs.bag.BagCompat;
import com.trunksbomb.bagtabs.bag.InventoryBag;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.BowItem;
import net.minecraft.world.item.CrossbowItem;
import net.minecraft.world.item.DiggerItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ProjectileWeaponItem;
import net.minecraft.world.item.ShearsItem;
import net.minecraft.world.item.ShieldItem;
import net.minecraft.world.item.SwordItem;
import net.minecraft.world.item.TridentItem;
import net.neoforged.neoforge.network.handling.IPayloadContext;
public record OpenBagPayload(int slot) implements CustomPacketPayload {
@@ -37,6 +51,99 @@ public record OpenBagPayload(int slot) implements CustomPacketPayload {
BagCompat.BagHandler handler = BagCompat.findHandler(stack);
if (handler != null) {
handler.open(serverPlayer, payload.slot(), stack);
return;
}
if (BagCompat.canShowInTabs(stack)) {
if (!openViaHotbarFallback(serverPlayer, payload.slot())) {
serverPlayer.sendSystemMessage(BagTabs.translation("generic_open_failed"));
BagTabs.LOGGER.debug("Failed to open bag fallback for item {} in slot {}", stack, payload.slot());
}
}
}
private static boolean openViaHotbarFallback(ServerPlayer player, int sourceSlot) {
Inventory inventory = player.getInventory();
ItemStack sourceStack = inventory.getItem(sourceSlot);
if (sourceStack.isEmpty()) {
return false;
}
int targetHotbarSlot = chooseHotbarSlot(inventory, sourceSlot);
if (targetHotbarSlot < 0) {
return false;
}
if (sourceSlot != targetHotbarSlot) {
ItemStack hotbarStack = inventory.getItem(targetHotbarSlot).copy();
inventory.setItem(targetHotbarSlot, sourceStack.copy());
inventory.setItem(sourceSlot, hotbarStack);
}
inventory.selected = targetHotbarSlot;
player.connection.send(new ClientboundSetCarriedItemPacket(targetHotbarSlot));
player.inventoryMenu.broadcastChanges();
ItemStack mainHandStack = player.getItemInHand(InteractionHand.MAIN_HAND);
if (mainHandStack.isEmpty()) {
return false;
}
int previousContainerId = player.containerMenu.containerId;
InteractionResult result = player.gameMode.useItem(player, player.level(), mainHandStack, InteractionHand.MAIN_HAND);
player.inventoryMenu.broadcastChanges();
return result.consumesAction() || player.containerMenu.containerId != previousContainerId;
}
private static int chooseHotbarSlot(Inventory inventory, int sourceSlot) {
if (sourceSlot >= 0 && sourceSlot < Inventory.getSelectionSize()) {
return sourceSlot;
}
for (int hotbarSlot = 0; hotbarSlot < Inventory.getSelectionSize(); hotbarSlot++) {
if (inventory.getItem(hotbarSlot).isEmpty()) {
return hotbarSlot;
}
}
for (int hotbarSlot = 0; hotbarSlot < Inventory.getSelectionSize(); hotbarSlot++) {
if (isBuildingBlock(inventory.getItem(hotbarSlot))) {
return hotbarSlot;
}
}
for (int hotbarSlot = 0; hotbarSlot < Inventory.getSelectionSize(); hotbarSlot++) {
if (isPreferredMiscItem(inventory.getItem(hotbarSlot))) {
return hotbarSlot;
}
}
return -1;
}
private static boolean isBuildingBlock(ItemStack stack) {
return !stack.isEmpty() && stack.getItem() instanceof BlockItem && !isTorch(stack.getItem());
}
private static boolean isPreferredMiscItem(ItemStack stack) {
if (stack.isEmpty()) {
return false;
}
Item item = stack.getItem();
return !stack.has(DataComponents.FOOD)
&& !isTorch(item)
&& !(item instanceof DiggerItem)
&& !(item instanceof SwordItem)
&& !(item instanceof BowItem)
&& !(item instanceof CrossbowItem)
&& !(item instanceof TridentItem)
&& !(item instanceof ShieldItem)
&& !(item instanceof ShearsItem)
&& !(item instanceof ProjectileWeaponItem);
}
private static boolean isTorch(Item item) {
return item instanceof BlockItem blockItem
&& blockItem.getBlock().getDescriptionId().toLowerCase(java.util.Locale.ROOT).contains("torch");
}
}

View File

@@ -36,6 +36,7 @@
"bagtabs.dock.side.screen_right": "Dock: Screen Right",
"bagtabs.dock.side.floating_horizontal": "Dock: Floating Horizontal",
"bagtabs.dock.side.floating_vertical": "Dock: Floating Vertical",
"bagtabs.generic_open_failed": "No safe hotbar slot was available to open that bag.",
"key.categories.bagtabs": "Bag Tabs",
"key.bagtabs.open_last_bag": "Open Last Bag"
}