compat for Traveller's Backpacks and Sophisticated Storage.
Drag-and-drop item stacks into tabs for supported mods
This commit is contained in:
@@ -20,6 +20,7 @@ public class BagTabsClient {
|
||||
modEventBus.addListener(BagTabsClient::registerItemColors);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::renderTabs);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::clickTabs);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::releaseTabs);
|
||||
}
|
||||
|
||||
private static void registerScreens(RegisterMenuScreensEvent event) {
|
||||
@@ -39,6 +40,10 @@ public class BagTabsClient {
|
||||
BagTabOverlay.mouseClicked(event);
|
||||
}
|
||||
|
||||
private static void releaseTabs(ScreenEvent.MouseButtonReleased.Pre event) {
|
||||
BagTabOverlay.mouseReleased(event);
|
||||
}
|
||||
|
||||
private static final class BagItemColor {
|
||||
private static final int DEFAULT_TINT = 0x9E7B4F;
|
||||
|
||||
|
||||
@@ -61,6 +61,14 @@ public final class BagCompat {
|
||||
|
||||
void open(ServerPlayer player, int slot, ItemStack stack);
|
||||
|
||||
default boolean canInsertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean insertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default int getActiveBagSlot(AbstractContainerMenu menu) {
|
||||
return -1;
|
||||
}
|
||||
@@ -78,6 +86,32 @@ public final class BagCompat {
|
||||
inventoryBag.openFromInventory(player, slot);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
if (carriedStack.isEmpty() || carriedStack.getItem() instanceof InventoryBag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.containerMenu instanceof BagMenu bagMenu && bagMenu.getBagSlot() == slot) {
|
||||
return bagMenu.insertCarriedStack(carriedStack);
|
||||
}
|
||||
|
||||
return BagContainer.insertInto(new BagContainer(player, slot), carriedStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInsertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
if (carriedStack.isEmpty() || carriedStack.getItem() instanceof InventoryBag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.containerMenu instanceof BagMenu bagMenu && bagMenu.getBagSlot() == slot) {
|
||||
return bagMenu.canInsertCarriedStack(carriedStack);
|
||||
}
|
||||
|
||||
return BagContainer.canInsertInto(new BagContainer(player, slot), carriedStack);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TravelersBackpackHandler implements BagHandler {
|
||||
@@ -118,6 +152,41 @@ public final class BagCompat {
|
||||
Object slot = invoke(wrapper, "getBackpackSlotIndex");
|
||||
return slot instanceof Integer integer ? integer : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInsertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
if (carriedStack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object storage = getTravelersStorage(player, slot, bagStack);
|
||||
return storage != null && canInsertIntoItemHandler(storage, carriedStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
if (carriedStack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object storage = getTravelersStorage(player, slot, bagStack);
|
||||
return storage != null && insertIntoItemHandler(storage, carriedStack);
|
||||
}
|
||||
|
||||
private Object getTravelersStorage(ServerPlayer player, int slot, ItemStack bagStack) {
|
||||
if (isInstance(MENU_CLASS, player.containerMenu) && getActiveBagSlot(player.containerMenu) == slot) {
|
||||
Object wrapper = invoke(player.containerMenu, "getWrapper");
|
||||
return wrapper == null ? null : invoke(wrapper, "getStorage");
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> wrapperClass = Class.forName(WRAPPER_CLASS);
|
||||
Object wrapper = wrapperClass.getMethod("fromStack", ItemStack.class).invoke(null, bagStack);
|
||||
return wrapper == null ? null : invoke(wrapper, "getStorage");
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException exception) {
|
||||
throw new RuntimeException("Failed to access Traveler's Backpack storage", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DankStorageHandler implements BagHandler {
|
||||
@@ -158,6 +227,85 @@ public final class BagCompat {
|
||||
|
||||
return findMatchingPlayerSlot(menu, bagStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInsertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
if (carriedStack.isEmpty() || ItemStack.isSameItemSameComponents(carriedStack, bagStack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object dankInventory = getDankInventory(player, slot, bagStack);
|
||||
return dankInventory != null && canInsertIntoDank(dankInventory, carriedStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
if (carriedStack.isEmpty() || ItemStack.isSameItemSameComponents(carriedStack, bagStack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object dankInventory = getDankInventory(player, slot, bagStack);
|
||||
return dankInventory != null && insertIntoDank(dankInventory, carriedStack);
|
||||
}
|
||||
|
||||
private Object getDankInventory(ServerPlayer player, int slot, ItemStack bagStack) {
|
||||
if (isInstance(MENU_CLASS, player.containerMenu) && getActiveBagSlot(player.containerMenu) == slot) {
|
||||
return getFieldValue(player.containerMenu, "dankInventory");
|
||||
}
|
||||
|
||||
try {
|
||||
Method getInventoryFrom = bagStack.getItem().getClass().getMethod(
|
||||
"getInventoryFrom",
|
||||
ItemStack.class,
|
||||
Class.forName("net.minecraft.server.MinecraftServer")
|
||||
);
|
||||
return getInventoryFrom.invoke(null, bagStack, player.server);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException exception) {
|
||||
throw new RuntimeException("Failed to access Dank Storage inventory", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canInsertIntoDank(Object dankInventory, ItemStack stack) {
|
||||
ItemStack remaining = stack.copy();
|
||||
int slotCount = (Integer) invoke(dankInventory, "slotCount");
|
||||
|
||||
for (int slot = 0; slot < slotCount && !remaining.isEmpty(); slot++) {
|
||||
remaining = (ItemStack) invoke(
|
||||
dankInventory,
|
||||
"insertStack",
|
||||
new Class<?>[] {int.class, ItemStack.class, boolean.class},
|
||||
slot,
|
||||
remaining,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return remaining.getCount() < stack.getCount();
|
||||
}
|
||||
|
||||
private boolean insertIntoDank(Object dankInventory, ItemStack stack) {
|
||||
ItemStack remaining = stack.copy();
|
||||
int slotCount = (Integer) invoke(dankInventory, "slotCount");
|
||||
|
||||
for (int slot = 0; slot < slotCount && !remaining.isEmpty(); slot++) {
|
||||
remaining = (ItemStack) invoke(
|
||||
dankInventory,
|
||||
"insertStack",
|
||||
new Class<?>[] {int.class, ItemStack.class, boolean.class},
|
||||
slot,
|
||||
remaining,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
int inserted = stack.getCount() - remaining.getCount();
|
||||
if (inserted <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stack.shrink(inserted);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SophisticatedBackpacksHandler implements BagHandler {
|
||||
@@ -201,6 +349,42 @@ public final class BagCompat {
|
||||
Object slot = invoke(context, "getBackpackSlotIndex");
|
||||
return slot instanceof Integer integer ? integer : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInsertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
if (carriedStack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object inventoryHandler = getSophisticatedInventoryHandler(player, slot, bagStack);
|
||||
return inventoryHandler != null && canInsertIntoWholeHandler(inventoryHandler, carriedStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insertFromCarried(ServerPlayer player, int slot, ItemStack bagStack, ItemStack carriedStack) {
|
||||
if (carriedStack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object inventoryHandler = getSophisticatedInventoryHandler(player, slot, bagStack);
|
||||
return inventoryHandler != null && insertIntoWholeHandler(inventoryHandler, carriedStack);
|
||||
}
|
||||
|
||||
private Object getSophisticatedInventoryHandler(ServerPlayer player, int slot, ItemStack bagStack) {
|
||||
Object wrapper;
|
||||
if (isInstance(MENU_CLASS, player.containerMenu) && getActiveBagSlot(player.containerMenu) == slot) {
|
||||
wrapper = invoke(player.containerMenu, "getStorageWrapper");
|
||||
} else {
|
||||
try {
|
||||
Class<?> wrapperClass = Class.forName("net.p3pp3rf1y.sophisticatedbackpacks.backpack.wrapper.BackpackWrapper");
|
||||
wrapper = wrapperClass.getMethod("fromStack", ItemStack.class).invoke(null, bagStack);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException exception) {
|
||||
throw new RuntimeException("Failed to access Sophisticated Backpack wrapper", exception);
|
||||
}
|
||||
}
|
||||
|
||||
return wrapper == null ? null : invoke(wrapper, "getInventoryHandler");
|
||||
}
|
||||
}
|
||||
|
||||
private static int findMatchingPlayerSlot(AbstractContainerMenu menu, ItemStack bagStack) {
|
||||
@@ -267,6 +451,94 @@ public final class BagCompat {
|
||||
}
|
||||
}
|
||||
|
||||
private static Object invoke(Object target, String methodName, Class<?>[] parameterTypes, Object... args) {
|
||||
try {
|
||||
Method method = target.getClass().getMethod(methodName, parameterTypes);
|
||||
return method.invoke(target, args);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException exception) {
|
||||
throw new RuntimeException("Failed to invoke " + methodName + " on " + target.getClass().getName(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getFieldValue(Object target, String fieldName) {
|
||||
try {
|
||||
Field field = target.getClass().getField(fieldName);
|
||||
return field.get(target);
|
||||
} catch (IllegalAccessException | NoSuchFieldException exception) {
|
||||
throw new RuntimeException("Failed to access field " + fieldName + " on " + target.getClass().getName(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canInsertIntoItemHandler(Object itemHandler, ItemStack stack) {
|
||||
ItemStack remaining = stack.copy();
|
||||
int slots = (Integer) invoke(itemHandler, "getSlots");
|
||||
|
||||
for (int slot = 0; slot < slots && !remaining.isEmpty(); slot++) {
|
||||
remaining = (ItemStack) invoke(
|
||||
itemHandler,
|
||||
"insertItem",
|
||||
new Class<?>[] {int.class, ItemStack.class, boolean.class},
|
||||
slot,
|
||||
remaining,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return remaining.getCount() < stack.getCount();
|
||||
}
|
||||
|
||||
private static boolean insertIntoItemHandler(Object itemHandler, ItemStack stack) {
|
||||
ItemStack remaining = stack.copy();
|
||||
int slots = (Integer) invoke(itemHandler, "getSlots");
|
||||
|
||||
for (int slot = 0; slot < slots && !remaining.isEmpty(); slot++) {
|
||||
remaining = (ItemStack) invoke(
|
||||
itemHandler,
|
||||
"insertItem",
|
||||
new Class<?>[] {int.class, ItemStack.class, boolean.class},
|
||||
slot,
|
||||
remaining,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
int inserted = stack.getCount() - remaining.getCount();
|
||||
if (inserted <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stack.shrink(inserted);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean canInsertIntoWholeHandler(Object inventoryHandler, ItemStack stack) {
|
||||
ItemStack remaining = (ItemStack) invoke(
|
||||
inventoryHandler,
|
||||
"insertItem",
|
||||
new Class<?>[] {ItemStack.class, boolean.class},
|
||||
stack.copy(),
|
||||
true
|
||||
);
|
||||
return remaining.getCount() < stack.getCount();
|
||||
}
|
||||
|
||||
private static boolean insertIntoWholeHandler(Object inventoryHandler, ItemStack stack) {
|
||||
ItemStack remaining = (ItemStack) invoke(
|
||||
inventoryHandler,
|
||||
"insertItem",
|
||||
new Class<?>[] {ItemStack.class, boolean.class},
|
||||
stack.copy(),
|
||||
false
|
||||
);
|
||||
int inserted = stack.getCount() - remaining.getCount();
|
||||
if (inserted <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stack.shrink(inserted);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void invokeStatic(String className, String methodName, Class<?>[] parameterTypes, Object... args) {
|
||||
try {
|
||||
Class<?> targetClass = Class.forName(className);
|
||||
|
||||
@@ -46,6 +46,52 @@ public class BagContainer extends SimpleContainer {
|
||||
return current.getItem() instanceof BagItem;
|
||||
}
|
||||
|
||||
public static boolean insertInto(net.minecraft.world.Container container, ItemStack stack) {
|
||||
if (stack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean changed = mergeIntoExistingStacks(container, stack);
|
||||
changed |= fillEmptySlots(container, stack);
|
||||
|
||||
if (changed) {
|
||||
container.setChanged();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static boolean canInsertInto(net.minecraft.world.Container container, ItemStack stack) {
|
||||
if (stack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int remaining = stack.getCount();
|
||||
|
||||
for (int slot = 0; slot < container.getContainerSize() && remaining > 0; slot++) {
|
||||
ItemStack existing = container.getItem(slot);
|
||||
if (existing.isEmpty()
|
||||
|| !ItemStack.isSameItemSameComponents(existing, stack)
|
||||
|| !existing.isStackable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int maxStackSize = Math.min(existing.getMaxStackSize(), container.getMaxStackSize());
|
||||
remaining -= Math.max(0, maxStackSize - existing.getCount());
|
||||
}
|
||||
|
||||
for (int slot = 0; slot < container.getContainerSize() && remaining > 0; slot++) {
|
||||
ItemStack existing = container.getItem(slot);
|
||||
if (!existing.isEmpty() || !container.canPlaceItem(slot, stack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
remaining -= Math.min(stack.getMaxStackSize(), container.getMaxStackSize());
|
||||
}
|
||||
|
||||
return remaining < stack.getCount();
|
||||
}
|
||||
|
||||
private NonNullList<ItemStack> copyItems() {
|
||||
NonNullList<ItemStack> items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
|
||||
|
||||
@@ -55,4 +101,47 @@ public class BagContainer extends SimpleContainer {
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private static boolean mergeIntoExistingStacks(net.minecraft.world.Container container, ItemStack stack) {
|
||||
boolean changed = false;
|
||||
|
||||
for (int slot = 0; slot < container.getContainerSize() && !stack.isEmpty(); slot++) {
|
||||
ItemStack existing = container.getItem(slot);
|
||||
if (existing.isEmpty()
|
||||
|| !ItemStack.isSameItemSameComponents(existing, stack)
|
||||
|| !existing.isStackable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int transferable = Math.min(stack.getCount(), existing.getMaxStackSize() - existing.getCount());
|
||||
if (transferable <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
existing.grow(transferable);
|
||||
stack.shrink(transferable);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static boolean fillEmptySlots(net.minecraft.world.Container container, ItemStack stack) {
|
||||
boolean changed = false;
|
||||
|
||||
for (int slot = 0; slot < container.getContainerSize() && !stack.isEmpty(); slot++) {
|
||||
ItemStack existing = container.getItem(slot);
|
||||
if (!existing.isEmpty() || !container.canPlaceItem(slot, stack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int transferable = Math.min(stack.getCount(), stack.getMaxStackSize());
|
||||
ItemStack inserted = stack.copyWithCount(transferable);
|
||||
container.setItem(slot, inserted);
|
||||
stack.shrink(transferable);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,15 @@ import com.trunksbomb.bagtabs.bag.BagAccess;
|
||||
import com.trunksbomb.bagtabs.bag.BagCompat;
|
||||
import com.trunksbomb.bagtabs.bag.BagEntry;
|
||||
import com.trunksbomb.bagtabs.item.BagItem;
|
||||
import com.trunksbomb.bagtabs.network.InsertIntoBagPayload;
|
||||
import com.trunksbomb.bagtabs.network.QueryInsertTargetsPayload;
|
||||
import com.trunksbomb.bagtabs.menu.BagMenu;
|
||||
import com.trunksbomb.bagtabs.network.OpenBagPayload;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
@@ -17,6 +21,7 @@ import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.neoforged.neoforge.client.event.ScreenEvent;
|
||||
@@ -30,6 +35,11 @@ public final class BagTabOverlay {
|
||||
private static final int TAB_GAP = 0;
|
||||
private static final int TAB_Y_OFFSET = -3;
|
||||
private static final int TAB_X_OFFSET = -6;
|
||||
private static final Set<Integer> INSERTABLE_SLOTS = new HashSet<>();
|
||||
private static ItemStack lastCarriedStack = ItemStack.EMPTY;
|
||||
private static int lastMenuContainerId = Integer.MIN_VALUE;
|
||||
private static int lastInsertRequestId = 0;
|
||||
private static int pendingInsertRequestId = -1;
|
||||
|
||||
private BagTabOverlay() {
|
||||
}
|
||||
@@ -52,9 +62,12 @@ public final class BagTabOverlay {
|
||||
GuiGraphics guiGraphics = event.getGuiGraphics();
|
||||
int mouseX = event.getMouseX();
|
||||
int mouseY = event.getMouseY();
|
||||
ItemStack carriedStack = screen.getMenu().getCarried();
|
||||
|
||||
refreshInsertTargets(screen, carriedStack);
|
||||
|
||||
for (RenderedTab tab : tabs) {
|
||||
renderTab(guiGraphics, tab, mouseX, mouseY);
|
||||
renderTab(guiGraphics, tab, mouseX, mouseY, carriedStack);
|
||||
guiGraphics.renderItem(tab.entry().stack(), tab.x() + 3, tab.y() + 3);
|
||||
}
|
||||
|
||||
@@ -76,6 +89,10 @@ public final class BagTabOverlay {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!screen.getMenu().getCarried().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (RenderedTab tab : getRenderedTabs(screen, player)) {
|
||||
if (tab.isHovered(event.getMouseX(), event.getMouseY())) {
|
||||
PacketDistributor.sendToServer(new OpenBagPayload(tab.entry().slot()));
|
||||
@@ -86,6 +103,48 @@ public final class BagTabOverlay {
|
||||
}
|
||||
}
|
||||
|
||||
public static void mouseReleased(ScreenEvent.MouseButtonReleased.Pre event) {
|
||||
if (!(event.getScreen() instanceof AbstractContainerScreen<?> screen) || !supportsTabs(screen) || event.getButton() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = Minecraft.getInstance().player;
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack carriedStack = screen.getMenu().getCarried();
|
||||
if (carriedStack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (RenderedTab tab : getRenderedTabs(screen, player)) {
|
||||
if (!tab.isHovered(event.getMouseX(), event.getMouseY())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!INSERTABLE_SLOTS.contains(tab.entry().slot())) {
|
||||
event.setCanceled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
PacketDistributor.sendToServer(new InsertIntoBagPayload(tab.entry().slot()));
|
||||
Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.ITEM_PICKUP, 0.8F));
|
||||
event.setCanceled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateInsertTargets(int requestId, List<Integer> insertableSlots) {
|
||||
if (requestId != pendingInsertRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
INSERTABLE_SLOTS.clear();
|
||||
INSERTABLE_SLOTS.addAll(insertableSlots);
|
||||
pendingInsertRequestId = -1;
|
||||
}
|
||||
|
||||
private static List<RenderedTab> getRenderedTabs(AbstractContainerScreen<?> screen, Player player) {
|
||||
List<BagEntry> bags = BagAccess.findBags(player);
|
||||
List<RenderedTab> renderedTabs = new ArrayList<>();
|
||||
@@ -129,7 +188,7 @@ public final class BagTabOverlay {
|
||||
return slots.isEmpty() ? screen.getMenu().slots : slots;
|
||||
}
|
||||
|
||||
private static void renderTab(GuiGraphics guiGraphics, RenderedTab tab, int mouseX, int mouseY) {
|
||||
private static void renderTab(GuiGraphics guiGraphics, RenderedTab tab, int mouseX, int mouseY, ItemStack carriedStack) {
|
||||
boolean hovered = tab.isHovered(mouseX, mouseY);
|
||||
boolean selected = tab.selected();
|
||||
int color = DyedItemColor.getOrDefault(tab.entry().stack(), BagItem.DEFAULT_COLOR);
|
||||
@@ -146,6 +205,14 @@ public final class BagTabOverlay {
|
||||
if (selected) {
|
||||
guiGraphics.fill(tab.x() + 2, tab.y() + 2, tab.x() + TAB_WIDTH - 2, tab.y() + 4, 0x90FFFFFF);
|
||||
}
|
||||
|
||||
if (!carriedStack.isEmpty()) {
|
||||
if (INSERTABLE_SLOTS.contains(tab.entry().slot())) {
|
||||
renderPlusIndicator(guiGraphics, tab.x() + 15, tab.y() + 3);
|
||||
} else {
|
||||
renderXIndicator(guiGraphics, tab.x() + 15, tab.y() + 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean supportsTabs(AbstractContainerScreen<?> screen) {
|
||||
@@ -156,6 +223,47 @@ public final class BagTabOverlay {
|
||||
return BagCompat.getActiveBagSlot(screen.getMenu());
|
||||
}
|
||||
|
||||
private static void refreshInsertTargets(AbstractContainerScreen<?> screen, ItemStack carriedStack) {
|
||||
if (carriedStack.isEmpty()) {
|
||||
INSERTABLE_SLOTS.clear();
|
||||
lastCarriedStack = ItemStack.EMPTY;
|
||||
lastMenuContainerId = screen.getMenu().containerId;
|
||||
pendingInsertRequestId = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
boolean menuChanged = screen.getMenu().containerId != lastMenuContainerId;
|
||||
boolean carriedChanged = !ItemStack.matches(lastCarriedStack, carriedStack);
|
||||
if (!menuChanged && !carriedChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastCarriedStack = carriedStack.copy();
|
||||
lastMenuContainerId = screen.getMenu().containerId;
|
||||
INSERTABLE_SLOTS.clear();
|
||||
pendingInsertRequestId = ++lastInsertRequestId;
|
||||
PacketDistributor.sendToServer(new QueryInsertTargetsPayload(pendingInsertRequestId));
|
||||
}
|
||||
|
||||
private static void renderPlusIndicator(GuiGraphics guiGraphics, int x, int y) {
|
||||
guiGraphics.fill(x + 1, y, x + 4, y + 5, 0xFF000000);
|
||||
guiGraphics.fill(x, y + 1, x + 5, y + 4, 0xFF000000);
|
||||
guiGraphics.fill(x + 2, y + 1, x + 3, y + 4, 0xFF6CFF6C);
|
||||
guiGraphics.fill(x + 1, y + 2, x + 4, y + 3, 0xFF6CFF6C);
|
||||
}
|
||||
|
||||
private static void renderXIndicator(GuiGraphics guiGraphics, int x, int y) {
|
||||
guiGraphics.fill(x, y, x + 1, y + 1, 0xFFFF6C6C);
|
||||
guiGraphics.fill(x + 4, y, x + 5, y + 1, 0xFFFF6C6C);
|
||||
guiGraphics.fill(x + 1, y + 1, x + 2, y + 2, 0xFFFF6C6C);
|
||||
guiGraphics.fill(x + 3, y + 1, x + 4, y + 2, 0xFFFF6C6C);
|
||||
guiGraphics.fill(x + 2, y + 2, x + 3, y + 3, 0xFFFF6C6C);
|
||||
guiGraphics.fill(x + 1, y + 3, x + 2, y + 4, 0xFFFF6C6C);
|
||||
guiGraphics.fill(x + 3, y + 3, x + 4, y + 4, 0xFFFF6C6C);
|
||||
guiGraphics.fill(x, y + 4, x + 1, y + 5, 0xFFFF6C6C);
|
||||
guiGraphics.fill(x + 4, y + 4, x + 5, y + 5, 0xFFFF6C6C);
|
||||
}
|
||||
|
||||
private record RenderedTab(BagEntry entry, int x, int y, boolean selected) {
|
||||
private boolean isHovered(double mouseX, double mouseY) {
|
||||
return mouseX >= this.x && mouseX < this.x + TAB_WIDTH && mouseY >= this.y && mouseY < this.y + TAB_HEIGHT;
|
||||
|
||||
@@ -61,6 +61,14 @@ public class BagMenu extends AbstractContainerMenu {
|
||||
return this.bagSlot;
|
||||
}
|
||||
|
||||
public boolean insertCarriedStack(ItemStack stack) {
|
||||
return BagContainer.insertInto(this.container, stack);
|
||||
}
|
||||
|
||||
public boolean canInsertCarriedStack(ItemStack stack) {
|
||||
return BagContainer.canInsertInto(this.container, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return this.container.stillValid(player);
|
||||
|
||||
@@ -8,5 +8,8 @@ public final class BagTabsNetwork {
|
||||
|
||||
public static void register(PayloadRegistrar registrar) {
|
||||
registrar.playToServer(OpenBagPayload.TYPE, OpenBagPayload.STREAM_CODEC, OpenBagPayload::handle);
|
||||
registrar.playToServer(InsertIntoBagPayload.TYPE, InsertIntoBagPayload.STREAM_CODEC, InsertIntoBagPayload::handle);
|
||||
registrar.playToServer(QueryInsertTargetsPayload.TYPE, QueryInsertTargetsPayload.STREAM_CODEC, QueryInsertTargetsPayload::handle);
|
||||
registrar.playToClient(InsertTargetsPayload.TYPE, InsertTargetsPayload.STREAM_CODEC, InsertTargetsPayload::handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.trunksbomb.bagtabs.network;
|
||||
|
||||
import com.trunksbomb.bagtabs.BagTabs;
|
||||
import com.trunksbomb.bagtabs.bag.BagCompat;
|
||||
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.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||
|
||||
public record InsertIntoBagPayload(int slot) implements CustomPacketPayload {
|
||||
public static final Type<InsertIntoBagPayload> TYPE = new Type<>(BagTabs.id("insert_into_bag"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, InsertIntoBagPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.VAR_INT,
|
||||
InsertIntoBagPayload::slot,
|
||||
InsertIntoBagPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<InsertIntoBagPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public static void handle(InsertIntoBagPayload payload, IPayloadContext context) {
|
||||
if (!(context.player() instanceof ServerPlayer serverPlayer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload.slot() < 0 || payload.slot() >= serverPlayer.getInventory().getContainerSize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack bagStack = serverPlayer.getInventory().getItem(payload.slot());
|
||||
ItemStack carriedStack = serverPlayer.containerMenu.getCarried();
|
||||
if (carriedStack.isEmpty() || bagStack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (carriedStack == bagStack) {
|
||||
return;
|
||||
}
|
||||
|
||||
BagCompat.BagHandler handler = BagCompat.findHandler(bagStack);
|
||||
if (handler != null && handler.insertFromCarried(serverPlayer, payload.slot(), bagStack, carriedStack)) {
|
||||
serverPlayer.containerMenu.broadcastChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.trunksbomb.bagtabs.network;
|
||||
|
||||
import com.trunksbomb.bagtabs.BagTabs;
|
||||
import com.trunksbomb.bagtabs.client.BagTabOverlay;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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.neoforged.neoforge.network.handling.IPayloadContext;
|
||||
|
||||
public record InsertTargetsPayload(int requestId, List<Integer> insertableSlots) implements CustomPacketPayload {
|
||||
public static final Type<InsertTargetsPayload> TYPE = new Type<>(BagTabs.id("insert_targets"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, InsertTargetsPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.VAR_INT,
|
||||
InsertTargetsPayload::requestId,
|
||||
ByteBufCodecs.collection(ArrayList::new, ByteBufCodecs.VAR_INT),
|
||||
InsertTargetsPayload::insertableSlots,
|
||||
InsertTargetsPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<InsertTargetsPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public static void handle(InsertTargetsPayload payload, IPayloadContext context) {
|
||||
BagTabOverlay.updateInsertTargets(payload.requestId(), payload.insertableSlots());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.trunksbomb.bagtabs.network;
|
||||
|
||||
import com.trunksbomb.bagtabs.BagTabs;
|
||||
import com.trunksbomb.bagtabs.bag.BagAccess;
|
||||
import com.trunksbomb.bagtabs.bag.BagEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.neoforge.network.PacketDistributor;
|
||||
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||
|
||||
public record QueryInsertTargetsPayload(int requestId) implements CustomPacketPayload {
|
||||
public static final Type<QueryInsertTargetsPayload> TYPE = new Type<>(BagTabs.id("query_insert_targets"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, QueryInsertTargetsPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.VAR_INT,
|
||||
QueryInsertTargetsPayload::requestId,
|
||||
QueryInsertTargetsPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<QueryInsertTargetsPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public static void handle(QueryInsertTargetsPayload payload, IPayloadContext context) {
|
||||
if (!(context.player() instanceof ServerPlayer serverPlayer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack carriedStack = serverPlayer.containerMenu.getCarried();
|
||||
List<Integer> insertableSlots = new ArrayList<>();
|
||||
|
||||
if (!carriedStack.isEmpty()) {
|
||||
for (BagEntry bag : BagAccess.findBags(serverPlayer)) {
|
||||
ItemStack bagStack = serverPlayer.getInventory().getItem(bag.slot());
|
||||
if (bag.handler().canInsertFromCarried(serverPlayer, bag.slot(), bagStack, carriedStack)) {
|
||||
insertableSlots.add(bag.slot());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PacketDistributor.sendToPlayer(serverPlayer, new InsertTargetsPayload(payload.requestId(), insertableSlots));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user