diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java index 232001a4e..c291e14a6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java @@ -12,7 +12,6 @@ import org.bukkit.Particle.DustOptions; import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager; import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener; -import me.mrCookieSlime.Slimefun.SlimefunPlugin; import me.mrCookieSlime.Slimefun.api.Slimefun; /** @@ -63,33 +62,53 @@ public abstract class Network { protected Location regulator; private Queue nodeQueue = new ArrayDeque<>(); - protected Set connectedLocations = new HashSet<>(); - protected Set regulatorNodes = new HashSet<>(); - protected Set connectorNodes = new HashSet<>(); - protected Set terminusNodes = new HashSet<>(); + private final NetworkManager manager; + protected final Set connectedLocations = new HashSet<>(); + protected final Set regulatorNodes = new HashSet<>(); + protected final Set connectorNodes = new HashSet<>(); + protected final Set terminusNodes = new HashSet<>(); - protected Network(Location regulator) { + protected Network(NetworkManager manager, Location regulator) { + this.manager = manager; this.regulator = regulator; + connectedLocations.add(regulator); nodeQueue.add(regulator.clone()); } + /** + * This returns the size of this {@link Network}. It is equivalent to the amount + * of {@link Location Locations} connected to this {@link Network}. + * + * @return The size of this {@link Network} + */ + public int getSize() { + return regulatorNodes.size() + connectorNodes.size() + terminusNodes.size(); + } + protected void addLocationToNetwork(Location l) { if (connectedLocations.contains(l)) { return; } connectedLocations.add(l.clone()); - handleLocationUpdate(l); + markDirty(l); } - public void handleLocationUpdate(Location l) { + /** + * This method marks the given {@link Location} as dirty and adds it to a {@link Queue} + * to handle this update. + * + * @param l + * The {@link Location} to update + */ + public void markDirty(Location l) { if (regulator.equals(l)) { - SlimefunPlugin.getNetworkManager().unregisterNetwork(this); - return; + manager.unregisterNetwork(this); + } + else { + nodeQueue.add(l.clone()); } - - nodeQueue.add(l.clone()); } /** @@ -118,7 +137,7 @@ public abstract class Network { } private void discoverStep() { - int maxSteps = SlimefunPlugin.getNetworkManager().getMaxSize(); + int maxSteps = manager.getMaxSize(); int steps = 0; while (nodeQueue.peek() != null) { @@ -129,7 +148,7 @@ public abstract class Network { if (classification != currentAssignment) { if (currentAssignment == NetworkComponent.REGULATOR || currentAssignment == NetworkComponent.CONNECTOR) { // Requires a complete rebuild of the network, so we just throw the current one away. - SlimefunPlugin.getNetworkManager().unregisterNetwork(this); + manager.unregisterNetwork(this); return; } else if (currentAssignment == NetworkComponent.TERMINUS) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java index d9add5947..94710903a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java @@ -3,6 +3,7 @@ package io.github.thebusybiscuit.slimefun4.core.networks; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import org.bukkit.Location; import org.bukkit.Server; @@ -21,7 +22,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListen * @see NetworkListener * */ -public final class NetworkManager { +public class NetworkManager { private final int maxNodes; private final List networks = new LinkedList<>(); @@ -29,11 +30,11 @@ public final class NetworkManager { /** * This creates a new {@link NetworkManager} with the given capacity. * - * @param capacity + * @param maxStepSize * The maximum amount of nodes a {@link Network} can have */ - public NetworkManager(int capacity) { - maxNodes = capacity; + public NetworkManager(int maxStepSize) { + maxNodes = maxStepSize; } /** @@ -55,14 +56,14 @@ public final class NetworkManager { return networks; } - public T getNetworkFromLocation(Location l, Class type) { + public Optional getNetworkFromLocation(Location l, Class type) { for (Network network : networks) { if (type.isInstance(network) && network.connectsTo(l)) { - return type.cast(network); + return Optional.of(type.cast(network)); } } - return null; + return Optional.empty(); } public List getNetworksFromLocation(Location l, Class type) { @@ -87,7 +88,7 @@ public final class NetworkManager { public void handleAllNetworkLocationUpdate(Location l) { for (Network n : getNetworksFromLocation(l, Network.class)) { - n.handleLocationUpdate(l); + n.markDirty(l); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java index 9d8fe3203..e773bb3f1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java @@ -36,18 +36,20 @@ public class CargoNet extends ChestTerminalNetwork { private int tickDelayThreshold = 0; public static CargoNet getNetworkFromLocation(Location l) { - return SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, CargoNet.class); + return SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, CargoNet.class).orElse(null); } public static CargoNet getNetworkFromLocationOrCreate(Location l) { - CargoNet cargoNetwork = getNetworkFromLocation(l); + Optional cargoNetwork = SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, CargoNet.class); - if (cargoNetwork == null) { - cargoNetwork = new CargoNet(l); - SlimefunPlugin.getNetworkManager().registerNetwork(cargoNetwork); + if (cargoNetwork.isPresent()) { + return cargoNetwork.get(); + } + else { + CargoNet network = new CargoNet(l); + SlimefunPlugin.getNetworkManager().registerNetwork(network); + return network; } - - return cargoNetwork; } protected CargoNet(Location l) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java index c52e11730..fc0762810 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java @@ -27,6 +27,7 @@ import io.github.thebusybiscuit.slimefun4.api.network.Network; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; +import me.mrCookieSlime.Slimefun.SlimefunPlugin; import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu; @@ -54,7 +55,7 @@ abstract class ChestTerminalNetwork extends Network { private final Set itemRequests = new HashSet<>(); protected ChestTerminalNetwork(Location regulator) { - super(regulator); + super(SlimefunPlugin.getNetworkManager(), regulator); } protected static Optional getAttachedBlock(Block block) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java index 6ebb39f1b..36b223706 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java @@ -1,6 +1,7 @@ package io.github.thebusybiscuit.slimefun4.core.networks.energy; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import org.bukkit.Location; @@ -59,19 +60,17 @@ public class EnergyNet extends Network { return EnergyNetComponentType.NONE; } - public static EnergyNet getNetworkFromLocation(Location l) { - return SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, EnergyNet.class); - } - public static EnergyNet getNetworkFromLocationOrCreate(Location l) { - EnergyNet energyNetwork = getNetworkFromLocation(l); + Optional cargoNetwork = SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, EnergyNet.class); - if (energyNetwork == null) { - energyNetwork = new EnergyNet(l); - SlimefunPlugin.getNetworkManager().registerNetwork(energyNetwork); + if (cargoNetwork.isPresent()) { + return cargoNetwork.get(); + } + else { + EnergyNet network = new EnergyNet(l); + SlimefunPlugin.getNetworkManager().registerNetwork(network); + return network; } - - return energyNetwork; } private final Set generators = new HashSet<>(); @@ -79,7 +78,7 @@ public class EnergyNet extends Network { private final Set consumers = new HashSet<>(); protected EnergyNet(Location l) { - super(l); + super(SlimefunPlugin.getNetworkManager(), l); } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/NetworkListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/NetworkListener.java index 571903964..c079d8ba7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/NetworkListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/NetworkListener.java @@ -23,8 +23,8 @@ public class NetworkListener implements Listener { private final NetworkManager manager; - public NetworkListener(SlimefunPlugin plugin) { - manager = SlimefunPlugin.getNetworkManager(); + public NetworkListener(SlimefunPlugin plugin, NetworkManager manager) { + this.manager = manager; plugin.getServer().getPluginManager().registerEvents(this, plugin); } @@ -34,7 +34,7 @@ public class NetworkListener implements Listener { } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlaceBreak(BlockPlaceEvent e) { + public void onBlockPlace(BlockPlaceEvent e) { manager.handleAllNetworkLocationUpdate(e.getBlock().getLocation()); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/SlimefunStartupTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/SlimefunStartupTask.java index 1687dc98f..abfd58f4c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/SlimefunStartupTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/SlimefunStartupTask.java @@ -62,7 +62,7 @@ public class SlimefunStartupTask implements Runnable { } if (isEnabled("ENERGY_REGULATOR", "CARGO_MANAGER")) { - new NetworkListener(plugin); + new NetworkListener(plugin, SlimefunPlugin.getNetworkManager()); } } diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestNetworkListener.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestNetworkListener.java new file mode 100644 index 000000000..610f1c218 --- /dev/null +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestNetworkListener.java @@ -0,0 +1,70 @@ +package io.github.thebusybiscuit.slimefun4.tests.listeners; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.BlockState; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.thebusybiscuit.slimefun4.api.network.Network; +import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener; +import me.mrCookieSlime.Slimefun.SlimefunPlugin; + +public class TestNetworkListener { + + private static SlimefunPlugin plugin; + private static NetworkListener listener; + private static NetworkManager manager = new NetworkManager(80); + private static ServerMock server; + + @BeforeAll + public static void load() { + server = MockBukkit.mock(); + plugin = MockBukkit.load(SlimefunPlugin.class); + listener = new NetworkListener(plugin, manager); + } + + @AfterAll + public static void unload() { + MockBukkit.unmock(); + } + + @Test + public void testBlockBreak() { + World world = server.addSimpleWorld("Simple Network Listener World"); + Location l = new Location(world, 3000, 120, -500); + + Network network = Mockito.mock(Network.class); + Mockito.when(network.connectsTo(l)).thenReturn(true); + manager.registerNetwork(network); + + listener.onBlockBreak(new BlockBreakEvent(l.getBlock(), server.addPlayer())); + Mockito.verify(network).markDirty(l); + } + + @Test + public void testBlockPlace() { + World world = server.addSimpleWorld("Simple Network Listener World"); + Location l = new Location(world, 3000, 120, -500); + Location l2 = new Location(world, 3000, 121, -500); + + Network network = Mockito.mock(Network.class); + Mockito.when(network.connectsTo(l)).thenReturn(true); + manager.registerNetwork(network); + + BlockState state = Mockito.mock(BlockState.class); + listener.onBlockPlace(new BlockPlaceEvent(l.getBlock(), state, l2.getBlock(), new ItemStack(Material.AIR), server.addPlayer(), true, EquipmentSlot.HAND)); + Mockito.verify(network).markDirty(l); + } + +} diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/networks/TestNetworkManager.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/networks/TestNetworkManager.java new file mode 100644 index 000000000..39c4839fa --- /dev/null +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/networks/TestNetworkManager.java @@ -0,0 +1,169 @@ +package io.github.thebusybiscuit.slimefun4.tests.networks; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.bukkit.Location; +import org.bukkit.World; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.thebusybiscuit.slimefun4.api.network.Network; +import io.github.thebusybiscuit.slimefun4.api.network.NetworkComponent; +import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager; +import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet; + +public class TestNetworkManager { + + private static ServerMock server; + + @BeforeAll + public static void load() { + server = MockBukkit.mock(); + } + + @AfterAll + public static void unload() { + MockBukkit.unmock(); + } + + @Test + public void testGetMaxNetworkSize() { + int size = 50; + NetworkManager manager = new NetworkManager(size); + + Assertions.assertEquals(size, manager.getMaxSize()); + } + + @Test + public void testGetNetworkList() { + NetworkManager manager = new NetworkManager(10); + World world = server.addSimpleWorld("Simple Network World"); + Location loc = new Location(world, 0, 100, 0); + + Network network = new DummyNetwork(manager, loc, 10, new HashMap<>()); + + Assertions.assertFalse(manager.getNetworkList().contains(network)); + manager.registerNetwork(network); + Assertions.assertTrue(manager.getNetworkList().contains(network)); + manager.unregisterNetwork(network); + Assertions.assertFalse(manager.getNetworkList().contains(network)); + } + + @Test + public void testDirtyRegulatorUnregistersNetwork() { + World world = server.addSimpleWorld("Simple Network World"); + Location loc = new Location(world, 0, 100, 0); + + NetworkManager manager = Mockito.mock(NetworkManager.class); + Network network = new DummyNetwork(manager, loc, 10, new HashMap<>()); + network.markDirty(loc); + + Mockito.verify(manager).unregisterNetwork(network); + } + + @Test + public void testGetNetworkAtLocation() { + NetworkManager manager = new NetworkManager(10); + World world = server.addSimpleWorld("Simple Network World"); + Location loc = new Location(world, 0, 100, 0); + Location loc2 = new Location(world, 0, 200, 0); + + Network network = new DummyNetwork(manager, loc, 10, new HashMap<>()); + + Assertions.assertFalse(manager.getNetworkFromLocation(loc, DummyNetwork.class).isPresent()); + + manager.registerNetwork(network); + + Optional optional = manager.getNetworkFromLocation(loc, DummyNetwork.class); + Assertions.assertTrue(optional.isPresent()); + Assertions.assertEquals(network, optional.get()); + + Assertions.assertFalse(manager.getNetworkFromLocation(loc2, DummyNetwork.class).isPresent()); + Assertions.assertFalse(manager.getNetworkFromLocation(loc, CargoNet.class).isPresent()); + } + + @Test + public void testGetNetworksAtLocation() { + NetworkManager manager = new NetworkManager(10); + World world = server.addSimpleWorld("Simple Network World"); + Location loc = new Location(world, 0, 100, 0); + Location loc2 = new Location(world, 0, 200, 0); + + Network network = new DummyNetwork(manager, loc, 10, new HashMap<>()); + manager.registerNetwork(network); + + Assertions.assertFalse(manager.getNetworksFromLocation(loc2, DummyNetwork.class).contains(network)); + Assertions.assertFalse(manager.getNetworksFromLocation(loc, CargoNet.class).contains(network)); + Assertions.assertTrue(manager.getNetworksFromLocation(loc, DummyNetwork.class).contains(network)); + } + + @Test + public void testSingleNodeNetwork() { + NetworkManager manager = new NetworkManager(1); + World world = server.addSimpleWorld("Simple Network World"); + Location loc = new Location(world, 0, 100, 0); + + Network network = new DummyNetwork(manager, loc, 1, new HashMap<>()); + network.tick(); + + Assertions.assertEquals(1, network.getSize()); + Assertions.assertEquals(NetworkComponent.REGULATOR, network.classifyLocation(loc)); + } + + @Test + public void testCornerConnection() { + NetworkManager manager = new NetworkManager(100); + World world = server.addSimpleWorld("Simple Network World"); + Map map = new HashMap<>(); + + Location loc = new Location(world, 0, 100, 0); + + Location loc2 = new Location(world, 0, 100, 2); + map.put(loc2, NetworkComponent.CONNECTOR); + + Location loc3 = new Location(world, 2, 100, 2); + map.put(loc3, NetworkComponent.CONNECTOR); + + Network network = new DummyNetwork(manager, loc, 3, map); + network.tick(); + + Assertions.assertEquals(3, network.getSize()); + } + + private class DummyNetwork extends Network { + + private final int range; + private final Map locations; + + protected DummyNetwork(NetworkManager manager, Location regulator, int range, Map locations) { + super(manager, regulator); + this.range = range; + this.locations = locations; + } + + @Override + public int getRange() { + return range; + } + + @Override + public NetworkComponent classifyLocation(Location l) { + if (l.equals(regulator)) return NetworkComponent.REGULATOR; + return locations.get(l); + } + + @Override + public void onClassificationChange(Location l, NetworkComponent from, NetworkComponent to) { + // Do nothing + } + + } + +}