New storage cache. Avoid copying stacks all the time.

This commit is contained in:
raoulvdberge
2018-11-21 19:11:37 +01:00
parent 23ec0c797c
commit 7988b0f789
7 changed files with 189 additions and 139 deletions

View File

@@ -16,6 +16,11 @@ public interface IStorage<T> {
};
/**
* Returns the stacks of the storage.
* Empty stacks are allowed.
* Please do not copy the stacks for performance reasons.
* For the caller: modifying stacks is not allowed!
*
* @return stacks stored in this storage, empty stacks are allowed
*/
Collection<T> getStacks();

View File

@@ -0,0 +1,89 @@
package com.raoulvdberge.refinedstorage.apiimpl.storage.externalstorage;
import com.raoulvdberge.refinedstorage.api.network.INetwork;
import com.raoulvdberge.refinedstorage.api.util.IComparer;
import com.raoulvdberge.refinedstorage.apiimpl.API;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidTankProperties;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class ExternalStorageCacheFluid {
private List<FluidStack> cache;
public void update(INetwork network, @Nullable IFluidHandler handler) {
if (handler == null) {
return;
}
if (cache == null) {
cache = new ArrayList<>();
for (IFluidTankProperties properties : handler.getTankProperties()) {
cache.add(properties.getContents() == null ? null : properties.getContents().copy());
}
return;
}
for (int i = 0; i < handler.getTankProperties().length; ++i) {
FluidStack actual = handler.getTankProperties()[i].getContents();
if (i >= cache.size()) { // ENLARGED
if (actual != null) {
network.getFluidStorageCache().add(actual, actual.amount, false, true);
cache.add(actual.copy());
}
continue;
}
FluidStack cached = cache.get(i);
if (actual == null && cached == null) { // NONE
continue;
}
if (actual == null && cached != null) { // REMOVED
network.getFluidStorageCache().remove(cached, cached.amount, true);
cache.set(i, null);
} else if (actual != null && cached == null) { // ADDED
network.getFluidStorageCache().add(actual, actual.amount, false, true);
cache.set(i, actual.copy());
} else if (!API.instance().getComparer().isEqual(actual, cached, IComparer.COMPARE_NBT)) { // CHANGED
network.getFluidStorageCache().remove(cached, cached.amount, true);
network.getFluidStorageCache().add(actual, actual.amount, false, true);
cache.set(i, actual.copy());
} else if (actual.amount > cached.amount) { // COUNT_CHANGED
network.getFluidStorageCache().add(actual, actual.amount - cached.amount, false, true);
cached.amount = actual.amount;
} else if (actual.amount < cached.amount) { // COUNT_CHANGED
network.getFluidStorageCache().remove(actual, cached.amount - actual.amount, true);
cached.amount = actual.amount;
}
}
if (cache.size() > handler.getTankProperties().length) { // SHRUNK
for (int i = cache.size() - 1; i >= handler.getTankProperties().length; --i) { // Reverse order for the remove call.
FluidStack cached = cache.get(i);
if (cached != null) {
network.getFluidStorageCache().remove(cached, cached.amount, true);
}
cache.remove(i);
}
}
network.getFluidStorageCache().flush();
}
}

View File

