Skip to content

Commit

Permalink
Rework parts of how the QIO Dashboard's container handles data:
Browse files Browse the repository at this point in the history
- Syncs contents as part of the initial open packet instead of having the client request all the data when it opens (or the window is resized, or the entire minecraft window is resized)
- Remove a redundant update search call from transferring data when resizing a QIO item viewer
- Override equals and hashcode for ISearchQuery
- Check the player who has the container open when getting the tooltips of items for search purposes
- Copy item lists and search lists when resizing the gui to avoid having to recalculate them
- Fix not updating the order of items when changing the sort direction or type if there was any search text
- Avoid resorting the items if the sort type doesn't care about count for any level of the sort, and we only changed stored counts of items
- Update the item list on the fly instead of recreating it from scratch every change
- Avoid recalculating the search list if the query didn't change
- Removed one layer of an unused cache from the cached search data
  • Loading branch information
pupnewfster committed Nov 20, 2024
1 parent 9848022 commit e7febd3
Show file tree
Hide file tree
Showing 30 changed files with 677 additions and 381 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mekanism.generators.common.network.to_server;

import io.netty.buffer.ByteBuf;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import mekanism.common.network.IMekanismPacket;
Expand All @@ -13,13 +14,15 @@
import mekanism.generators.common.tile.fusion.TileEntityFusionReactorController;
import mekanism.generators.common.tile.turbine.TileEntityTurbineCasing;
import net.minecraft.core.BlockPos;
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.util.ByIdMap;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Player;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand All @@ -46,8 +49,12 @@ public void handle(IPayloadContext context) {
Player player = context.player();
//If we are on the server (the only time we should be receiving this packet), let forge handle switching the Gui
TileEntityMekanism tile = WorldUtils.getTileEntity(TileEntityMekanism.class, player.level(), pos);
if (tile != null) {
player.openMenu(buttonClicked.getProvider(tile), pos);
MenuProvider provider = buttonClicked.getProvider(tile);
if (provider != null) {
player.openMenu(provider, buf -> {
buf.writeBlockPos(pos);
buttonClicked.encodeExtraData(buf, tile);
});
}
}

Expand All @@ -59,6 +66,11 @@ public enum ClickedGeneratorsTileButton {
return GeneratorsContainerTypes.FISSION_REACTOR.getProvider(GeneratorsLang.FISSION_REACTOR, tile);
}
return null;
}, (buffer, tile) -> {
//Mirror the logic from TileEntityMekanism#openGui for what data we write so that we properly reinitialize the initial GUI
//TODO: Is this correct? I believe it is, and it doesn't hurt anything currently as it effectively is a NO-OP for both these cases
// but there is a chance this isn't exactly correct
tile.encodeExtraContainerData(buffer);
}),
TAB_HEAT(tile -> GeneratorsContainerTypes.FUSION_REACTOR_HEAT.getProvider(GeneratorsLang.FUSION_REACTOR, tile)),
TAB_FUEL(tile -> GeneratorsContainerTypes.FUSION_REACTOR_FUEL.getProvider(GeneratorsLang.FUSION_REACTOR, tile)),
Expand All @@ -77,14 +89,29 @@ public enum ClickedGeneratorsTileButton {
public static final StreamCodec<ByteBuf, ClickedGeneratorsTileButton> STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, ClickedGeneratorsTileButton::ordinal);

private final Function<TileEntityMekanism, @Nullable MenuProvider> providerFromTile;
@Nullable
private final BiConsumer<RegistryFriendlyByteBuf, TileEntityMekanism> extraEncodingData;

ClickedGeneratorsTileButton(Function<TileEntityMekanism, @Nullable MenuProvider> providerFromTile) {
this(providerFromTile, null);
}

ClickedGeneratorsTileButton(Function<TileEntityMekanism, @Nullable MenuProvider> providerFromTile,
@Nullable BiConsumer<RegistryFriendlyByteBuf, TileEntityMekanism> extraEncodingData) {
this.providerFromTile = providerFromTile;
this.extraEncodingData = extraEncodingData;
}

