Skip to content

Commit

Permalink
feat: relay
Browse files Browse the repository at this point in the history
  • Loading branch information
raoulvdberge committed May 5, 2024
1 parent 3faf3ae commit 43038e4
Show file tree
Hide file tree
Showing 208 changed files with 4,315 additions and 63 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ eclipse/
.idea/
!.idea/dictionaries/refinedstorage2.xml
!.idea/icon.png
out/
/bin/
logs/
.cache/
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Relay

### Changed

- The Detector, Network Receiver, Network Transmitter and Security Manager will now always connect regardless of color.
- The Relay now has a "pass-through" mode. By default, pass-through is on, which means that when the Relay is active, the network signal from the input network will be passed through as-is to the output side.
- When the "pass-through" mode on the Relay is off, the network signal from the input network will no longer be passed through to the output side, but you can choose to pass the energy buffer, security settings or (specific) storage resources of the input network to the output network.

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ public interface EnergyProvider {
long getCapacity();

long extract(long amount);

default boolean contains(EnergyProvider energyProvider) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ default SecurityDecision isAllowed(Permission permission) {
return SecurityDecision.PASS;
}

default boolean isActive() {
default boolean isProviderActive() {
return true;
}

default boolean contains(SecurityNetworkComponent component) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import org.apiguardian.api.API;

@API(status = API.Status.STABLE, since = "2.0.0-milestone.3.5")
@FunctionalInterface
public interface SecurityNetworkComponent extends NetworkComponent {
boolean isAllowed(Permission permission, SecurityActor actor);

boolean contains(SecurityNetworkComponent component);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.refinedmods.refinedstorage2.api.network.NetworkComponent;
import com.refinedmods.refinedstorage2.api.storage.Actor;
import com.refinedmods.refinedstorage2.api.storage.Storage;
import com.refinedmods.refinedstorage2.api.storage.TrackedResourceAmount;
import com.refinedmods.refinedstorage2.api.storage.channel.StorageChannel;

Expand All @@ -12,4 +13,6 @@
@API(status = API.Status.STABLE, since = "2.0.0-milestone.1.1")
public interface StorageNetworkComponent extends NetworkComponent, StorageChannel {
List<TrackedResourceAmount> getResources(Class<? extends Actor> actorType);

boolean contains(Storage storage);
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ private void splitNetworks(final ConnectionProvider connectionProvider,

connectionProvider.sortDeterministically(removedEntries).stream().sorted(HIGHEST_PRIORITY_FIRST).forEach(e -> {
if (e.getNode().getNetwork() == null) {
throw new IllegalStateException("Network of resulting removed node cannot be empty");
throw new IllegalStateException(
"Network of resulting removed node (" + e.getNode() + ") cannot be empty"
);
}
e.getNode().getNetwork().removeContainer(e);
e.getNode().setNetwork(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public void onContainerRemoved(final NetworkNodeContainer container) {
public long getStored() {
long stored = 0;
for (final EnergyProvider provider : providers) {
if (provider.contains(this)) {
continue;
}
if (stored + provider.getStored() < 0) {
return Long.MAX_VALUE;
}
Expand All @@ -40,6 +43,9 @@ public long getStored() {
public long getCapacity() {
long capacity = 0;
for (final EnergyProvider provider : providers) {
if (provider.contains(this)) {
continue;
}
if (capacity + provider.getCapacity() < 0) {
return Long.MAX_VALUE;
}
Expand All @@ -52,11 +58,24 @@ public long getCapacity() {
public long extract(final long amount) {
long extracted = 0;
for (final EnergyProvider provider : providers) {
if (provider.contains(this)) {
continue;
}
extracted += provider.extract(amount - extracted);
if (extracted == amount) {
break;
}
}
return extracted;
}

@Override
public boolean contains(final EnergyProvider energyProvider) {
for (final EnergyProvider provider : providers) {
if (provider.contains(energyProvider)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,9 @@ public SecurityDecision isAllowed(final Permission permission) {
}
return delegate.isAllowed(permission);
}

@Override
public boolean isProviderActive() {
return isActive();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public void onContainerRemoved(final NetworkNodeContainer container) {
@Override
public boolean isAllowed(final Permission permission, final SecurityActor actor) {
final Set<SecurityDecisionProvider> activeProviders = providers.stream()
.filter(SecurityDecisionProvider::isActive)
.filter(SecurityDecisionProvider::isProviderActive)
.filter(provider -> !provider.contains(this))
.collect(Collectors.toSet());
if (activeProviders.isEmpty()) {
return defaultPolicy.isAllowed(permission);
Expand All @@ -57,6 +58,16 @@ public boolean isAllowed(final Permission permission, final SecurityActor actor)
return tryFallback(permission, activeProviders);
}

@Override
public boolean contains(final SecurityNetworkComponent component) {
for (final SecurityDecisionProvider provider : providers) {
if (provider.contains(component)) {
return true;
}
}
return false;
}

private boolean tryFallback(final Permission permission, final Set<SecurityDecisionProvider> activeProviders) {
final Set<SecurityDecision> decisions = activeProviders.stream().map(provider ->
CoreValidations.validateNotNull(provider.isAllowed(permission), "Decision cannot be null")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@ public List<TrackedResourceAmount> getResources(final Class<? extends Actor> act
findTrackedResourceByActorType(resourceAmount.getResource(), actorType).orElse(null)
)).toList();
}

@Override
public boolean contains(final Storage storage) {
return this.storage.contains(storage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.refinedmods.refinedstorage2.api.core.Action;
import com.refinedmods.refinedstorage2.api.network.energy.EnergyNetworkComponent;
import com.refinedmods.refinedstorage2.api.network.energy.EnergyProvider;
import com.refinedmods.refinedstorage2.api.network.energy.EnergyStorage;
import com.refinedmods.refinedstorage2.api.network.impl.node.controller.ControllerNetworkNode;
import com.refinedmods.refinedstorage2.api.network.impl.storage.AbstractNetworkNode;
import com.refinedmods.refinedstorage2.api.network.node.container.NetworkNodeContainer;

import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -172,10 +174,75 @@ void shouldNotExceedLongMaxWithCapacityOrStored() {
assertThat(capacity).isEqualTo(Long.MAX_VALUE);
}

@Test
void shouldDetectCycles() {
// Arrange
final EnergyNetworkComponent a = new EnergyNetworkComponentImpl();
final EnergyNetworkComponent b = new EnergyNetworkComponentImpl();
b.onContainerAdded(proxyContainer(a));

// Act
a.onContainerAdded(proxyContainer(b));

final EnergyStorageImpl energyStorage = new EnergyStorageImpl(10);
energyStorage.receive(5, Action.EXECUTE);
a.onContainerAdded(container(energyStorage));

final long extracted = a.extract(1);

// Assert
assertThat(a.getStored()).isEqualTo(4);
assertThat(a.getCapacity()).isEqualTo(10);
assertThat(extracted).isEqualTo(1);

assertThat(b.getStored()).isZero();
assertThat(b.getCapacity()).isZero();
assertThat(b.extract(1)).isZero();
}

private NetworkNodeContainer proxyContainer(final EnergyNetworkComponent component) {
final ProxyEnergyProvider proxyEnergyProvider = new ProxyEnergyProvider(component);
proxyEnergyProvider.setActive(true);
return () -> proxyEnergyProvider;
}

private NetworkNodeContainer container(final EnergyStorage energyStorage) {
final ControllerNetworkNode controller = new ControllerNetworkNode();
controller.setEnergyStorage(energyStorage);
controller.setActive(true);
return () -> controller;
}

private static class ProxyEnergyProvider extends AbstractNetworkNode implements EnergyProvider {
private final EnergyNetworkComponent delegate;

private ProxyEnergyProvider(final EnergyNetworkComponent delegate) {
this.delegate = delegate;
}

@Override
public long getStored() {
return delegate.getStored();
}

@Override
public long getCapacity() {
return delegate.getCapacity();
}

@Override
public long extract(final long amount) {
return delegate.extract(amount);
}

@Override
public boolean contains(final EnergyProvider energyProvider) {
return energyProvider == delegate || delegate.contains(energyProvider);
}

@Override
public long getEnergyUsage() {
return 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.refinedmods.refinedstorage2.api.network.impl.security;

import com.refinedmods.refinedstorage2.api.network.impl.node.security.SecurityDecisionProviderProxyNetworkNode;
import com.refinedmods.refinedstorage2.api.network.impl.storage.AbstractNetworkNode;
import com.refinedmods.refinedstorage2.api.network.security.Permission;
import com.refinedmods.refinedstorage2.api.network.security.SecurityActor;
import com.refinedmods.refinedstorage2.api.network.security.SecurityDecision;
import com.refinedmods.refinedstorage2.api.network.security.SecurityDecisionProvider;
import com.refinedmods.refinedstorage2.api.network.security.SecurityNetworkComponent;
import com.refinedmods.refinedstorage2.api.network.security.SecurityPolicy;
Expand Down Expand Up @@ -179,6 +181,25 @@ void shouldClearPolicies() {
assertThat(sut.isAllowed(TestPermissions.OTHER2, TestActors.A)).isTrue();
}

@Test
void shouldDetectCycles() {
// Arrange
final SecurityNetworkComponent a = new SecurityNetworkComponentImpl(policy(TestPermissions.ALLOW_BY_DEFAULT));

final SecurityNetworkComponent b = new SecurityNetworkComponentImpl(policy(TestPermissions.OTHER));
b.onContainerAdded(() -> new ProxySecurityDecisionProvider(a));

// Act
a.onContainerAdded(() -> new ProxySecurityDecisionProvider(b));

// Assert
assertThat(a.isAllowed(TestPermissions.ALLOW_BY_DEFAULT, TestActors.A)).isTrue();
assertThat(a.isAllowed(TestPermissions.OTHER, TestActors.A)).isFalse();

assertThat(b.isAllowed(TestPermissions.ALLOW_BY_DEFAULT, TestActors.A)).isFalse();
assertThat(b.isAllowed(TestPermissions.OTHER, TestActors.A)).isTrue();
}

private static SecurityDecisionProviderProxyNetworkNode createNode(final SecurityDecisionProvider provider) {
return createNode(provider, true);
}
Expand All @@ -201,4 +222,27 @@ enum TestActors implements SecurityActor {
private SecurityPolicy policy(final Permission... permissions) {
return new SecurityPolicy(Set.of(permissions));
}

private static class ProxySecurityDecisionProvider extends AbstractNetworkNode implements SecurityDecisionProvider {
private final SecurityNetworkComponent delegate;

private ProxySecurityDecisionProvider(final SecurityNetworkComponent delegate) {
this.delegate = delegate;
}

@Override
public long getEnergyUsage() {
return 0;
}

@Override
public SecurityDecision isAllowed(final Permission permission, final SecurityActor actor) {
return delegate.isAllowed(permission, actor) ? SecurityDecision.ALLOW : SecurityDecision.DENY;
}

@Override
public boolean contains(final SecurityNetworkComponent component) {
return component == delegate || delegate.contains(component);
}
}
}
Loading

0 comments on commit 43038e4

Please sign in to comment.