@@ -0,0 +1,87 @@
package com.raoulvdberge.refinedstorage.apiimpl.storage.externalstorage;
import com.raoulvdberge.refinedstorage.api.network.INetwork;
import com.raoulvdberge.refinedstorage.apiimpl.API;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class ExternalStorageCacheItem {
private List<ItemStack> cache;
public void update(INetwork network, @Nullable IItemHandler handler) {
if (handler == null) {
return;
}
if (cache == null) {
cache = new ArrayList<>();
for (int i = 0; i < handler.getSlots(); ++i) {
cache.add(handler.getStackInSlot(i).copy());
}
return;
}
for (int i = 0; i < handler.getSlots(); ++i) {
ItemStack actual = handler.getStackInSlot(i);
if (i >= cache.size()) { // ENLARGED
if (!actual.isEmpty()) {
network.getItemStorageCache().add(actual, actual.getCount(), false, true);
cache.add(actual.copy());
}
continue;
}
ItemStack cached = cache.get(i);
if (!cached.isEmpty() && actual.isEmpty()) { // REMOVED
network.getItemStorageCache().remove(cached, cached.getCount(), true);
cache.set(i, ItemStack.EMPTY);
} else if (cached.isEmpty() && !actual.isEmpty()) { // ADDED
network.getItemStorageCache().add(actual, actual.getCount(), false, true);
cache.set(i, actual.copy());
} else if (!API.instance().getComparer().isEqualNoQuantity(cached, actual)) { // CHANGED
network.getItemStorageCache().remove(cached, cached.getCount(), true);
network.getItemStorageCache().add(actual, actual.getCount(), false, true);
cache.set(i, actual.copy());
} else if (cached.getCount() != actual.getCount()) { // COUNT_CHANGED
int delta = actual.getCount() - cached.getCount();
if (delta > 0) {
network.getItemStorageCache().add(actual, delta, false, true);
cached.grow(delta);
} else {
network.getItemStorageCache().remove(actual, Math.abs(delta), true);
cached.shrink(Math.abs(delta));
}
}
}
if (cache.size() > handler.getSlots()) { // SHRUNK
for (int i = cache.size() - 1; i >= handler.getSlots(); --i) { // Reverse order for the remove call.
ItemStack cached = cache.get(i);
if (!cached.isEmpty()) {
network.getItemStorageCache().remove(cached, cached.getCount(), true);
}
cache.remove(i);
}
}
network.getItemStorageCache().flush();
}
}

View File