@Nullable
public MenuProvider getProvider(TileEntityMekanism tile) {
return providerFromTile.apply(tile);
@Contract("null -> null")
public MenuProvider getProvider(@Nullable TileEntityMekanism tile) {
return tile == null ? null : providerFromTile.apply(tile);
}

private void encodeExtraData(RegistryFriendlyByteBuf buffer, TileEntityMekanism tile) {
if (extraEncodingData != null) {
extraEncodingData.accept(buffer, tile);
}
}
}
}
36 changes: 14 additions & 22 deletions src/main/java/mekanism/client/gui/element/scroll/GuiSlotScroll.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import mekanism.common.annotations.GLFWMouseButtons;
import mekanism.common.inventory.ISlotClickHandler;
import mekanism.common.inventory.ISlotClickHandler.IScrollableSlot;
import mekanism.common.lib.inventory.HashedItem;
import mekanism.common.util.MekanismUtils;
import mekanism.common.util.MekanismUtils.ResourceType;
import mekanism.common.util.UnitDisplayUtils;
Expand All @@ -29,7 +28,6 @@
import net.minecraft.util.Mth;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class GuiSlotScroll extends GuiElement implements IRecipeViewerIngredientHelper {

Expand All @@ -39,26 +37,25 @@ public class GuiSlotScroll extends GuiElement implements IRecipeViewerIngredient
private final GuiScrollBar scrollBar;

private final int xSlots, ySlots;
private final Supplier<@Nullable List<IScrollableSlot>> slotList;
private final Supplier<@NotNull List<IScrollableSlot>> slotList;
private final ISlotClickHandler clickHandler;

public GuiSlotScroll(IGuiWrapper gui, int x, int y, int xSlots, int ySlots, Supplier<@Nullable List<IScrollableSlot>> slotList, ISlotClickHandler clickHandler) {
public GuiSlotScroll(IGuiWrapper gui, int x, int y, int xSlots, int ySlots, Supplier<@NotNull List<IScrollableSlot>> slotList, ISlotClickHandler clickHandler) {
super(gui, x, y, xSlots * 18 + 18, ySlots * 18);
this.xSlots = xSlots;
this.ySlots = ySlots;
this.slotList = slotList;
this.clickHandler = clickHandler;
scrollBar = addChild(new GuiScrollBar(gui, relativeX + xSlots * 18 + 4, y, ySlots * 18,
() -> getSlotList() == null ? 0 : Mth.ceil((double) getSlotList().size() / xSlots), () -> ySlots));
scrollBar = addChild(new GuiScrollBar(gui, relativeX + xSlots * 18 + 4, y, ySlots * 18, () -> Mth.ceil((double) getSlotList().size() / this.xSlots),
() -> this.ySlots));
}

@Override
public void drawBackground(@NotNull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
super.drawBackground(guiGraphics, mouseX, mouseY, partialTicks);
List<IScrollableSlot> list = getSlotList();
ResourceLocation resource = list == null ? SLOTS_DARK : SLOTS;
guiGraphics.blit(resource, relativeX, relativeY, 0, 0, xSlots * 18, ySlots * 18, 288, 288);
if (list != null) {
guiGraphics.blit(list.isEmpty() ? SLOTS_DARK : SLOTS, relativeX, relativeY, 0, 0, xSlots * 18, ySlots * 18, 288, 288);
if (!list.isEmpty()) {
int slotStart = scrollBar.getCurrentSelection() * xSlots, max = xSlots * ySlots;
for (int i = 0; i < max; i++) {
int slot = slotStart + i;
Expand Down Expand Up @@ -111,7 +108,7 @@ public boolean mouseReleased(double mouseX, double mouseY, @GLFWMouseButtons int

private IScrollableSlot getSlot(double mouseX, double mouseY) {
List<IScrollableSlot> list = getSlotList();
if (list == null) {
if (list.isEmpty()) {
return null;
}
int slotX = (int) ((mouseX - getX()) / 18), slotY = (int) ((mouseY - getY()) / 18);
Expand All @@ -137,7 +134,7 @@ private void renderSlot(GuiGraphics guiGraphics, IScrollableSlot slot, int slotX
if (isSlotEmpty(slot)) {
return;
}
gui().renderItemWithOverlay(guiGraphics, slot.item().getInternalStack(), relativeX + slotX + 1, relativeY + slotY + 1, 1, "");
gui().renderItemWithOverlay(guiGraphics, slot.getInternalStack(), relativeX + slotX + 1, relativeY + slotY + 1, 1, "");
long count = slot.count();
if (count > 1) {
Component text;
Expand All @@ -157,7 +154,7 @@ private void renderSlotTooltip(GuiGraphics guiGraphics, IScrollableSlot slot, in
if (isSlotEmpty(slot)) {
return;
}
ItemStack stack = slot.item().getInternalStack();
ItemStack stack = slot.getInternalStack();
long count = slot.count();
if (count < 10_000) {
guiGraphics.renderTooltip(font(), stack, slotX, slotY);
Expand All @@ -169,14 +166,8 @@ private void renderSlotTooltip(GuiGraphics guiGraphics, IScrollableSlot slot, in
}

private boolean isSlotEmpty(IScrollableSlot slot) {
if (slot.count() == 0) {
//Count is not expected to be zero, but validate it anyway
return true;
}
//Slot's item is not null in default impl, but check in case we make it null at some point
// and also validate if the internal stack is empty in case it is raw and there is some edge case
HashedItem item = slot.item();
return item == null || item.getInternalStack().isEmpty();
//Count is not expected to be zero, but validate it anyway
return slot.count() == 0 || slot.getInternalStack().isEmpty();
}

private void renderSlotText(GuiGraphics guiGraphics, Component text, int x, int y) {
Expand All @@ -194,20 +185,21 @@ private void renderSlotText(GuiGraphics guiGraphics, Component text, int x, int
pose.popPose();
}

@NotNull
private List<IScrollableSlot> getSlotList() {
return slotList.get();
}

@Override
public Optional<?> getIngredient(double mouseX, double mouseY) {
IScrollableSlot slot = getSlot(mouseX, mouseY);
return slot == null ? Optional.empty() : Optional.of(slot.item().getInternalStack());
return slot == null ? Optional.empty() : Optional.of(slot.getInternalStack());
}

@Override
public Rect2i getIngredientBounds(double mouseX, double mouseY) {
List<IScrollableSlot> list = getSlotList();
if (list != null) {
if (!list.isEmpty()) {
int slotX = (int) ((mouseX - getX()) / 18), slotY = (int) ((mouseY - getY()) / 18);
int slotStartX = getX() + slotX * 18 + 1, slotStartY = getY() + slotY * 18 + 1;
if (mouseX >= slotStartX && mouseX < slotStartX + 16 && mouseY >= slotStartY && mouseY < slotStartY + 16) {
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/mekanism/client/gui/qio/GuiQIOItemViewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ protected void addGuiElements() {
.setInputValidator(this::isValidSearchChar)
.setBackground(BackgroundType.ELEMENT_HOLDER)
//Note: This responder will also be called when the menu is resized/repositioned and the text gets copied
.setResponder(text -> menu.updateSearch(menu.getLevel(), text));
.setResponder(text -> menu.updateSearch(menu.getLevel(), text, true));
searchField.setMaxLength(50);
searchField.setVisible(true);
searchField.setTextColor(0xFFFFFF);
Expand Down Expand Up @@ -173,7 +173,6 @@ private void recreateViewer() {
getMinecraft().player.containerMenu = s.getMenu();
getMinecraft().setScreen(s);
s.searchField.setText(searchField.getText());
c.updateSearch(c.getLevel(), searchField.getText());
//Transfer all the windows to the new GUI
s.transferWindows(windows);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand All @@ -18,11 +19,11 @@
import mekanism.api.AutomationType;
import mekanism.api.inventory.IInventorySlot;
import mekanism.api.math.MathUtils;
import mekanism.common.inventory.ISlotClickHandler.IScrollableSlot;
import mekanism.common.inventory.container.slot.HotBarSlot;
import mekanism.common.inventory.container.slot.InsertableSlot;
import mekanism.common.inventory.container.slot.MainInventorySlot;
import mekanism.common.lib.inventory.HashedItem;
import mekanism.common.lib.inventory.HashedItem.UUIDAwareHashedItem;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
Expand All @@ -41,13 +42,12 @@ public class QIOCraftingTransferHelper {
private byte emptyInventorySlots;
private boolean isValid;

public QIOCraftingTransferHelper(Object2LongMap<UUIDAwareHashedItem> cachedInventory, List<HotBarSlot> hotBarSlots, List<MainInventorySlot> mainInventorySlots,
public QIOCraftingTransferHelper(Collection<? extends IScrollableSlot> cachedInventory, List<HotBarSlot> hotBarSlots, List<MainInventorySlot> mainInventorySlots,
QIOCraftingWindow craftingWindow, Player player) {
isValid = true;
reverseLookup = new HashMap<>();
for (Object2LongMap.Entry<UUIDAwareHashedItem> entry : cachedInventory.object2LongEntrySet()) {
UUIDAwareHashedItem source = entry.getKey();
reverseLookup.computeIfAbsent(source.asRawHashedItem(), item -> new HashedItemSource()).addQIOSlot(source.getUUID(), entry.getLongValue());
for (IScrollableSlot source : cachedInventory) {
reverseLookup.computeIfAbsent(source.asRawHashedItem(), item -> new HashedItemSource()).addQIOSlot(source.itemUUID(), source.count());
}
byte inventorySlotIndex = 0;
for (; inventorySlotIndex < 9; inventorySlotIndex++) {
Expand Down
38 changes: 27 additions & 11 deletions src/main/java/mekanism/common/content/qio/QIOFrequency.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import mekanism.common.lib.inventory.HashedItem;
import mekanism.common.lib.inventory.HashedItem.UUIDAwareHashedItem;
import mekanism.common.lib.security.SecurityFrequency;
import mekanism.common.network.to_client.qio.PacketBatchItemViewerSync;
import mekanism.common.network.to_client.qio.PacketUpdateItemViewer;
import mekanism.common.util.MekanismUtils;
import net.minecraft.SharedConstants;
Expand Down Expand Up @@ -228,9 +227,10 @@ private QIOItemTypeData createTypeDataForAbsent(HashedItem type) {
modItems.add(type);
//Fuzzy item lookup has no wildcard cache related to it
fuzzyItemLookupMap.computeIfAbsent(stack.getItem(), item -> new HashSet<>()).add(type);
QIOItemTypeData data = new QIOItemTypeData(type);
//Ensure we have a matching uuid for this item
QIOGlobalItemLookup.INSTANCE.getOrTrackUUID(type);
return new QIOItemTypeData(type);
data.getItemUUID();
return data;
}

@Override
Expand Down Expand Up @@ -411,11 +411,6 @@ private boolean buildWildcardMapping(SetMultimap<String, String> wildcardCache,

public void openItemViewer(ServerPlayer player) {
playersViewingItems.add(player);
Object2LongMap<UUIDAwareHashedItem> map = new Object2LongOpenHashMap<>(itemDataMap.size());
for (QIOItemTypeData data : itemDataMap.values()) {
map.put(new UUIDAwareHashedItem(data.itemType, QIOGlobalItemLookup.INSTANCE.getOrTrackUUID(data.itemType)), data.count);
}
PacketDistributor.sendToPlayer(player, new PacketBatchItemViewerSync(totalCountCapacity, totalTypeCapacity, map));
}

public void closeItemViewer(ServerPlayer player) {
Expand Down Expand Up @@ -658,14 +653,20 @@ private void setNeedsUpdate(@Nullable HashedItem changedItem) {
if (!playersViewingItems.isEmpty()) {//Skip marking for update if there are no players viewing the items
needsUpdate = true;
if (changedItem != null) {
updatedItems.add(QIOGlobalItemLookup.INSTANCE.getUUIDForType(changedItem));
UUID uuid = QIOGlobalItemLookup.INSTANCE.getUUIDForType(changedItem);
if (uuid != null) {
updatedItems.add(uuid);
}
}
}
}

private void markForUpdate(HashedItem changedItem) {
if (!playersViewingItems.isEmpty()) {//Skip marking for update if there are no players viewing the items
updatedItems.add(QIOGlobalItemLookup.INSTANCE.getUUIDForType(changedItem));
UUID uuid = QIOGlobalItemLookup.INSTANCE.getUUIDForType(changedItem);
if (uuid != null) {
updatedItems.add(uuid);
}
}
}

Expand All @@ -675,9 +676,12 @@ private void setNeedsUpdate() {

public class QIOItemTypeData {

private final Set<QIODriveKey> containingDrives = new HashSet<>();
private final HashedItem itemType;

@Nullable
private UUID itemUUID;
private long count = 0;
private final Set<QIODriveKey> containingDrives = new HashSet<>();

public QIOItemTypeData(HashedItem itemType) {
this.itemType = itemType;
Expand Down Expand Up @@ -757,5 +761,17 @@ private ItemStack remove(int amount) {
public long getCount() {
return count;
}

public UUID getItemUUID() {
if (itemUUID == null) {
//Lazily cache what the uuid for the stack is
itemUUID = QIOGlobalItemLookup.INSTANCE.getOrTrackUUID(itemType);
}
return itemUUID;
}

public HashedItem getItemType() {
return itemType;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -337,13 +337,15 @@ protected int getRemaining(int slot, ItemStack currentStored) {
if (frequency.isStoring(entry.getKey())) {
//It is stored, check to make sure it isn't a type we are removing fully
UUID uuid = QIOGlobalItemLookup.INSTANCE.getUUIDForType(entry.getKey());
FrequencySlotData slotData = frequencyAvailableItems.get(uuid);
if (slotData != null && slotData.getAvailable() == 0) {
// if it is, then we need to reclaim the item type as being available
availableItemTypes--;
if (availableItemTypes <= 0) {
//Not enough room for types
return false;
if (uuid != null) {
FrequencySlotData slotData = frequencyAvailableItems.get(uuid);
if (slotData != null && slotData.getAvailable() == 0) {
// if it is, then we need to reclaim the item type as being available
availableItemTypes--;
if (availableItemTypes <= 0) {
//Not enough room for types
return false;
}
}
}
} else {
Expand Down
Loading

0 comments on commit e7febd3

Please sign in to comment.