diff --git a/src/main/java/com/raoulvdberge/refinedstorage/api/network/INetwork.java b/src/main/java/com/raoulvdberge/refinedstorage/api/network/INetwork.java index 706814bde..7ec8f27a5 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/api/network/INetwork.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/api/network/INetwork.java @@ -91,10 +91,16 @@ public interface INetwork { /** * Sends a item storage change to all clients that are watching a grid connected to this network. * - * @param stack the stack - * @param delta the delta + * @param stack the stack + * @param delta the delta + * @param batched whether the delta can be batched to be sent all at once using {@link #sendBatchedItemStorageDeltaToClient()} */ - void sendItemStorageDeltaToClient(ItemStack stack, int delta); + void sendItemStorageDeltaToClient(ItemStack stack, int delta, boolean batched); + + /** + * Sends batched item storage deltas, accumulated through {@link #sendItemStorageDeltaToClient(ItemStack, int, boolean)}. + */ + void sendBatchedItemStorageDeltaToClient(); /** * Sends a grid update packet with all the fluids to all clients that are watching a grid connected to this network. diff --git a/src/main/java/com/raoulvdberge/refinedstorage/api/storage/IStorageCache.java b/src/main/java/com/raoulvdberge/refinedstorage/api/storage/IStorageCache.java index 05d4feb5b..0c7f6aad3 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/api/storage/IStorageCache.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/api/storage/IStorageCache.java @@ -32,8 +32,9 @@ public interface IStorageCache { * @param stack the stack to add, do NOT modify * @param size the size to add * @param rebuilding true if this method is called while rebuilding, false otherwise + * @param batched true if this change needs to be batched */ - void add(@Nonnull T stack, int size, boolean rebuilding); + void add(@Nonnull T stack, int size, boolean rebuilding, boolean batched); /** * Removes a stack from the cache. @@ -41,10 +42,11 @@ public interface IStorageCache { * Note that this doesn't modify any of the connected storages, but just modifies the cache. * Use {@link IStorage#extract(T, int, int, boolean)} to remove a stack from an actual storage. * - * @param stack the stack to remove, do NOT modify - * @param size the size to remove + * @param stack the stack to remove, do NOT modify + * @param size the size to remove + * @param batched true if this change needs to be batched, false otherwise */ - void remove(@Nonnull T stack, int size); + void remove(@Nonnull T stack, int size, boolean batched); /** * Adds a listener to be called when this storage cache changes. diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/externalstorage/StorageItemExternal.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/externalstorage/StorageItemExternal.java index c01a06e30..6f3a9bb28 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/externalstorage/StorageItemExternal.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/externalstorage/StorageItemExternal.java @@ -35,7 +35,7 @@ public abstract class StorageItemExternal implements IStorage { // If we exceed the cache size, than that means this item is added if (i >= cache.size()) { if (!actual.isEmpty()) { - network.getItemStorageCache().add(actual, actual.getCount(), false); + network.getItemStorageCache().add(actual, actual.getCount(), false, true); } continue; @@ -45,29 +45,29 @@ public abstract class StorageItemExternal implements IStorage { if (!cached.isEmpty() && actual.isEmpty()) { // If the cached is not empty but the actual is, we remove this item - network.getItemStorageCache().remove(cached, cached.getCount()); + network.getItemStorageCache().remove(cached, cached.getCount(), true); } else if (cached.isEmpty() && !actual.isEmpty()) { // If the cached is empty and the actual isn't, we added this item - network.getItemStorageCache().add(actual, actual.getCount(), false); + network.getItemStorageCache().add(actual, actual.getCount(), false, true); network.getCraftingManager().track(actual, actual.getCount()); } else if (cached.isEmpty() && actual.isEmpty()) { // If they're both empty, nothing happens } else if (!API.instance().getComparer().isEqualNoQuantity(cached, actual)) { // If both items mismatch, remove the old and add the new - network.getItemStorageCache().remove(cached, cached.getCount()); - network.getItemStorageCache().add(actual, actual.getCount(), false); + network.getItemStorageCache().remove(cached, cached.getCount(), true); + network.getItemStorageCache().add(actual, actual.getCount(), false, true); network.getCraftingManager().track(actual, actual.getCount()); } else if (cached.getCount() != actual.getCount()) { int delta = actual.getCount() - cached.getCount(); if (delta > 0) { - network.getItemStorageCache().add(actual, delta, false); + network.getItemStorageCache().add(actual, delta, false, true); network.getCraftingManager().track(actual, delta); } else { - network.getItemStorageCache().remove(actual, Math.abs(delta)); + network.getItemStorageCache().remove(actual, Math.abs(delta), true); } } } @@ -77,12 +77,14 @@ public abstract class StorageItemExternal implements IStorage { if (cache.size() > newStacks.size()) { for (int i = newStacks.size(); i < cache.size(); ++i) { if (cache.get(i) != ItemStack.EMPTY) { - network.getItemStorageCache().remove(cache.get(i), cache.get(i).getCount()); + network.getItemStorageCache().remove(cache.get(i), cache.get(i).getCount(), true); } } } this.cache = newStacks; + + network.sendBatchedItemStorageDeltaToClient(); } @Override diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheFluid.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheFluid.java index 3595210b0..1c38af1ba 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheFluid.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheFluid.java @@ -45,7 +45,7 @@ public class StorageCacheFluid implements IStorageCache { } for (FluidStack stack : storage.getStacks()) { - add(stack, stack.amount, true); + add(stack, stack.amount, true, false); } } @@ -55,7 +55,7 @@ public class StorageCacheFluid implements IStorageCache { } @Override - public synchronized void add(@Nonnull FluidStack stack, int size, boolean rebuilding) { + public synchronized void add(@Nonnull FluidStack stack, int size, boolean rebuilding, boolean batched) { list.add(stack, size); if (!rebuilding) { @@ -66,7 +66,7 @@ public class StorageCacheFluid implements IStorageCache { } @Override - public synchronized void remove(@Nonnull FluidStack stack, int size) { + public synchronized void remove(@Nonnull FluidStack stack, int size, boolean batched) { if (list.remove(stack, size)) { network.sendFluidStorageDeltaToClient(stack, -size); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItem.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItem.java index b5b5d3290..c90ad4103 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItem.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItem.java @@ -46,7 +46,7 @@ public class StorageCacheItem implements IStorageCache { for (ItemStack stack : storage.getStacks()) { if (!stack.isEmpty()) { - add(stack, stack.getCount(), true); + add(stack, stack.getCount(), true, false); } } } @@ -57,20 +57,20 @@ public class StorageCacheItem implements IStorageCache { } @Override - public synchronized void add(@Nonnull ItemStack stack, int size, boolean rebuilding) { + public synchronized void add(@Nonnull ItemStack stack, int size, boolean rebuilding, boolean batched) { list.add(stack, size); if (!rebuilding) { - network.sendItemStorageDeltaToClient(stack, size); + network.sendItemStorageDeltaToClient(stack, size, batched); listeners.forEach(Runnable::run); } } @Override - public synchronized void remove(@Nonnull ItemStack stack, int size) { + public synchronized void remove(@Nonnull ItemStack stack, int size, boolean batched) { if (list.remove(stack, size)) { - network.sendItemStorageDeltaToClient(stack, -size); + network.sendItemStorageDeltaToClient(stack, -size, batched); listeners.forEach(Runnable::run); } diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItemPortable.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItemPortable.java index c2be7cfa3..062d4bfee 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItemPortable.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItemPortable.java @@ -41,7 +41,7 @@ public class StorageCacheItemPortable implements IStorageCache { } @Override - public void add(@Nonnull ItemStack stack, int size, boolean rebuilding) { + public void add(@Nonnull ItemStack stack, int size, boolean rebuilding, boolean batched) { list.add(stack, size); if (!rebuilding) { @@ -52,7 +52,7 @@ public class StorageCacheItemPortable implements IStorageCache { } @Override - public void remove(@Nonnull ItemStack stack, int size) { + public void remove(@Nonnull ItemStack stack, int size, boolean batched) { if (list.remove(stack, size)) { portableGrid.getWatchers().forEach(w -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(null, stack, -size), (EntityPlayerMP) w)); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageDiskItemPortable.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageDiskItemPortable.java index 8c9031449..9060b3486 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageDiskItemPortable.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageDiskItemPortable.java @@ -66,7 +66,7 @@ public class StorageDiskItemPortable implements IStorageDisk { int inserted = parent.getCacheDelta(storedPre, size, remainder); if (inserted > 0) { - portableGrid.getCache().add(stack, inserted, false); + portableGrid.getCache().add(stack, inserted, false, false); } } @@ -79,7 +79,7 @@ public class StorageDiskItemPortable implements IStorageDisk { ItemStack extracted = parent.extract(stack, size, flags, simulate); if (!simulate && extracted != null) { - portableGrid.getCache().remove(extracted, extracted.getCount()); + portableGrid.getCache().remove(extracted, extracted.getCount(), false); } return extracted; diff --git a/src/main/java/com/raoulvdberge/refinedstorage/integration/projecte/StorageItemTransmutationTable.java b/src/main/java/com/raoulvdberge/refinedstorage/integration/projecte/StorageItemTransmutationTable.java index 32ec4e120..c3caaa7eb 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/integration/projecte/StorageItemTransmutationTable.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/integration/projecte/StorageItemTransmutationTable.java @@ -13,6 +13,7 @@ import net.minecraftforge.items.ItemHandlerHelper; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -30,20 +31,22 @@ public class StorageItemTransmutationTable extends StorageItemExternal { @Override public Collection getStacks() { - List stored = new LinkedList<>(); - if (externalStorage.getOwner() != null) { IKnowledgeProvider provider = ProjectEAPI.getTransmutationProxy().getKnowledgeProviderFor(externalStorage.getOwner()); // @todo: https://github.com/sinkillerj/ProjectE/issues/1591 if (!provider.getClass().getName().equals("moze_intel.projecte.impl.TransmutationOffline$1")) { + List stored = new LinkedList<>(); + for (ItemStack knowledge : provider.getKnowledge()) { stored.add(ItemHandlerHelper.copyStackWithSize(knowledge, (int) Math.floor(provider.getEmc() / (double) ProjectEAPI.getEMCProxy().getValue(knowledge)))); } + + return stored; } } - return stored; + return Collections.emptyList(); } @Nullable diff --git a/src/main/java/com/raoulvdberge/refinedstorage/network/MessageGridItemDelta.java b/src/main/java/com/raoulvdberge/refinedstorage/network/MessageGridItemDelta.java index 422abb3a3..e8728ef4f 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/network/MessageGridItemDelta.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/network/MessageGridItemDelta.java @@ -10,16 +10,24 @@ import net.minecraft.item.ItemStack; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; +import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nullable; +import java.util.LinkedList; +import java.util.List; public class MessageGridItemDelta implements IMessage, IMessageHandler { @Nullable private INetwork network; + + private List> deltas; + @Nullable private ItemStack stack; private int delta; - private GridStackItem clientStack; + @Nullable + private GridStackItem gridStack; + private List> gridStacks; public MessageGridItemDelta() { } @@ -30,26 +38,64 @@ public class MessageGridItemDelta implements IMessage, IMessageHandler> deltas) { + this.network = network; + this.deltas = deltas; + } + @Override public void fromBytes(ByteBuf buf) { - clientStack = new GridStackItem(buf); - delta = buf.readInt(); + int size = buf.readInt(); + + if (size == 1) { + gridStack = new GridStackItem(buf); + delta = buf.readInt(); + } else { + gridStacks = new LinkedList<>(); + + for (int i = 0; i < size; ++i) { + gridStacks.add(Pair.of(new GridStackItem(buf), buf.readInt())); + } + } } @Override public void toBytes(ByteBuf buf) { - StackUtils.writeItemStack(buf, stack, network, false); - buf.writeInt(delta); + if (stack != null) { + buf.writeInt(1); + + StackUtils.writeItemStack(buf, stack, network, false); + buf.writeInt(delta); + } else { + buf.writeInt(deltas.size()); + + for (Pair delta : deltas) { + StackUtils.writeItemStack(buf, delta.getLeft(), network, false); + buf.writeInt(delta.getRight()); + } + } } @Override public IMessage onMessage(MessageGridItemDelta message, MessageContext ctx) { - Item item = message.clientStack.getStack().getItem(); + if (message.gridStack != null) { + process(message.gridStack, message.delta); + } else { + message.gridStacks.forEach(p -> process(p.getLeft(), p.getRight())); + } + + GuiGrid.markForSorting(); + + return null; + } + + private void process(GridStackItem gridStack, int delta) { + Item item = gridStack.getStack().getItem(); for (GridStackItem stack : GuiGrid.ITEMS.get(item)) { - if (stack.equals(message.clientStack)) { - if (stack.getStack().getCount() + message.delta == 0) { - if (message.clientStack.isCraftable()) { + if (stack.equals(gridStack)) { + if (stack.getStack().getCount() + delta == 0) { + if (gridStack.isCraftable()) { stack.setDisplayCraftText(true); } else { GuiGrid.ITEMS.remove(item, stack); @@ -58,23 +104,18 @@ public class MessageGridItemDelta implements IMessage, IMessageHandler itemStorage = new StorageCacheItem(this); + private List> batchedItemStorageDeltas = new LinkedList<>(); + private IStorageCache fluidStorage = new StorageCacheFluid(this); private Map readerWriterChannels = new HashMap<>(); @@ -298,10 +301,25 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe } @Override - public void sendItemStorageDeltaToClient(ItemStack stack, int delta) { - world.getMinecraftServer().getPlayerList().getPlayers().stream() - .filter(player -> isWatchingGrid(player, GridType.NORMAL, GridType.CRAFTING, GridType.PATTERN)) - .forEach(player -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(this, stack, delta), player)); + public void sendBatchedItemStorageDeltaToClient() { + if (!batchedItemStorageDeltas.isEmpty()) { + world.getMinecraftServer().getPlayerList().getPlayers().stream() + .filter(player -> isWatchingGrid(player, GridType.NORMAL, GridType.CRAFTING, GridType.PATTERN)) + .forEach(player -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(this, batchedItemStorageDeltas), player)); + + batchedItemStorageDeltas.clear(); + } + } + + @Override + public void sendItemStorageDeltaToClient(ItemStack stack, int delta, boolean batched) { + if (!batched) { + world.getMinecraftServer().getPlayerList().getPlayers().stream() + .filter(player -> isWatchingGrid(player, GridType.NORMAL, GridType.CRAFTING, GridType.PATTERN)) + .forEach(player -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(this, stack, delta), player)); + } else { + batchedItemStorageDeltas.add(Pair.of(stack, delta)); + } } @Override @@ -439,7 +457,7 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe } if (!simulate && inserted - insertedExternally > 0) { - itemStorage.add(stack, inserted - insertedExternally, false); + itemStorage.add(stack, inserted - insertedExternally, false, false); } return remainder; @@ -484,7 +502,7 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe } if (newStack != null && newStack.getCount() - extractedExternally > 0 && !simulate) { - itemStorage.remove(newStack, newStack.getCount() - extractedExternally); + itemStorage.remove(newStack, newStack.getCount() - extractedExternally, false); } return newStack; @@ -526,7 +544,7 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe } if (inserted > 0) { - fluidStorage.add(stack, inserted, false); + fluidStorage.add(stack, inserted, false, false); } return remainder; @@ -567,7 +585,7 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe } if (newStack != null && !simulate) { - fluidStorage.remove(newStack, newStack.amount); + fluidStorage.remove(newStack, newStack.amount, false); } return newStack;