diff --git a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTest.java b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTest.java index 5bc29c45c..d7cc58db4 100644 --- a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTest.java +++ b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTest.java @@ -11,6 +11,7 @@ import com.refinedmods.refinedstorage2.api.network.impl.node.relay.RelayInputNetworkNode; import com.refinedmods.refinedstorage2.api.network.impl.node.relay.RelayOutputNetworkNode; import com.refinedmods.refinedstorage2.api.network.impl.node.storage.StorageNetworkNode; +import com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer.StorageTransferNetworkNode; import com.refinedmods.refinedstorage2.network.test.nodefactory.ControllerNetworkNodeFactory; import com.refinedmods.refinedstorage2.network.test.nodefactory.DetectorNetworkNodeFactory; import com.refinedmods.refinedstorage2.network.test.nodefactory.ExporterNetworkNodeFactory; @@ -22,6 +23,7 @@ import com.refinedmods.refinedstorage2.network.test.nodefactory.RelayOutputNetworkNodeFactory; import com.refinedmods.refinedstorage2.network.test.nodefactory.SimpleNetworkNodeFactory; import com.refinedmods.refinedstorage2.network.test.nodefactory.StorageNetworkNodeFactory; +import com.refinedmods.refinedstorage2.network.test.nodefactory.StorageTransferNetworkNodeFactory; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -44,5 +46,6 @@ @RegisterNetworkNode(value = DetectorNetworkNodeFactory.class, clazz = DetectorNetworkNode.class) @RegisterNetworkNode(value = RelayInputNetworkNodeFactory.class, clazz = RelayInputNetworkNode.class) @RegisterNetworkNode(value = RelayOutputNetworkNodeFactory.class, clazz = RelayOutputNetworkNode.class) +@RegisterNetworkNode(value = StorageTransferNetworkNodeFactory.class, clazz = StorageTransferNetworkNode.class) public @interface NetworkTest { } diff --git a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTestFixtures.java b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTestFixtures.java index 4cbfc678c..aa4a5a1c5 100644 --- a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTestFixtures.java +++ b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTestFixtures.java @@ -15,6 +15,8 @@ import com.refinedmods.refinedstorage2.api.resource.list.ResourceListImpl; import com.refinedmods.refinedstorage2.network.test.fake.FakePermissions; +import java.util.LinkedHashMap; + public final class NetworkTestFixtures { public static final ComponentMapFactory NETWORK_COMPONENT_MAP_FACTORY = new ComponentMapFactory<>(); @@ -30,7 +32,7 @@ public final class NetworkTestFixtures { ); NETWORK_COMPONENT_MAP_FACTORY.addFactory( StorageNetworkComponent.class, - network -> new StorageNetworkComponentImpl(new ResourceListImpl()) + network -> new StorageNetworkComponentImpl(new ResourceListImpl(new LinkedHashMap<>())) ); NETWORK_COMPONENT_MAP_FACTORY.addFactory( SecurityNetworkComponent.class, diff --git a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/nodefactory/StorageTransferNetworkNodeFactory.java b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/nodefactory/StorageTransferNetworkNodeFactory.java new file mode 100644 index 000000000..3dc766bd3 --- /dev/null +++ b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/nodefactory/StorageTransferNetworkNodeFactory.java @@ -0,0 +1,23 @@ +package com.refinedmods.refinedstorage2.network.test.nodefactory; + +import com.refinedmods.refinedstorage2.api.network.impl.node.AbstractNetworkNode; +import com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer.StorageTransferNetworkNode; +import com.refinedmods.refinedstorage2.network.test.AddNetworkNode; + +import java.util.Map; + +public class StorageTransferNetworkNodeFactory extends AbstractNetworkNodeFactory { + public static final String PROPERTY_ENERGY_USAGE_PER_STORAGE = "energy_usage_per_storage"; + public static final String PROPERTY_SIZE = "size"; + + @Override + protected AbstractNetworkNode innerCreate(final AddNetworkNode ctx, final Map properties) { + final long energyUsagePerStorage = (long) properties.getOrDefault(PROPERTY_ENERGY_USAGE_PER_STORAGE, 0L); + final int size = (int) properties.getOrDefault(PROPERTY_SIZE, 6); + return new StorageTransferNetworkNode( + getEnergyUsage(properties), + energyUsagePerStorage, + size + ); + } +} diff --git a/refinedstorage2-network-test/src/test/java/com/refinedmods/refinedstorage2/network/test/NetworkNodeFactoryTest.java b/refinedstorage2-network-test/src/test/java/com/refinedmods/refinedstorage2/network/test/NetworkNodeFactoryTest.java index bfe41ac4f..cff8bdbf9 100644 --- a/refinedstorage2-network-test/src/test/java/com/refinedmods/refinedstorage2/network/test/NetworkNodeFactoryTest.java +++ b/refinedstorage2-network-test/src/test/java/com/refinedmods/refinedstorage2/network/test/NetworkNodeFactoryTest.java @@ -11,6 +11,7 @@ import com.refinedmods.refinedstorage2.api.network.impl.node.relay.RelayInputNetworkNode; import com.refinedmods.refinedstorage2.api.network.impl.node.relay.RelayOutputNetworkNode; import com.refinedmods.refinedstorage2.api.network.impl.node.storage.StorageNetworkNode; +import com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer.StorageTransferNetworkNode; import org.junit.jupiter.api.Test; @@ -41,6 +42,8 @@ class NetworkNodeFactoryTest { RelayInputNetworkNode relayInput; @AddNetworkNode RelayOutputNetworkNode relayOutput; + @AddNetworkNode + StorageTransferNetworkNode storageTransfer; @Test void testInitialization() { diff --git a/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferListener.java b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferListener.java new file mode 100644 index 000000000..42a1deaec --- /dev/null +++ b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferListener.java @@ -0,0 +1,6 @@ +package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer; + +@FunctionalInterface +public interface StorageTransferListener { + void onTransferReady(int index); +} diff --git a/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferMode.java b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferMode.java new file mode 100644 index 000000000..58e8eed37 --- /dev/null +++ b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferMode.java @@ -0,0 +1,6 @@ +package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer; + +public enum StorageTransferMode { + INSERT, + EXTRACT +} diff --git a/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNode.java b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNode.java new file mode 100644 index 000000000..fe2fce054 --- /dev/null +++ b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNode.java @@ -0,0 +1,158 @@ +package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer; + +import com.refinedmods.refinedstorage2.api.network.impl.node.AbstractStorageContainerNetworkNode; +import com.refinedmods.refinedstorage2.api.network.node.NetworkNodeActor; +import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage2.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage2.api.resource.ResourceKey; +import com.refinedmods.refinedstorage2.api.resource.filter.Filter; +import com.refinedmods.refinedstorage2.api.resource.filter.FilterMode; +import com.refinedmods.refinedstorage2.api.storage.Actor; +import com.refinedmods.refinedstorage2.api.storage.Storage; +import com.refinedmods.refinedstorage2.api.storage.TransferHelper; +import com.refinedmods.refinedstorage2.api.storage.limited.LimitedStorage; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.function.Predicate; +import java.util.function.ToLongFunction; +import java.util.function.UnaryOperator; +import javax.annotation.Nullable; + +public class StorageTransferNetworkNode extends AbstractStorageContainerNetworkNode { + private final Filter filter = new Filter(); + private final Actor actor = new NetworkNodeActor(this); + + private StorageTransferMode mode = StorageTransferMode.INSERT; + @Nullable + private ToLongFunction transferQuotaProvider; + @Nullable + private StorageTransferListener listener; + + public StorageTransferNetworkNode(final long energyUsage, final long energyUsagePerStorage, final int size) { + super(energyUsage, energyUsagePerStorage, size); + } + + public void setMode(final StorageTransferMode mode) { + this.mode = mode; + } + + public void setFilters(final Set filters) { + filter.setFilters(filters); + } + + public void setFilterMode(final FilterMode filterMode) { + filter.setMode(filterMode); + } + + public void setNormalizer(final UnaryOperator normalizer) { + filter.setNormalizer(normalizer); + } + + public void setTransferQuotaProvider(final ToLongFunction transferQuotaProvider) { + this.transferQuotaProvider = transferQuotaProvider; + } + + public void setListener(@Nullable final StorageTransferListener listener) { + this.listener = listener; + } + + @Override + public void doWork() { + super.doWork(); + if (!isActive() || network == null) { + return; + } + final StorageNetworkComponent networkStorage = network.getComponent(StorageNetworkComponent.class); + for (int i = 0; i < storages.length / 2; ++i) { + final Storage storage = storages[i]; + if (storage == null) { + continue; + } + final Result result = transfer(storage, networkStorage); + if (processResult(result, i)) { + return; + } + } + } + + private Result transfer(final Storage storage, final StorageNetworkComponent networkStorage) { + if (transferQuotaProvider == null) { + return Result.FAILURE; + } + final long transferQuota = transferQuotaProvider.applyAsLong(storage); + if (mode == StorageTransferMode.INSERT) { + return transfer(storage, networkStorage, transferQuota, this::hasNoExtractableResources); + } + return transfer( + networkStorage, + storage, + transferQuota, + source -> hasNoExtractableResources(source) || storageIsFull(storage) + ); + } + + private Result transfer(final Storage source, + final Storage destination, + final long transferQuota, + final Predicate readyPredicate) { + if (readyPredicate.test(source)) { + return Result.SUCCESS; + } + if (transfer(source, destination, transferQuota)) { + return readyPredicate.test(source) + ? Result.SUCCESS + : Result.PARTIAL; + } + return Result.FAILURE; + } + + private boolean transfer(final Storage source, final Storage destination, final long transferQuota) { + long remainder = transferQuota; + final Collection all = new LinkedHashSet<>(source.getAll()); + for (final ResourceAmount resourceAmount : all) { + final ResourceKey resource = resourceAmount.getResource(); + if (!filter.isAllowed(resource)) { + continue; + } + final long amount = Math.min(remainder, resourceAmount.getAmount()); + final long transferred = TransferHelper.transfer(resource, amount, actor, source, destination, source); + remainder -= transferred; + if (remainder == 0) { + break; + } + } + return remainder != transferQuota; + } + + private boolean hasNoExtractableResources(final Storage source) { + return source.getAll().stream().noneMatch(resourceAmount -> filter.isAllowed(resourceAmount.getResource())); + } + + private boolean storageIsFull(final Storage storage) { + return storage instanceof LimitedStorage limitedStorage + && limitedStorage.getCapacity() > 0 + && limitedStorage.getCapacity() == limitedStorage.getStored(); + } + + private boolean processResult(final Result result, final int index) { + if (result.isSuccess()) { + if (result == Result.SUCCESS && listener != null) { + listener.onTransferReady(index); + } + return true; + } + return false; + } + + private enum Result { + SUCCESS, + PARTIAL, + FAILURE; + + private boolean isSuccess() { + return this == PARTIAL || this == SUCCESS; + } + } +} diff --git a/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/package-info.java b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/package-info.java new file mode 100644 index 000000000..6f817bc33 --- /dev/null +++ b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@FieldsAndMethodsAreNonnullByDefault +package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer; + +import com.refinedmods.refinedstorage2.api.core.FieldsAndMethodsAreNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeProviderImpl.java b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/ProviderImpl.java similarity index 66% rename from refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeProviderImpl.java rename to refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/ProviderImpl.java index 5181ab680..a55d74eda 100644 --- a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeProviderImpl.java +++ b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/ProviderImpl.java @@ -1,13 +1,12 @@ -package com.refinedmods.refinedstorage2.api.network.impl.node.storage; +package com.refinedmods.refinedstorage2.api.network.impl.node; -import com.refinedmods.refinedstorage2.api.network.impl.node.AbstractStorageContainerNetworkNode; import com.refinedmods.refinedstorage2.api.storage.Storage; import java.util.HashMap; import java.util.Map; import java.util.Optional; -class StorageNetworkNodeProviderImpl implements AbstractStorageContainerNetworkNode.Provider { +public class ProviderImpl implements AbstractStorageContainerNetworkNode.Provider { private final Map storages = new HashMap<>(); @Override diff --git a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/PriorityStorageNetworkNodeTest.java b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/PriorityStorageNetworkNodeTest.java index b5b7fa837..f1761eac5 100644 --- a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/PriorityStorageNetworkNodeTest.java +++ b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/PriorityStorageNetworkNodeTest.java @@ -1,6 +1,7 @@ package com.refinedmods.refinedstorage2.api.network.impl.node.storage; import com.refinedmods.refinedstorage2.api.core.Action; +import com.refinedmods.refinedstorage2.api.network.impl.node.ProviderImpl; import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent; import com.refinedmods.refinedstorage2.api.storage.EmptyActor; import com.refinedmods.refinedstorage2.api.storage.Storage; @@ -26,11 +27,11 @@ class PriorityStorageNetworkNodeTest { @AddNetworkNode StorageNetworkNode b; - StorageNetworkNodeProviderImpl provider; + ProviderImpl provider; @BeforeEach void setUp() { - provider = new StorageNetworkNodeProviderImpl(); + provider = new ProviderImpl(); } @ParameterizedTest @@ -41,13 +42,13 @@ void shouldRespectPriority( ) { // Arrange final Storage storage1 = new LimitedStorageImpl(100); - final StorageNetworkNodeProviderImpl provider1 = new StorageNetworkNodeProviderImpl(); + final ProviderImpl provider1 = new ProviderImpl(); provider1.set(1, storage1); a.setProvider(provider1); a.setActive(true); final Storage storage2 = new LimitedStorageImpl(100); - final StorageNetworkNodeProviderImpl provider2 = new StorageNetworkNodeProviderImpl(); + final ProviderImpl provider2 = new ProviderImpl(); provider2.set(1, storage2); b.setProvider(provider2); b.setActive(true); diff --git a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeTest.java b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeTest.java index 74241524f..441da0459 100644 --- a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeTest.java +++ b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeTest.java @@ -2,6 +2,7 @@ import com.refinedmods.refinedstorage2.api.core.Action; import com.refinedmods.refinedstorage2.api.network.Network; +import com.refinedmods.refinedstorage2.api.network.impl.node.ProviderImpl; import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent; import com.refinedmods.refinedstorage2.api.resource.ResourceAmount; import com.refinedmods.refinedstorage2.api.resource.filter.FilterMode; @@ -55,11 +56,11 @@ class StorageNetworkNodeTest { }) StorageNetworkNode sut; - StorageNetworkNodeProviderImpl provider; + ProviderImpl provider; @BeforeEach void setUp() { - provider = new StorageNetworkNodeProviderImpl(); + provider = new ProviderImpl(); } @Test diff --git a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNodeTest.java b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNodeTest.java new file mode 100644 index 000000000..ecfd065ac --- /dev/null +++ b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNodeTest.java @@ -0,0 +1,715 @@ +package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer; + +import com.refinedmods.refinedstorage2.api.core.Action; +import com.refinedmods.refinedstorage2.api.network.impl.node.ProviderImpl; +import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage2.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage2.api.resource.ResourceKey; +import com.refinedmods.refinedstorage2.api.resource.filter.FilterMode; +import com.refinedmods.refinedstorage2.api.resource.list.ResourceListImpl; +import com.refinedmods.refinedstorage2.api.storage.Actor; +import com.refinedmods.refinedstorage2.api.storage.EmptyActor; +import com.refinedmods.refinedstorage2.api.storage.InMemoryStorageImpl; +import com.refinedmods.refinedstorage2.api.storage.Storage; +import com.refinedmods.refinedstorage2.api.storage.limited.LimitedStorageImpl; +import com.refinedmods.refinedstorage2.network.test.AddNetworkNode; +import com.refinedmods.refinedstorage2.network.test.InjectNetworkStorageComponent; +import com.refinedmods.refinedstorage2.network.test.NetworkTest; +import com.refinedmods.refinedstorage2.network.test.SetupNetwork; + +import java.util.LinkedHashMap; +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.A; +import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.A_ALTERNATIVE; +import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.B; +import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.C; +import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.D; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@NetworkTest +@SetupNetwork +class StorageTransferNetworkNodeTest { + @AddNetworkNode + StorageTransferNetworkNode sut; + StorageTransferListener listener; + ProviderImpl provider; + + @BeforeEach + void setUp() { + provider = new ProviderImpl(); + listener = mock(StorageTransferListener.class); + sut.setListener(listener); + } + + @Test + void shouldNotTransferWithoutNetwork(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + + final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + + // Act + sut.setNetwork(null); + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).isEmpty(); + assertThat(source.getAll()).isNotEmpty(); + } + + @Test + void shouldNotTransferWhenInactive(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + + final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + + // Act + sut.setActive(false); + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).isEmpty(); + assertThat(source.getAll()).isNotEmpty(); + } + + @ParameterizedTest + @EnumSource(StorageTransferMode.class) + void shouldNotTransferWithoutTransferQuotaProvider( + final StorageTransferMode mode, + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + + final Storage source = new InMemoryStorageImpl(); + source.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source); + + sut.setProvider(provider); + sut.setMode(mode); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(A, 5) + ); + assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(B, 5) + ); + } + + @Test + void shouldInsert(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + + final Storage source0 = new LimitedStorageImpl(1) { + @Override + public long extract(ResourceKey resource, long amount, Action action, Actor actor) { + return 0; + } + }; + source0.insert(A, 1, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source0); + + final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(1, source1); + + final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(2, source2); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(B, 5), + new ResourceAmount(C, 10) + ); + assertThat(source0.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(A, 1) + ); + assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 25), + new ResourceAmount(D, 5) + ); + assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(D, 5) + ); + verify(listener, never()).onTransferReady(anyInt()); + } + + @Test + void shouldInsertAllowlist(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + + final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set((sut.getSize() / 2) - 2, source1); + + final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set((sut.getSize() / 2) - 1, source2); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + sut.setFilterMode(FilterMode.ALLOW); + sut.setFilters(Set.of(A, B)); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(B, 5) + ); + assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 35), + new ResourceAmount(D, 5) + ); + assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(D, 5) + ); + verify(listener, times(1)).onTransferReady((sut.getSize() / 2) - 2); + } + + @Test + void shouldInsertBlocklist(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + + final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source1); + + final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(1, source2); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + sut.setFilterMode(FilterMode.BLOCK); + sut.setFilters(Set.of(A, B)); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(C, 20) + ); + assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(B, 5), + new ResourceAmount(C, 15), + new ResourceAmount(D, 5) + ); + assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(D, 5) + ); + verify(listener, never()).onTransferReady(anyInt()); + } + + @Test + void shouldNotifyListenerWhenReadyInsertingBecauseStorageWasAlreadyEmpty() { + // Arrange + final Storage source = new InMemoryStorageImpl(); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 15L); + + // Act + sut.doWork(); + + // Assert + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldNotifyListenerWhenReadyInsertingAllResources( + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + + final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 15L); + + // Act + sut.doWork(); + + // Assert + assertThat(source.getAll()).isEmpty(); + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldNotifyListenerWhenReadyInsertingAllResourcesAndUsingFilterButInsertedNothing() { + // Arrange + final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source1); + + final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(1, source2); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 15L); + sut.setFilterMode(FilterMode.ALLOW); + sut.setFilters(Set.of(A)); + + // Act + sut.doWork(); + + // Assert + assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(B, 5), + new ResourceAmount(C, 5) + ); + assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(D, 5) + ); + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldNotifyListenerWhenReadyInsertingAllResourcesAndUsingFilterButStillInsertedSomething( + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + + final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source1); + + final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(1, source2); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 15L); + sut.setFilterMode(FilterMode.ALLOW); + sut.setFilters(Set.of(A)); + + // Act + sut.doWork(); + + // Assert + assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(B, 5), + new ResourceAmount(C, 5) + ); + assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(D, 5) + ); + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldNotNotifyListenerWhenReadyInsertingAllResourcesAndNetworkIsFull( + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new LimitedStorageImpl(15)); + + final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source1.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source1); + + final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source2.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(1, source2); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 100L); + + // Act + sut.doWork(); + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(B, 5), + new ResourceAmount(C, 5) + ); + assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(D, 5) + ); + assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(A, 5) + ); + verify(listener, never()).onTransferReady(anyInt()); + } + + @Test + void shouldExtract(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + + final Storage source1 = new LimitedStorageImpl(0); + provider.set(0, source1); + + final Storage source2 = new InMemoryStorageImpl(); + provider.set(1, source2); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + sut.setMode(StorageTransferMode.EXTRACT); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 25), + new ResourceAmount(D, 5) + ); + assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(B, 5), + new ResourceAmount(C, 10) + ); + assertThat(source1.getAll()).isEmpty(); + verify(listener, never()).onTransferReady(anyInt()); + } + + @Test + void shouldExtractAllowlist(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + + final Storage source1 = new InMemoryStorageImpl(); + provider.set(0, source1); + + final Storage source2 = new InMemoryStorageImpl(); + provider.set(1, source2); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + sut.setMode(StorageTransferMode.EXTRACT); + sut.setFilterMode(FilterMode.ALLOW); + sut.setFilters(Set.of(A, B)); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 35), + new ResourceAmount(D, 5) + ); + assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(B, 5) + ); + assertThat(source2.getAll()).isEmpty(); + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldExtractBlocklist(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + + final Storage source1 = new InMemoryStorageImpl(); + provider.set(0, source1); + + final Storage source2 = new InMemoryStorageImpl(); + provider.set(1, source2); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + sut.setMode(StorageTransferMode.EXTRACT); + sut.setFilterMode(FilterMode.BLOCK); + sut.setFilters(Set.of(A, B)); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(B, 5), + new ResourceAmount(C, 15), + new ResourceAmount(D, 5) + ); + assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 20) + ); + assertThat(source2.getAll()).isEmpty(); + verify(listener, never()).onTransferReady(anyInt()); + } + + @Test + void shouldNotifyListenerWhenReadyExtractingBecauseStorageWasAlreadyEmpty() { + // Arrange + final Storage source = new InMemoryStorageImpl(); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 15L); + sut.setMode(StorageTransferMode.EXTRACT); + + // Act + sut.doWork(); + + // Assert + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldNotifyListenerWhenReadyExtractingAllResources( + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE); + + final Storage source = new InMemoryStorageImpl(); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 15L); + sut.setMode(StorageTransferMode.EXTRACT); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).isEmpty(); + assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(B, 5), + new ResourceAmount(C, 5) + ); + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldNotifyListenerWhenReadyExtractingAllResourcesAndUsingFilterButInsertedNothing( + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + + final Storage source = new InMemoryStorageImpl(); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 15L); + sut.setFilterMode(FilterMode.ALLOW); + sut.setFilters(Set.of(A)); + sut.setMode(StorageTransferMode.EXTRACT); + + // Act + sut.doWork(); + + // Assert + assertThat(source.getAll()).isEmpty(); + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(B, 5), + new ResourceAmount(C, 5), + new ResourceAmount(D, 5) + ); + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldNotifyListenerWhenReadyExtractingAllResourcesAndUsingFilterButStillExtractedSomething( + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()))); + networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE); + + final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 15L); + sut.setFilterMode(FilterMode.ALLOW); + sut.setFilters(Set.of(A)); + sut.setMode(StorageTransferMode.EXTRACT); + + // Act + sut.doWork(); + + // Assert + assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(A, 5) + ); + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(B, 5), + new ResourceAmount(C, 5) + ); + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldNotifyListenerWhenExtractingAllResourcesAndReachingCapacity( + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + networkStorage.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE); + + final Storage source = new LimitedStorageImpl(10); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 100L); + sut.setMode(StorageTransferMode.EXTRACT); + + // Act + sut.doWork(); + + // Assert + assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(B, 5) + ); + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(C, 5) + ); + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldRespectNormalizer( + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + + final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())); + source.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source.insert(A_ALTERNATIVE, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE); + source.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(0, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + sut.setFilterMode(FilterMode.ALLOW); + sut.setFilters(Set.of(A)); + sut.setNormalizer(resource -> resource == A || resource == A_ALTERNATIVE ? A : resource); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 5), + new ResourceAmount(A_ALTERNATIVE, 5) + ); + assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(B, 5), + new ResourceAmount(D, 5) + ); + verify(listener, times(1)).onTransferReady(0); + } + + @Test + void shouldNotTransferAtIndexHigherThanHalf( + @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage + ) { + // Arrange + networkStorage.addSource(new InMemoryStorageImpl()); + + final Storage source = new InMemoryStorageImpl(); + source.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE); + provider.set(sut.getSize() / 2, source); + + sut.setProvider(provider); + sut.setTransferQuotaProvider(storage -> 20L); + + // Act + sut.doWork(); + + // Assert + assertThat(networkStorage.getAll()).isEmpty(); + assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(A, 5) + ); + verify(listener, never()).onTransferReady(1); + } +} diff --git a/refinedstorage2-resource-api/src/main/java/com/refinedmods/refinedstorage2/api/resource/list/ResourceListImpl.java b/refinedstorage2-resource-api/src/main/java/com/refinedmods/refinedstorage2/api/resource/list/ResourceListImpl.java index d9a2c42b1..3ba666d75 100644 --- a/refinedstorage2-resource-api/src/main/java/com/refinedmods/refinedstorage2/api/resource/list/ResourceListImpl.java +++ b/refinedstorage2-resource-api/src/main/java/com/refinedmods/refinedstorage2/api/resource/list/ResourceListImpl.java @@ -11,11 +11,19 @@ import org.apiguardian.api.API; /** - * An implementation of a {@link ResourceList} that stores the resource entries in a {@link HashMap}. + * An implementation of a {@link ResourceList} that stores the resource entries in memory. */ @API(status = API.Status.STABLE, since = "2.0.0-milestone.1.2") public class ResourceListImpl implements ResourceList { - private final Map entries = new HashMap<>(); + private final Map entries; + + public ResourceListImpl(final Map entries) { + this.entries = entries; + } + + public ResourceListImpl() { + this(new HashMap<>()); + } @Override public OperationResult add(final ResourceKey resource, final long amount) { diff --git a/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/InMemoryStorageImpl.java b/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/InMemoryStorageImpl.java index d03e7aa85..dbdf0cb0b 100644 --- a/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/InMemoryStorageImpl.java +++ b/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/InMemoryStorageImpl.java @@ -15,9 +15,17 @@ */ @API(status = API.Status.STABLE, since = "2.0.0-milestone.1.0") public class InMemoryStorageImpl implements Storage { - private final ResourceList list = new ResourceListImpl(); + private final ResourceList list; private long stored; + public InMemoryStorageImpl(final ResourceList list) { + this.list = list; + } + + public InMemoryStorageImpl() { + this(new ResourceListImpl()); + } + @Override public long extract(final ResourceKey resource, final long amount, final Action action, final Actor actor) { ResourceAmount.validate(resource, amount); diff --git a/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/StateTrackedStorage.java b/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/StateTrackedStorage.java index 6e2da1903..70da6d0ce 100644 --- a/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/StateTrackedStorage.java +++ b/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/StateTrackedStorage.java @@ -17,6 +17,7 @@ public class StateTrackedStorage implements TrackedStorage, LimitedStorage { private final Storage delegate; @Nullable private final Listener listener; + private StorageState state; public StateTrackedStorage(final Storage delegate, @Nullable final Listener listener) {