Skip to content

Commit

Permalink
Rewrite how we sync data from the QIO to the client to also include a…
Browse files Browse the repository at this point in the history
… UUID so as to not have to sync the full NBT tag if it is reduced due to shareTag being overridden or if there is capability NBT data. As a side effect of slightly larger packets from server to client for syncing, the client to server extraction packets are in general smaller. #6501 Also fixes QIO systems not properly loading any data if the mod that adds a stored item is removed, now instead the item from that mod is ignored (Note: This means adding the mod back won't make the items come back but they didn't before this fix either).
  • Loading branch information
pupnewfster committed Oct 5, 2020
1 parent c9220ea commit be85fda
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 50 deletions.
8 changes: 6 additions & 2 deletions src/main/java/mekanism/common/content/qio/IQIODriveItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ default void loadItemMap(ItemStack stack, QIODriveData data) {
for (int i = 0; i < list.size(); i++) {
CompoundNBT tag = list.getCompound(i);
ItemStack itemType = ItemStack.read(tag.getCompound(NBTConstants.ITEM));
long count = tag.getLong(NBTConstants.AMOUNT);
data.getItemMap().put(new HashedItem(itemType), count);
if (!itemType.isEmpty()) {
//Only add the item if the item could be read. If it can't that means the mod adding the item was probaly removed
//TODO: Eventually we may want to keep the NBT so that if the mod gets added back it exists again
long count = tag.getLong(NBTConstants.AMOUNT);
data.getItemMap().put(new HashedItem(itemType), count);
}
}
}
}
Expand Down
36 changes: 27 additions & 9 deletions src/main/java/mekanism/common/content/qio/QIOFrequency.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package mekanism.common.content.qio;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
Expand All @@ -25,6 +27,7 @@
import mekanism.common.lib.frequency.Frequency;
import mekanism.common.lib.frequency.FrequencyType;
import mekanism.common.lib.inventory.HashedItem;
import mekanism.common.lib.inventory.HashedItem.UUIDAwareHashedItem;
import mekanism.common.network.PacketQIOItemViewerGuiSync;
import mekanism.common.util.NBTUtils;
import net.minecraft.entity.player.ServerPlayerEntity;
Expand All @@ -43,10 +46,12 @@ public class QIOFrequency extends Frequency {
private final Set<IQIODriveHolder> driveHolders = new HashSet<>();
// efficiently keep track of the tags utilized by the items stored
private final BiMultimap<String, HashedItem> tagLookupMap = new BiMultimap<>();
//Keep track of a UUID for each hashed item
private final BiMap<HashedItem, UUID> itemTypeLookup = HashBiMap.create();
// a sensitive cache for wildcard tag lookups (wildcard -> [matching tags])
private final SetMultimap<String, String> tagWildcardCache = HashMultimap.create();

private final Set<HashedItem> updatedItems = new HashSet<>();
private final Set<UUIDAwareHashedItem> updatedItems = new HashSet<>();
private final Set<ServerPlayerEntity> playersViewingItems = new HashSet<>();

/** If we need to send a packet to viewing clients with changed item data. */
Expand Down Expand Up @@ -78,6 +83,16 @@ public Map<HashedItem, QIOItemTypeData> getItemDataMap() {
return itemDataMap;
}

@Nullable
public HashedItem getTypeByUUID(@Nullable UUID uuid) {
return uuid == null ? null : itemTypeLookup.inverse().get(uuid);
}

@Nullable
public UUID getUUIDForType(HashedItem item) {
return itemTypeLookup.get(item);
}

public ItemStack addItem(ItemStack stack) {
HashedItem type = new HashedItem(stack);
// these checks are extremely important; they prevent us from wasting CPU searching for a place to put the new items,
Expand All @@ -89,6 +104,7 @@ public ItemStack addItem(ItemStack stack) {
QIOItemTypeData data = itemDataMap.computeIfAbsent(type, t -> {
tagLookupMap.putAll(TagCache.getItemTags(stack), t);
tagWildcardCache.clear();
itemTypeLookup.put(t, UUID.randomUUID());
return new QIOItemTypeData(t);
});
return type.createStack((int) data.add(stack.getCount()));
Expand Down Expand Up @@ -124,6 +140,7 @@ public ItemStack removeByType(@Nullable HashedItem itemType, int amount) {
if (data.count == 0) {
itemDataMap.remove(data.itemType);
tagLookupMap.removeValue(data.itemType);
itemTypeLookup.remove(data.itemType);
tagWildcardCache.clear();
}
return removed;
Expand Down Expand Up @@ -156,8 +173,8 @@ private void buildWildcardMapping(String wildcard) {

public void openItemViewer(ServerPlayerEntity player) {
playersViewingItems.add(player);
Object2LongMap<HashedItem> map = new Object2LongOpenHashMap<>();
itemDataMap.values().forEach(d -> map.put(d.itemType, d.count));
Object2LongMap<UUIDAwareHashedItem> map = new Object2LongOpenHashMap<>();
itemDataMap.values().forEach(d -> map.put(new UUIDAwareHashedItem(d.itemType, getUUIDForType(d.itemType)), d.count));
Mekanism.packetHandler.sendTo(PacketQIOItemViewerGuiSync.batch(map, totalCountCapacity, totalTypeCapacity), player);
}

Expand Down Expand Up @@ -203,7 +220,7 @@ public QIODriveData getDriveData(QIODriveKey key) {
public void tick() {
super.tick();
if (!updatedItems.isEmpty() || needsUpdate) {
Object2LongMap<HashedItem> map = new Object2LongOpenHashMap<>();
Object2LongMap<UUIDAwareHashedItem> map = new Object2LongOpenHashMap<>();
updatedItems.forEach(type -> {
QIOItemTypeData data = itemDataMap.get(type);
map.put(type, data == null ? 0 : data.count);
Expand Down Expand Up @@ -322,10 +339,11 @@ public void addDrive(QIODriveKey key) {
data.getItemMap().forEach((storedKey, value) -> {
itemDataMap.computeIfAbsent(storedKey, e -> {
tagWildcardCache.clear();
tagLookupMap.putAll(TagCache.getItemTags(storedKey.getStack()), storedKey);
return new QIOItemTypeData(storedKey);
tagLookupMap.putAll(TagCache.getItemTags(e.getStack()), e);
itemTypeLookup.put(e, UUID.randomUUID());
return new QIOItemTypeData(e);
}).addFromDrive(data, value);
updatedItems.add(storedKey);
updatedItems.add(new UUIDAwareHashedItem(storedKey, getUUIDForType(storedKey)));
});
setNeedsUpdate();
}
Expand All @@ -348,7 +366,7 @@ public void removeDrive(QIODriveKey key, boolean updateItemMap) {
itemDataMap.remove(storedKey);
tagWildcardCache.clear();
}
updatedItems.add(storedKey);
updatedItems.add(new UUIDAwareHashedItem(storedKey, getUUIDForType(storedKey)));
}
});
setNeedsUpdate();
Expand Down Expand Up @@ -380,7 +398,7 @@ private void setNeedsUpdate(@Nullable HashedItem changedItem) {
needsUpdate = true;
isDirty = true;
if (changedItem != null) {
updatedItems.add(changedItem);
updatedItems.add(new UUIDAwareHashedItem(changedItem, getUUIDForType(changedItem)));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mekanism.common.inventory;

import java.util.UUID;
import mekanism.common.lib.inventory.HashedItem;
import net.minecraft.item.ItemStack;

Expand All @@ -11,6 +12,8 @@ interface IScrollableSlot {

HashedItem getItem();

UUID getItemUUID();

long getCount();

String getDisplayName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nonnull;
import mekanism.api.math.MathUtils;
import mekanism.api.text.ILangEntry;
Expand All @@ -21,6 +22,7 @@
import mekanism.common.inventory.ISlotClickHandler;
import mekanism.common.inventory.container.slot.InventoryContainerSlot;
import mekanism.common.lib.inventory.HashedItem;
import mekanism.common.lib.inventory.HashedItem.UUIDAwareHashedItem;
import mekanism.common.network.PacketGuiItemDataRequest;
import mekanism.common.network.PacketQIOItemViewerSlotInteract;
import mekanism.common.registration.impl.ContainerTypeRegistryObject;
Expand Down Expand Up @@ -52,7 +54,7 @@ public static int getSlotsYMax() {
private ListSortType sortType = MekanismConfig.client.qioItemViewerSortType.get();
private SortDirection sortDirection = MekanismConfig.client.qioItemViewerSortDirection.get();

private Object2LongMap<HashedItem> cachedInventory = new Object2LongOpenHashMap<>();
private Object2LongMap<UUIDAwareHashedItem> cachedInventory = new Object2LongOpenHashMap<>();
private long cachedCountCapacity;
private int cachedTypeCapacity;
private long totalItems;
Expand Down Expand Up @@ -201,14 +203,14 @@ private ItemStack updateSlot(PlayerEntity player, Slot currentSlot, ItemStack re
return newStack;
}

public void handleBatchUpdate(Object2LongMap<HashedItem> itemMap, long countCapacity, int typeCapacity) {
public void handleBatchUpdate(Object2LongMap<UUIDAwareHashedItem> itemMap, long countCapacity, int typeCapacity) {
cachedInventory = itemMap;
cachedCountCapacity = countCapacity;
cachedTypeCapacity = typeCapacity;
syncItemList();
}

public void handleUpdate(Object2LongMap<HashedItem> itemMap, long countCapacity, int typeCapacity) {
public void handleUpdate(Object2LongMap<UUIDAwareHashedItem> itemMap, long countCapacity, int typeCapacity) {
itemMap.object2LongEntrySet().forEach(entry -> {
long value = entry.getLongValue();
if (value == 0) {
Expand Down Expand Up @@ -236,7 +238,7 @@ private void syncItemList() {
searchCache.clear();
totalItems = 0;
cachedInventory.forEach((key, value) -> {
itemList.add(new ItemSlotData(key, value));
itemList.add(new ItemSlotData(key, key.getUUID(), value));
totalItems += value;
});
sortItemList();
Expand Down Expand Up @@ -330,24 +332,22 @@ public void updateSearch(String queryText) {
public void onClick(IScrollableSlot slot, int button, boolean hasShiftDown, ItemStack heldItem) {
if (hasShiftDown) {
if (slot != null) {
Mekanism.packetHandler.sendToServer(PacketQIOItemViewerSlotInteract.shiftTake(slot.getItem()));
Mekanism.packetHandler.sendToServer(PacketQIOItemViewerSlotInteract.shiftTake(slot.getItemUUID()));
}
return;
}
if (button == 0) {
if (heldItem.isEmpty() && slot != null) {
HashedItem item = slot.getItem();
int toTake = Math.min(item.getStack().getMaxStackSize(), MathUtils.clampToInt(slot.getCount()));
Mekanism.packetHandler.sendToServer(PacketQIOItemViewerSlotInteract.take(item, toTake));
int toTake = Math.min(slot.getItem().getStack().getMaxStackSize(), MathUtils.clampToInt(slot.getCount()));
Mekanism.packetHandler.sendToServer(PacketQIOItemViewerSlotInteract.take(slot.getItemUUID(), toTake));
} else if (!heldItem.isEmpty()) {
Mekanism.packetHandler.sendToServer(PacketQIOItemViewerSlotInteract.put(heldItem.getCount()));
}
} else if (button == 1) {
if (heldItem.isEmpty() && slot != null) {
HashedItem item = slot.getItem();
//Cap it out at the max stack size of the item, but try to take half of what is stored (taking at least one if it is a single item)
int toTake = Math.min(item.getStack().getMaxStackSize(), Math.max(1, MathUtils.clampToInt(slot.getCount() / 2)));
Mekanism.packetHandler.sendToServer(PacketQIOItemViewerSlotInteract.take(item, toTake));
int toTake = Math.min(slot.getItem().getStack().getMaxStackSize(), Math.max(1, MathUtils.clampToInt(slot.getCount() / 2)));
Mekanism.packetHandler.sendToServer(PacketQIOItemViewerSlotInteract.take(slot.getItemUUID(), toTake));
} else if (!heldItem.isEmpty()) {
Mekanism.packetHandler.sendToServer(PacketQIOItemViewerSlotInteract.put(1));
}
Expand All @@ -357,10 +357,12 @@ public void onClick(IScrollableSlot slot, int button, boolean hasShiftDown, Item
public static class ItemSlotData implements IScrollableSlot {

private final HashedItem itemType;
private final UUID typeUUID;
private final long count;

private ItemSlotData(HashedItem itemType, long count) {
private ItemSlotData(HashedItem itemType, UUID typeUUID, long count) {
this.itemType = itemType;
this.typeUUID = typeUUID;
this.count = count;
}

Expand All @@ -369,6 +371,11 @@ public HashedItem getItem() {
return itemType;
}

@Override
public UUID getItemUUID() {
return typeUUID;
}

@Override
public long getCount() {
return count;
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/mekanism/common/lib/inventory/HashedItem.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package mekanism.common.lib.inventory;

import java.util.UUID;
import javax.annotation.Nullable;
import mekanism.common.util.InventoryUtils;
import mekanism.common.util.StackUtils;
import net.minecraft.item.ItemStack;
Expand All @@ -19,6 +21,11 @@ public HashedItem(ItemStack stack) {
hashCode = initHashCode();
}

protected HashedItem(HashedItem other) {
this.itemStack = other.itemStack;
this.hashCode = other.hashCode;
}

public ItemStack getStack() {
return itemStack;
}
Expand Down Expand Up @@ -52,4 +59,49 @@ private int initHashCode() {
}
return code;
}

public static class UUIDAwareHashedItem extends HashedItem {

private final UUID uuid;
private final boolean overrideHash;

/**
* @apiNote For use on the client side, hash is taken into account for equals and hashCode
*/
public UUIDAwareHashedItem(ItemStack stack, UUID uuid) {
super(stack);
this.uuid = uuid;
this.overrideHash = true;
}

public UUIDAwareHashedItem(HashedItem other, UUID uuid) {
super(other);
this.uuid = uuid;
this.overrideHash = false;
}

@Nullable
public UUID getUUID() {
return uuid;
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (overrideHash && uuid != null) {
return obj instanceof UUIDAwareHashedItem && uuid.equals(((UUIDAwareHashedItem) obj).uuid) && super.equals(obj);
}
return super.equals(obj);
}

@Override
public int hashCode() {
if (overrideHash && uuid != null) {
return 31 * super.hashCode() + uuid.hashCode();
}
return super.hashCode();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import java.util.function.Supplier;
import mekanism.common.inventory.container.QIOItemViewerContainer;
import mekanism.common.lib.inventory.HashedItem;
import mekanism.common.lib.inventory.HashedItem.UUIDAwareHashedItem;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent.Context;

public class PacketQIOItemViewerGuiSync {

private final Type type;
private final Object2LongMap<HashedItem> itemMap;
private final Object2LongMap<UUIDAwareHashedItem> itemMap;
private final long countCapacity;
private final int typeCapacity;

private PacketQIOItemViewerGuiSync(Type type, Object2LongMap<HashedItem> itemMap, long countCapacity, int typeCapacity) {
private PacketQIOItemViewerGuiSync(Type type, Object2LongMap<UUIDAwareHashedItem> itemMap, long countCapacity, int typeCapacity) {
this.type = type;
this.itemMap = itemMap;
this.countCapacity = countCapacity;
this.typeCapacity = typeCapacity;
}

public static PacketQIOItemViewerGuiSync batch(Object2LongMap<HashedItem> itemMap, long countCapacity, int typeCapacity) {
public static PacketQIOItemViewerGuiSync batch(Object2LongMap<UUIDAwareHashedItem> itemMap, long countCapacity, int typeCapacity) {
return new PacketQIOItemViewerGuiSync(Type.BATCH, itemMap, countCapacity, typeCapacity);
}

public static PacketQIOItemViewerGuiSync update(Object2LongMap<HashedItem> itemMap, long countCapacity, int typeCapacity) {
public static PacketQIOItemViewerGuiSync update(Object2LongMap<UUIDAwareHashedItem> itemMap, long countCapacity, int typeCapacity) {
return new PacketQIOItemViewerGuiSync(Type.UPDATE, itemMap, countCapacity, typeCapacity);
}

Expand Down Expand Up @@ -67,6 +67,13 @@ public static void encode(PacketQIOItemViewerGuiSync pkt, PacketBuffer buf) {
buf.writeVarInt(pkt.itemMap.size());
pkt.itemMap.forEach((key, value) -> {
buf.writeItemStack(key.getStack());
if (key.getUUID() == null) {
//Shouldn't be null unless something failed, but if it does try to handle it relatively gracefully
buf.writeBoolean(false);
} else {
buf.writeBoolean(true);
buf.writeUniqueId(key.getUUID());
}
buf.writeVarLong(value);
});
}
Expand All @@ -76,14 +83,14 @@ public static PacketQIOItemViewerGuiSync decode(PacketBuffer buf) {
Type type = buf.readEnumValue(Type.class);
long countCapacity = 0;
int typeCapacity = 0;
Object2LongMap<HashedItem> map = null;
Object2LongMap<UUIDAwareHashedItem> map = null;
if (type == Type.BATCH || type == Type.UPDATE) {
countCapacity = buf.readVarLong();
typeCapacity = buf.readVarInt();
map = new Object2LongOpenHashMap<>();
int count = buf.readVarInt();
map = new Object2LongOpenHashMap<>(count);
for (int i = 0; i < count; i++) {
map.put(new HashedItem(buf.readItemStack()), buf.readVarLong());
map.put(new UUIDAwareHashedItem(buf.readItemStack(), buf.readBoolean() ? buf.readUniqueId() : null), buf.readVarLong());
}
}
return new PacketQIOItemViewerGuiSync(type, map, countCapacity, typeCapacity);
Expand Down
Loading

0 comments on commit be85fda

Please sign in to comment.