diff --git a/src/main/java/mekanism/client/gui/GuiMekanism.java b/src/main/java/mekanism/client/gui/GuiMekanism.java index bd6f87f7c3c..379c37ba8a2 100644 --- a/src/main/java/mekanism/client/gui/GuiMekanism.java +++ b/src/main/java/mekanism/client/gui/GuiMekanism.java @@ -532,6 +532,11 @@ public ItemRenderer getItemRenderer() { return itemRenderer; } + @Override + public boolean currentlyQuickCrafting() { + return isQuickCrafting && !quickCraftSlots.isEmpty(); + } + @Override public void addWindow(GuiWindow window) { GuiWindow top = windows.isEmpty() ? null : windows.iterator().next(); diff --git a/src/main/java/mekanism/client/gui/IGuiWrapper.java b/src/main/java/mekanism/client/gui/IGuiWrapper.java index f9324d4657a..6abead994f5 100644 --- a/src/main/java/mekanism/client/gui/IGuiWrapper.java +++ b/src/main/java/mekanism/client/gui/IGuiWrapper.java @@ -72,6 +72,10 @@ default void removeWindow(GuiWindow window) { Mekanism.logger.error("Tried to call 'removeWindow' but unsupported in {}", getClass().getName()); } + default boolean currentlyQuickCrafting() { + return false; + } + @Nullable default GuiWindow getWindowHovering(double mouseX, double mouseY) { Mekanism.logger.error("Tried to call 'getWindowHovering' but unsupported in {}", getClass().getName()); diff --git a/src/main/java/mekanism/client/gui/element/scroll/GuiSlotScroll.java b/src/main/java/mekanism/client/gui/element/scroll/GuiSlotScroll.java index e409bcc1932..1d69afe6724 100644 --- a/src/main/java/mekanism/client/gui/element/scroll/GuiSlotScroll.java +++ b/src/main/java/mekanism/client/gui/element/scroll/GuiSlotScroll.java @@ -1,6 +1,8 @@ package mekanism.client.gui.element.scroll; import com.mojang.blaze3d.matrix.MatrixStack; +import java.math.RoundingMode; +import java.text.DecimalFormat; import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -24,6 +26,11 @@ public class GuiSlotScroll extends GuiRelativeElement { private static final ResourceLocation SLOTS = MekanismUtils.getResource(ResourceType.GUI_SLOT, "slots.png"); private static final ResourceLocation SLOTS_DARK = MekanismUtils.getResource(ResourceType.GUI_SLOT, "slots_dark.png"); + private static final DecimalFormat COUNT_FORMAT = new DecimalFormat("#.#"); + + static { + COUNT_FORMAT.setRoundingMode(RoundingMode.FLOOR); + } private final GuiScrollBar scrollBar; @@ -91,6 +98,10 @@ public boolean mouseScrolled(double mouseX, double mouseY, double delta) { @Override public boolean mouseReleased(double mouseX, double mouseY, int button) { + if (gui().currentlyQuickCrafting()) { + //If the player is currently quick crafting don't do any special handling for as if they clicked in the screen + return super.mouseReleased(mouseX, mouseY, button); + } super.mouseReleased(mouseX, mouseY, button); IScrollableSlot slot = getSlot(mouseX, mouseY, x, y); clickHandler.onClick(slot, button, Screen.hasShiftDown(), minecraft.player.inventory.getCarried()); @@ -167,16 +178,18 @@ private void renderSlotText(MatrixStack matrix, String text, int x, int y) { } private String getCountText(long count) { + //Note: For cases like 9,999,999 we intentionally display as 9999.9K instead of 10M so that people + // do not think they have more stored than they actually have just because it is rounding up if (count <= 1) { return null; } else if (count < 10_000) { return Long.toString(count); } else if (count < 10_000_000) { - return Double.toString(Math.round(count / 1_000D)) + "K"; + return COUNT_FORMAT.format(count / 1_000D) + "K"; } else if (count < 10_000_000_000L) { - return Double.toString(Math.round(count / 1_000_000D)) + "M"; + return COUNT_FORMAT.format(count / 1_000_000D) + "M"; } else if (count < 10_000_000_000_000L) { - return Double.toString(Math.round(count / 1_000_000_000D)) + "B"; + return COUNT_FORMAT.format(count / 1_000_000_000D) + "B"; } return ">10T"; } diff --git a/src/main/java/mekanism/common/content/qio/QIOCraftingWindow.java b/src/main/java/mekanism/common/content/qio/QIOCraftingWindow.java index 04f0aaa2f8a..b0f3e66827b 100644 --- a/src/main/java/mekanism/common/content/qio/QIOCraftingWindow.java +++ b/src/main/java/mekanism/common/content/qio/QIOCraftingWindow.java @@ -528,8 +528,9 @@ private boolean updateRemainderHelperInputs(boolean updated, @Nonnull ItemStack private class QIOCraftingInventory extends CraftingInventory { - //TODO - 10.1: Suppress warning and also add a note that we override all the places where it being - // null would cause issues, and we handle slots individually as well + //Note: We suppress the warning about this passing null as the container as we override all methods that + // that make use of the container to use our handler instead + @SuppressWarnings("ConstantConditions") public QIOCraftingInventory() { super(null, 0, 0); } diff --git a/src/main/java/mekanism/common/content/qio/QIOFrequency.java b/src/main/java/mekanism/common/content/qio/QIOFrequency.java index c5cda5c7dda..8536131eeed 100644 --- a/src/main/java/mekanism/common/content/qio/QIOFrequency.java +++ b/src/main/java/mekanism/common/content/qio/QIOFrequency.java @@ -54,8 +54,13 @@ public class QIOFrequency extends Frequency { private final Map> modIDLookupMap = new HashMap<>(); // efficiently keep track of the items for use in fuzzy lookup utilized by the items stored private final Map> fuzzyItemLookupMap = new HashMap<>(); - //Keep track of a UUID for each hashed item + // keep track of a UUID for each hashed item private final BiMap itemTypeLookup = HashBiMap.create(); + // allows for lazily removing the UUIDs assigned to items in the itemTypeLookup BiMap without having any issues + // come up related to updatedItems being a set and UUIDAwareHashedItems server side intentionally not comparing + // the UUIDs, so then if multiple add/remove calls happened at once the items in need of updating potentially + // would sync using a different UUID to the client, causing the client to not know the old stack needed to be removed + private final Set uuidsToInvalidate = new HashSet<>(); // a sensitive cache for wildcard tag lookups (wildcard -> [matching tags]) private final SetMultimap tagWildcardCache = HashMultimap.create(); private final Set failedWildcardTags = new HashSet<>(); @@ -141,7 +146,15 @@ private QIOItemTypeData createTypeDataForAbsent(HashedItem type) { }).add(type); //Fuzzy item lookup has no wildcard cache related to it fuzzyItemLookupMap.computeIfAbsent(stack.getItem(), item -> new HashSet<>()).add(type); - itemTypeLookup.put(type, UUID.randomUUID()); + UUID oldUUID = getUUIDForType(type); + if (oldUUID != null) { + //If there was a UUID stored and prepped to be invalidated, remove it from the UUIDS we are trying to invalidate + // so that it is able to continue being used/sync'd to the client + uuidsToInvalidate.remove(oldUUID); + } else { + // otherwise, create a new uuid for use with this item type + itemTypeLookup.put(type, UUID.randomUUID()); + } return new QIOItemTypeData(type); } @@ -154,7 +167,7 @@ public ItemStack removeItem(ItemStack stack, int amount) { } public ItemStack removeByType(@Nullable HashedItem itemType, int amount) { - if (itemDataMap.isEmpty()) { + if (itemDataMap.isEmpty() || amount <= 0) { return ItemStack.EMPTY; } @@ -180,7 +193,11 @@ public ItemStack removeByType(@Nullable HashedItem itemType, int amount) { private void removeItemData(HashedItem type) { itemDataMap.remove(type); - itemTypeLookup.remove(type); + //If the item has a UUID that corresponds to it, add that UUID to our list of uuids to invalidate + UUID toInvalidate = getUUIDForType(type); + if (toInvalidate != null) { + uuidsToInvalidate.add(toInvalidate); + } //Note: We need to copy the tags to a new collection as otherwise when we start removing them from the lookup // they will also get removed from this view Set tags = new HashSet<>(tagLookupMap.getKeys(type)); @@ -346,6 +363,13 @@ public QIODriveData getDriveData(QIODriveKey key) { @Override public void tick() { super.tick(); + if (!uuidsToInvalidate.isEmpty()) { + //If we have uuids we need to invalidate the Item UUID pairing of them + for (UUID uuidToInvalidate : uuidsToInvalidate) { + itemTypeLookup.inverse().remove(uuidToInvalidate); + } + uuidsToInvalidate.clear(); + } if (!updatedItems.isEmpty() || needsUpdate) { Object2LongMap map = new Object2LongOpenHashMap<>(); updatedItems.forEach(type -> { diff --git a/src/main/java/mekanism/common/inventory/container/slot/VirtualCraftingOutputSlot.java b/src/main/java/mekanism/common/inventory/container/slot/VirtualCraftingOutputSlot.java index 0ea9a0b858e..879b8c8135a 100644 --- a/src/main/java/mekanism/common/inventory/container/slot/VirtualCraftingOutputSlot.java +++ b/src/main/java/mekanism/common/inventory/container/slot/VirtualCraftingOutputSlot.java @@ -90,7 +90,10 @@ public ItemStack onTake(@Nonnull PlayerEntity player, @Nonnull ItemStack stack) @Override public boolean mayPickup(@Nonnull PlayerEntity player) { - return canCraft && super.mayPickup(player); + if (player.level.isClientSide || !(player instanceof ServerPlayerEntity)) { + return canCraft && super.mayPickup(player); + } + return craftingWindow.canViewRecipe((ServerPlayerEntity) player) && super.mayPickup(player); } @Nonnull diff --git a/src/main/java/mekanism/common/inventory/container/tile/MekanismTileContainer.java b/src/main/java/mekanism/common/inventory/container/tile/MekanismTileContainer.java index 822d7e839c5..27e91e8fa20 100644 --- a/src/main/java/mekanism/common/inventory/container/tile/MekanismTileContainer.java +++ b/src/main/java/mekanism/common/inventory/container/tile/MekanismTileContainer.java @@ -75,7 +75,6 @@ protected void addSlots() { } if (tile.supportsUpgrades()) { //Add the virtual slot for the upgrade (add them before the main inventory to make sure they take priority in targeting) - //TODO - 10.1: Test this and test how it handles addSlot(upgradeSlot = tile.getComponent().getUpgradeSlot().createContainerSlot()); addSlot(upgradeOutputSlot = tile.getComponent().getUpgradeOutputSlot().createContainerSlot()); } diff --git a/src/main/java/mekanism/common/network/to_server/PacketQIOFillCraftingWindow.java b/src/main/java/mekanism/common/network/to_server/PacketQIOFillCraftingWindow.java index c8c51efb97c..fd78f5facef 100644 --- a/src/main/java/mekanism/common/network/to_server/PacketQIOFillCraftingWindow.java +++ b/src/main/java/mekanism/common/network/to_server/PacketQIOFillCraftingWindow.java @@ -275,7 +275,7 @@ private void transferItems(@Nullable QIOFrequency frequency, QIOCraftingWindow c targetContents.put(entry.getByteKey(), stack); } } - //Extract what items are still + //Extract what items are still in the window Byte2ObjectMap remainingCraftingGridContents = new Byte2ObjectArrayMap<>(9); for (byte i = 0; i < 9; i++) { CraftingWindowInventorySlot inputSlot = craftingWindow.getInputSlot(i);