@@ -5,8 +5,6 @@ import com.raoulvdberge.refinedstorage.api.storage.AccessType;
import com.raoulvdberge.refinedstorage.api.storage.externalstorage.IExternalStorageContext;
import com.raoulvdberge.refinedstorage.api.storage.externalstorage.IStorageExternal;
import com.raoulvdberge.refinedstorage.api.util.Action;
import com.raoulvdberge.refinedstorage.api.util.IComparer;
import com.raoulvdberge.refinedstorage.apiimpl.API;
import com.raoulvdberge.refinedstorage.util.StackUtils;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
@@ -23,8 +21,8 @@ import java.util.function.Supplier;
public class StorageExternalFluid implements IStorageExternal<FluidStack> {
private IExternalStorageContext context;
private Supplier<IFluidHandler> handlerSupplier;
private List<FluidStack> cache;
private boolean connectedToInterface;
private ExternalStorageCacheFluid cache = new ExternalStorageCacheFluid();
public StorageExternalFluid(IExternalStorageContext context, Supplier<IFluidHandler> handlerSupplier, boolean connectedToInterface) {
this.context = context;
@@ -45,60 +43,11 @@ public class StorageExternalFluid implements IStorageExternal<FluidStack> {
@Override
public void update(INetwork network) {
// If we are insert only, we don't care about sending changes
if (getAccessType() == AccessType.INSERT) {
return;
}
if (cache == null) {
cache = new ArrayList<>(getStacksWithNulls());
return;
}
List<FluidStack> newStacks = new ArrayList<>(getStacksWithNulls());
for (int i = 0; i < newStacks.size(); ++i) {
FluidStack actual = newStacks.get(i);
// If we exceed the cache size, than that means this item is added
if (i >= cache.size()) {
if (actual != null) {
network.getFluidStorageCache().add(actual, actual.amount, false, true);
}
continue;
}
FluidStack cached = cache.get(i);
if (actual == null && cached == null) {
// NO OP
} else if (actual == null && cached != null) {
network.getFluidStorageCache().remove(cached, cached.amount, true);
} else if (actual != null && cached == null) {
network.getFluidStorageCache().add(actual, actual.amount, false, true);
} else if (!API.instance().getComparer().isEqual(actual, cached, IComparer.COMPARE_NBT)) {
network.getFluidStorageCache().remove(cached, cached.amount, true);
network.getFluidStorageCache().add(actual, actual.amount, false, true);
} else if (actual.amount > cached.amount) {
network.getFluidStorageCache().add(actual, actual.amount - cached.amount, false, true);
} else if (actual.amount < cached.amount) {
network.getFluidStorageCache().remove(actual, cached.amount - actual.amount, true);
}
}
if (cache.size() > newStacks.size()) {
for (int i = newStacks.size(); i < cache.size(); ++i) {
if (cache.get(i) != null) {
network.getFluidStorageCache().remove(cache.get(i), cache.get(i).amount, true);
}
}
}
this.cache = newStacks;
network.getFluidStorageCache().flush();
cache.update(network, handlerSupplier.get());
}
@Override
@@ -129,29 +78,7 @@ public class StorageExternalFluid implements IStorageExternal<FluidStack> {
FluidStack stack = properties.getContents();
if (stack != null) {
fluids.add(stack.copy());
}
}
return fluids;
}
return Collections.emptyList();
}
private Collection<FluidStack> getStacksWithNulls() {
IFluidTankProperties[] props = getProperties();
if (props != null) {
List<FluidStack> fluids = new ArrayList<>();
for (IFluidTankProperties properties : props) {
FluidStack stack = properties.getContents();
if (stack != null) {
fluids.add(stack.copy());
} else {
fluids.add(null);
fluids.add(stack);
}
}

View File

@@ -22,8 +22,8 @@ import java.util.function.Supplier;
public class StorageExternalItem implements IStorageExternal<ItemStack> {
private IExternalStorageContext context;
private Supplier<IItemHandler> handlerSupplier;
private List<ItemStack> cache;
private boolean connectedToInterface;
private ExternalStorageCacheItem cache = new ExternalStorageCacheItem();
public StorageExternalItem(IExternalStorageContext context, Supplier<IItemHandler> handlerSupplier, boolean connectedToInterface) {
this.context = context;
@@ -37,69 +37,11 @@ public class StorageExternalItem implements IStorageExternal<ItemStack> {
@Override
public void update(INetwork network) {
// If we are insert only, we don't care about sending changes
if (getAccessType() == AccessType.INSERT) {
return;
}
if (cache == null) {
cache = new ArrayList<>(getStacks());
return;
}
List<ItemStack> newStacks = new ArrayList<>(getStacks());
for (int i = 0; i < newStacks.size(); ++i) {
ItemStack actual = newStacks.get(i);
// 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, true);
}
continue;
}
ItemStack cached = cache.get(i);
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(), 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, true);
} 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(), true);
network.getItemStorageCache().add(actual, actual.getCount(), false, true);
} else if (cached.getCount() != actual.getCount()) {
int delta = actual.getCount() - cached.getCount();
if (delta > 0) {
network.getItemStorageCache().add(actual, delta, false, true);
} else {
network.getItemStorageCache().remove(actual, Math.abs(delta), true);
}
}
}
// If the cache size is somehow bigger than the actual stacks, that means the inventory shrunk
// In that case, we remove the items that have been removed due to the shrinkage
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(), true);
}
}
}
this.cache = newStacks;
network.getItemStorageCache().flush();
cache.update(network, handlerSupplier.get());
}
@Override
@@ -130,7 +72,7 @@ public class StorageExternalItem implements IStorageExternal<ItemStack> {
List<ItemStack> stacks = new ArrayList<>();
for (int i = 0; i < handler.getSlots(); ++i) {
stacks.add(handler.getStackInSlot(i).copy());
stacks.add(handler.getStackInSlot(i));
}
return stacks;

View File

@@ -15,7 +15,7 @@ public class StackListFluid implements IStackList<FluidStack> {
@Override
public void add(@Nonnull FluidStack stack, int size) {
if (stack == null || size < 0) {
if (size < 0) {
throw new IllegalArgumentException("Cannot accept empty stack");
}

View File

@@ -16,7 +16,7 @@ public class StackListItem implements IStackList<ItemStack> {
@Override
public void add(@Nonnull ItemStack stack, int size) {
if (stack == null || stack.isEmpty() || size <= 0) {
if (stack.isEmpty() || size <= 0) {
throw new IllegalArgumentException("Cannot accept empty stack");
}