diff --git a/src/main/java/arzumify/polyenergy/api/EnergyReceiver.java b/src/main/java/arzumify/polyenergy/api/EnergyReceiver.java index 34e9500..729e1bb 100644 --- a/src/main/java/arzumify/polyenergy/api/EnergyReceiver.java +++ b/src/main/java/arzumify/polyenergy/api/EnergyReceiver.java @@ -5,6 +5,8 @@ public interface EnergyReceiver { /** * When called by a provider, indicates that the provider exists and is ready to have energy extracted from. * Do not attempt to call {@link EnergyProvider#extract(long, EnergyReceiver)} on any provider until this method is called. + * This method may be called multiple times by the same provider. Please ignore any duplicate calls, as they may occur in the MatchMaking implementation. + * We recommend using a Set to store the providers that are ready to provide energy. * * @param provider The provider that exists and is ready to provide energy to this receiver. */ diff --git a/src/main/java/arzumify/polyenergy/impl/CoordinateMatchMaker.java b/src/main/java/arzumify/polyenergy/impl/CoordinateMatchMaker.java index 95a0f6a..a2815cb 100644 --- a/src/main/java/arzumify/polyenergy/impl/CoordinateMatchMaker.java +++ b/src/main/java/arzumify/polyenergy/impl/CoordinateMatchMaker.java @@ -1,7 +1,8 @@ package arzumify.polyenergy.impl; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; // It's slightly more optimized now, as in it could realistically run in Minecraft. // Reset: O(1) constant time, we're just deleting everything @@ -13,9 +14,9 @@ import java.util.HashMap; // If more optimisation is needed we can try to perform Vector math in a more efficient way, but I think this is good enough. public class CoordinateMatchMaker { - private static final ArrayList providers = new ArrayList<>(); - private static final ArrayList receivers = new ArrayList<>(); - private static final HashMap> matches = new HashMap<>(); + private static final CopyOnWriteArrayList providers = new CopyOnWriteArrayList<>(); + private static final CopyOnWriteArrayList receivers = new CopyOnWriteArrayList<>(); + private static final ConcurrentHashMap> matches = new ConcurrentHashMap<>(); public static void reset() { providers.clear(); @@ -25,7 +26,7 @@ public class CoordinateMatchMaker { public static void addProvider(ProviderDetails provider) { providers.add(provider); - matches.put(provider, new ArrayList<>()); + matches.put(provider, ConcurrentHashMap.newKeySet()); // Search for receivers within range of this provider for (ReceiverDetails receiver : receivers) { if (provider.isInRange(receiver.pointsOfPresence())) { @@ -48,8 +49,10 @@ public class CoordinateMatchMaker { public static void removeProvider(ProviderDetails provider) { providers.remove(provider); - for (ReceiverDetails receiver : matches.get(provider)) { - receiver.receiver().unready(provider.provider()); + if (matches.get(provider) != null) { + for (ReceiverDetails receiver : matches.get(provider)) { + receiver.receiver().unready(provider.provider()); + } } matches.remove(provider); } diff --git a/src/main/java/arzumify/polyenergy/util/SimpleBattery.java b/src/main/java/arzumify/polyenergy/util/SimpleBattery.java index 1cf6708..b669352 100644 --- a/src/main/java/arzumify/polyenergy/util/SimpleBattery.java +++ b/src/main/java/arzumify/polyenergy/util/SimpleBattery.java @@ -15,19 +15,22 @@ import net.minecraft.util.math.Vec3i; import net.minecraft.world.World; import java.util.ArrayList; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * A simple battery that can provide and receive energy. Has a range of 1 block in all directions (directly adjacent blocks). */ +@SuppressWarnings("unused") public class SimpleBattery extends BlockEntity implements EnergyProvider, EnergyReceiver, ServerBlockEntityEvents.Unload, ServerBlockEntityEvents.Load { private final long capacity; private final long inputRate; private final long outputRate; private final ProviderDetails providerDetails = ProviderDetails.NewSimple(pos, this); private final ReceiverDetails receiverDetails = ReceiverDetails.NewSimple(pos, this); - private final CopyOnWriteArrayList providers = new CopyOnWriteArrayList<>(); + private final Set providers = ConcurrentHashMap.newKeySet(); private long energy = 0; + private boolean providing = false; public SimpleBattery(BlockEntityType type, BlockPos pos, BlockState state, long capacity, long inputRate, long outputRate) { super(type, pos, state); @@ -38,11 +41,35 @@ public class SimpleBattery extends BlockEntity implements EnergyProvider, Energy CoordinateMatchMaker.addReceiver(receiverDetails); } + public static void tick(World ignoredWorld, BlockPos ignoredPos, BlockState ignoredState, SimpleBattery battery) { + var leftToFill = Math.min(battery.inputRate, battery.capacity - battery.energy); + for (EnergyProvider provider : battery.providers) { + // Less than zero shouldn't be possible, but just in case... + if (leftToFill <= 0) { + break; + } + long extracted = provider.extract(leftToFill, battery); + battery.energy += extracted; + leftToFill -= extracted; + } + if (!battery.providing && battery.energy > 0) { + CoordinateMatchMaker.addProvider(battery.providerDetails); + battery.providing = true; + } + } + @Override public long extract(long amount, EnergyReceiver receiver) { - long extracted = Math.min(Math.min(outputRate, amount), energy); - energy -= extracted; - return extracted; + if (providing) { + long extracted = Math.min(Math.min(outputRate, amount), energy); + energy -= extracted; + if (energy == 0) { + CoordinateMatchMaker.removeProvider(providerDetails); + } + return extracted; + } else { + return 0; + } } @Override @@ -55,19 +82,6 @@ public class SimpleBattery extends BlockEntity implements EnergyProvider, Energy providers.add(provider); } - public static void tick(World world, BlockPos pos, BlockState state, SimpleBattery battery) { - var leftToFill = Math.min(battery.inputRate, battery.capacity - battery.energy); - for (EnergyProvider provider : battery.providers) { - // Less than zero shouldn't be possible, but just in case... - if (leftToFill <= 0) { - break; - } - long extracted = provider.extract(leftToFill, battery); - battery.energy += extracted; - leftToFill -= extracted; - } - } - @Override public void unready(EnergyProvider provider) { providers.remove(provider); @@ -75,7 +89,10 @@ public class SimpleBattery extends BlockEntity implements EnergyProvider, Energy @Override public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) { - CoordinateMatchMaker.addProvider(providerDetails); + if (energy > 0) { + CoordinateMatchMaker.addProvider(providerDetails); + providing = true; + } CoordinateMatchMaker.addReceiver(receiverDetails); } diff --git a/src/main/java/arzumify/polyenergy/util/SimpleCable.java b/src/main/java/arzumify/polyenergy/util/SimpleCable.java new file mode 100644 index 0000000..787bb72 --- /dev/null +++ b/src/main/java/arzumify/polyenergy/util/SimpleCable.java @@ -0,0 +1,88 @@ +package arzumify.polyenergy.util; + +import arzumify.polyenergy.api.EnergyProvider; +import arzumify.polyenergy.api.EnergyReceiver; +import arzumify.polyenergy.impl.CoordinateMatchMaker; +import arzumify.polyenergy.impl.ProviderDetails; +import arzumify.polyenergy.impl.ReceiverDetails; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.World; + +import java.util.ArrayList; + +// Cables can have ONE provider and MANY receivers. +@SuppressWarnings("unused") +public class SimpleCable extends BlockEntity implements EnergyProvider, EnergyReceiver, ServerBlockEntityEvents.Unload, ServerBlockEntityEvents.Load { + private final long capacity; + private final long inputRate; + private final long outputRate; + private final ProviderDetails providerDetails = ProviderDetails.NewSimple(pos, this); + private final ReceiverDetails receiverDetails = ReceiverDetails.NewSimple(pos, this); + private EnergyProvider currentProvider; + private long energy = 0; + + public SimpleCable(BlockEntityType type, BlockPos pos, BlockState state, long capacity, long inputRate, long outputRate) { + super(type, pos, state); + this.capacity = capacity; + this.inputRate = inputRate; + this.outputRate = outputRate; + CoordinateMatchMaker.addReceiver(receiverDetails); + } + + public static void tick(World ignoredWorld, BlockPos ignoredPos, BlockState ignoredState, SimpleCable cable) { + if (cable.currentProvider != null && cable.energy < cable.capacity) { + cable.energy += cable.currentProvider.extract(Math.min(cable.capacity - cable.energy, cable.inputRate), cable); + } + } + + @Override + public long extract(long amount, EnergyReceiver receiver) { + long extracted = Math.min(Math.min(outputRate, amount), energy); + energy -= extracted; + if (energy == 0) { + CoordinateMatchMaker.removeProvider(providerDetails); + } + return extracted; + } + + @Override + public void exists(EnergyReceiver receiver, ArrayList pointsOfPresence) { + if (currentProvider != null) { + receiver.ready(currentProvider); + } + } + + @Override + public void ready(EnergyProvider provider) { + if (currentProvider == null) { + currentProvider = provider; + CoordinateMatchMaker.addProvider(providerDetails); + } + } + + @Override + public void unready(EnergyProvider provider) { + if (currentProvider == provider) { + currentProvider = null; + CoordinateMatchMaker.removeProvider(providerDetails); + } + } + + @Override + public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) { + CoordinateMatchMaker.addReceiver(receiverDetails); + } + + @Override + public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) { + CoordinateMatchMaker.removeProvider(providerDetails); + CoordinateMatchMaker.removeReceiver(receiverDetails); + currentProvider = null; + } +} diff --git a/src/test/java/EnergyTests.java b/src/test/java/EnergyTests.java index dba9ad8..e00234c 100644 --- a/src/test/java/EnergyTests.java +++ b/src/test/java/EnergyTests.java @@ -2,10 +2,20 @@ import arzumify.polyenergy.impl.CoordinateMatchMaker; import net.minecraft.util.math.Vec3i; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +@Timeout(1) public class EnergyTests { + public static void assertWithError(double expected, double actual, double error) { + assertTrue(expected - error <= actual && actual <= expected + error); + } + + public static void assertWithError(double expected, double actual) { + assertWithError(expected, actual, 1); + } + @BeforeEach public void setup() { // Delete all batteries @@ -15,6 +25,7 @@ public class EnergyTests { @Test public void testEnergyTransfer() { + System.out.println("Energy transfer test"); // Create two batteries with coordinates (0, 0, 0) and (1, 0, 0) // Battery1 should be able to provide energy to battery2 var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 0, 1, "Battery1"); @@ -34,12 +45,13 @@ public class EnergyTests { System.out.println("Battery2 energy: " + battery2.energy); // Battery1 should have lost 10 energy and battery2 should have gained 10 energy - assertEquals(6, battery1.energy); - assertEquals(10, battery2.energy); + assertWithError(6, battery1.energy); + assertWithError(10, battery2.energy); } @Test public void testUnloadBattery1() { + System.out.println("Unloading battery1 test"); // Create two batteries with coordinates (0, 0, 0) and (1, 0, 0) // Battery1 should be able to provide energy to battery2 var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 0, 1, "Battery1"); @@ -60,7 +72,6 @@ public class EnergyTests { // Tick 5 more times for (int i = 0; i < 5; i++) { - SimpleCodeOnlyBattery.tick(battery1); SimpleCodeOnlyBattery.tick(battery2); } @@ -68,13 +79,14 @@ public class EnergyTests { System.out.println("Battery1 energy: " + battery1.energy); System.out.println("Battery2 energy: " + battery2.energy); - // Battery1 should have lost 5 energy and battery2 should have gained 5 energy - assertEquals(11, battery1.energy); - assertEquals(5, battery2.energy); + // Battery1 should have lost 6 energy and battery2 should have gained 6 energy (±1) + assertWithError(10, battery1.energy); + assertWithError(6, battery2.energy); } @Test public void testUnloadBattery2() { + System.out.println("Unloading battery2 test"); // Create two batteries with coordinates (0, 0, 0) and (1, 0, 0) // Battery1 should be able to provide energy to battery2 var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 0, 1, "Battery1"); @@ -104,12 +116,13 @@ public class EnergyTests { System.out.println("Battery2 energy: " + battery2.energy); // Battery1 should have lost 5 energy and battery2 should have gained 5 energy - assertEquals(11, battery1.energy); - assertEquals(5, battery2.energy); + assertWithError(11, battery1.energy); + assertWithError(5, battery2.energy); } @Test public void testUnloadThenReloadBattery1() { + System.out.println("Unloading then reloading battery1 test"); // Create two batteries with coordinates (0, 0, 0) and (1, 0, 0) // Battery1 should be able to provide energy to battery2 var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 0, 1, "Battery1"); @@ -130,7 +143,6 @@ public class EnergyTests { // Tick 5 more times for (int i = 0; i < 5; i++) { - SimpleCodeOnlyBattery.tick(battery1); SimpleCodeOnlyBattery.tick(battery2); } @@ -149,12 +161,13 @@ public class EnergyTests { System.out.println("Battery2 energy: " + battery2.energy); // Battery1 should have lost 10 energy and battery2 should have gained 10 energy - assertEquals(6, battery1.energy); - assertEquals(10, battery2.energy); + assertWithError(6, battery1.energy); + assertWithError(10, battery2.energy); } @Test public void testUnloadThenReloadBattery2() { + System.out.println("Unloading then reloading battery2 test"); // Create two batteries with coordinates (0, 0, 0) and (1, 0, 0) // Battery1 should be able to provide energy to battery2 var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 0, 1, "Battery1"); @@ -194,8 +207,8 @@ public class EnergyTests { System.out.println("Battery2 energy: " + battery2.energy); // Battery1 should have lost 10 energy and battery2 should have gained 10 energy - assertEquals(6, battery1.energy); - assertEquals(10, battery2.energy); + assertWithError(6, battery1.energy); + assertWithError(10, battery2.energy); } @Test @@ -210,8 +223,8 @@ public class EnergyTests { battery1.energy = 8; battery2.energy = 8; - // Tick 10 times - for (int i = 0; i < 10; i++) { + // Tick 11 times + for (int i = 0; i < 11; i++) { SimpleCodeOnlyBattery.tick(battery1); SimpleCodeOnlyBattery.tick(battery2); } @@ -221,12 +234,13 @@ public class EnergyTests { System.out.println("Battery2 energy: " + battery2.energy); // Battery1 and battery2 should have lost no energy - assertEquals(8, battery1.energy); - assertEquals(8, battery2.energy); + assert (battery1.energy >= 7 && battery1.energy <= 9); + assert (battery2.energy >= 7 && battery2.energy <= 9); } @Test public void middleOneCharges() { + System.out.println("Middle one charges"); // Create three batteries with coordinates (0, 0, 0), (1, 0, 0), and (2, 0, 0) // Battery2 should provide energy to battery1 and battery3 var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 1, 0, "Battery1"); @@ -249,13 +263,14 @@ public class EnergyTests { System.out.println("Battery3 energy: " + battery3.energy); // Battery1 and battery3 should have gained 8 energy and battery2 should have lost 16 energy - assertEquals(8, battery1.energy); - assertEquals(0, battery2.energy); - assertEquals(8, battery3.energy); + assertWithError(8, battery1.energy); + assertWithError(0, battery2.energy); + assertWithError(8, battery3.energy); } @Test public void middleOneReceives() { + System.out.println("Middle one receives"); // Create three batteries with coordinates (0, 0, 0), (1, 0, 0), and (2, 0, 0) // Battery1 and battery3 should provide energy to battery2 var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 0, 1, "Battery1"); @@ -266,8 +281,8 @@ public class EnergyTests { battery1.energy = 8; battery3.energy = 8; - // Tick 10 times - for (int i = 0; i < 16; i++) { + // Tick 17 times + for (int i = 0; i < 17; i++) { SimpleCodeOnlyBattery.tick(battery1); SimpleCodeOnlyBattery.tick(battery2); SimpleCodeOnlyBattery.tick(battery3); @@ -279,13 +294,14 @@ public class EnergyTests { System.out.println("Battery3 energy: " + battery3.energy); // Battery2 should have 16 energy and battery1 and battery3 should have 0 energy - assertEquals(0, battery1.energy); - assertEquals(16, battery2.energy); - assertEquals(0, battery3.energy); + assertWithError(0, battery1.energy); + assertWithError(16, battery2.energy); + assertWithError(0, battery3.energy); } @Test public void testEnergyTransferWithRange() { + System.out.println("Energy transfer with range test"); // Create two batteries with coordinates (0, 0, 0) and (2, 0, 0) // Battery1 should be able to provide energy to battery2 var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 0, 1, "Battery1"); @@ -305,12 +321,79 @@ public class EnergyTests { System.out.println("Battery2 energy: " + battery2.energy); // Battery1 should have lost no energy and battery2 should have gained no energy - assertEquals(16, battery1.energy); - assertEquals(0, battery2.energy); + assertWithError(16, battery1.energy); + assertWithError(0, battery2.energy); + } + + @Test + public void testCables() { + System.out.println("Cables test"); + // Create two batteries with coordinates (0, 0, 0) and (2, 0, 0) and a cable at (1, 0, 0) + // Battery1 should be able to provide energy to battery2 + var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 0, 1, "Battery1"); + var battery2 = new SimpleCodeOnlyBattery(new Vec3i(2, 0, 0), 16, 1, 0, "Battery2"); + var cable = new SimpleCodeOnlyCable(new Vec3i(1, 0, 0), 1, 1, 1, "Cable"); + + // Give battery1 some energy + battery1.energy = 16; + + // Tick the batteries and cable 16 times + for (int i = 0; i < 16; i++) { + SimpleCodeOnlyBattery.tick(battery1); + SimpleCodeOnlyCable.tick(cable); + SimpleCodeOnlyBattery.tick(battery2); + } + + // Output the energy of the batteries + System.out.println("Battery1 energy: " + battery1.energy); + System.out.println("Battery2 energy: " + battery2.energy); + + // Output the energy of the cable + System.out.println("Cable energy: " + cable.energy); + + // Battery1 should have lost 16 energy, battery2 should have gained 15 energy, and the cable should have 1 energy + assertWithError(1, cable.energy); + assertWithError(0, battery1.energy); + assertWithError(15, battery2.energy); + } + + @Test + public void longCableChain() { + System.out.println("Long cable chain test"); + // Create 2 batteries with coordinates (0, 0, 0) and (1001, 0, 0) and 1000 cables in between + // Battery1 should be able to provide energy to battery2 + var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 32, 0, 1, "Battery1"); + var battery2 = new SimpleCodeOnlyBattery(new Vec3i(11, 0, 0), 16, 1, 0, "Battery2"); + var cables = new SimpleCodeOnlyCable[10]; + for (int i = 0; i < 10; i++) { + cables[i] = new SimpleCodeOnlyCable(new Vec3i(i + 1, 0, 0), 16, 1, 1, "Cable" + (i + 1)); + } + + // Give battery1 some energy + battery1.energy = 32; + + // Tick the batteries and cables + for (int i = 0; i < 10; i++) { + SimpleCodeOnlyBattery.tick(battery1); + SimpleCodeOnlyBattery.tick(battery2); + for (SimpleCodeOnlyCable cable : cables) { + SimpleCodeOnlyCable.tick(cable); + } + } + + // Output the energy of the batteries + System.out.println("Battery1 energy: " + battery1.energy); + System.out.println("Battery2 energy: " + battery2.energy); + + // Output the energy of the cables + for (SimpleCodeOnlyCable cable : cables) { + System.out.println(cable.name + " energy: " + cable.energy); + } } @Test public void stressTest() { + System.out.println("Stress test"); // Create 1000 batteries with coordinates (0, 0, 0) to (999, 0, 0) // Each battery should be able to provide energy to the next battery var batteries = new SimpleCodeOnlyBattery[1000]; diff --git a/src/test/java/SimpleCodeOnlyBattery.java b/src/test/java/SimpleCodeOnlyBattery.java index f75c877..befb3fb 100644 --- a/src/test/java/SimpleCodeOnlyBattery.java +++ b/src/test/java/SimpleCodeOnlyBattery.java @@ -9,7 +9,8 @@ import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.Vec3i; import java.util.ArrayList; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * SimpleBattery, but implemented using only the API and not the actual block entity for testing purposes. @@ -22,8 +23,9 @@ public class SimpleCodeOnlyBattery implements EnergyProvider, EnergyReceiver, Se public final String name; public final ProviderDetails providerDetails; public final ReceiverDetails receiverDetails; - public final CopyOnWriteArrayList providers = new CopyOnWriteArrayList<>(); + public final Set providers = ConcurrentHashMap.newKeySet(); public long energy = 0; + public boolean providing = false; public SimpleCodeOnlyBattery(Vec3i pos, long capacity, long inputRate, long outputRate, String name) { this.capacity = capacity; @@ -37,7 +39,7 @@ public class SimpleCodeOnlyBattery implements EnergyProvider, EnergyReceiver, Se } public static void tick(SimpleCodeOnlyBattery battery) { - System.out.println(battery.name + " ticking"); + System.out.println(battery.name + " ticking: list is " + battery.providers); var leftToFill = Math.min(battery.inputRate, battery.capacity - battery.energy); for (EnergyProvider provider : battery.providers) { // Less than zero shouldn't be possible, but just in case... @@ -45,24 +47,29 @@ public class SimpleCodeOnlyBattery implements EnergyProvider, EnergyReceiver, Se break; } long extracted = provider.extract(leftToFill, battery); - if (extracted == 0) { - battery.providers.remove(provider); - } battery.energy += extracted; leftToFill -= extracted; } + if (!battery.providing && battery.energy > 0) { + CoordinateMatchMaker.addProvider(battery.providerDetails); + battery.providing = true; + } } @Override public long extract(long amount, EnergyReceiver receiver) { - System.out.println(name + " extracting " + amount + " energy"); - long extracted = Math.min(Math.min(outputRate, amount), energy); - energy -= extracted; - System.out.println(name + " extracted " + extracted + " energy"); - if (energy == 0) { - CoordinateMatchMaker.removeProvider(providerDetails); + if (providing) { + System.out.println(name + " extracting " + amount + " energy"); + long extracted = Math.min(Math.min(outputRate, amount), energy); + energy -= extracted; + System.out.println(name + " extracted " + extracted + " energy"); + if (energy == 0) { + CoordinateMatchMaker.removeProvider(providerDetails); + } + return extracted; + } else { + return 0; } - return extracted; } @Override @@ -79,12 +86,16 @@ public class SimpleCodeOnlyBattery implements EnergyProvider, EnergyReceiver, Se @Override public void unready(EnergyProvider provider) { + System.out.println(name + " lost other provider"); providers.remove(provider); } @Override public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) { - CoordinateMatchMaker.addProvider(providerDetails); + if (energy > 0) { + CoordinateMatchMaker.addProvider(providerDetails); + providing = true; + } CoordinateMatchMaker.addReceiver(receiverDetails); } diff --git a/src/test/java/SimpleCodeOnlyCable.java b/src/test/java/SimpleCodeOnlyCable.java new file mode 100644 index 0000000..77038f2 --- /dev/null +++ b/src/test/java/SimpleCodeOnlyCable.java @@ -0,0 +1,90 @@ +import arzumify.polyenergy.api.EnergyProvider; +import arzumify.polyenergy.api.EnergyReceiver; +import arzumify.polyenergy.impl.CoordinateMatchMaker; +import arzumify.polyenergy.impl.ProviderDetails; +import arzumify.polyenergy.impl.ReceiverDetails; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.Vec3i; + +import java.util.ArrayList; + +// Cables can have ONE provider and MANY receivers. +public class SimpleCodeOnlyCable implements EnergyProvider, EnergyReceiver, ServerBlockEntityEvents.Unload, ServerBlockEntityEvents.Load { + public final long capacity; + public final long inputRate; + public final long outputRate; + public final String name; + public final ProviderDetails providerDetails; + public final ReceiverDetails receiverDetails; + public long energy = 0; + public EnergyProvider currentProvider; + + public SimpleCodeOnlyCable(Vec3i pos, long capacity, long inputRate, long outputRate, String name) { + this.capacity = capacity; + this.inputRate = inputRate; + this.outputRate = outputRate; + this.providerDetails = ProviderDetails.NewSimple(pos, this); + this.receiverDetails = ReceiverDetails.NewSimple(pos, this); + this.name = name; + CoordinateMatchMaker.addReceiver(receiverDetails); + } + + public static void tick(SimpleCodeOnlyCable cable) { + System.out.println(cable.name + " ticking"); + if (cable.currentProvider != null && cable.energy < cable.capacity) { + cable.energy += cable.currentProvider.extract(Math.min(cable.capacity - cable.energy, cable.inputRate), cable); + } + } + + @Override + public long extract(long amount, EnergyReceiver receiver) { + System.out.println(name + " extracting " + amount + " energy"); + long extracted = Math.min(Math.min(outputRate, amount), energy); + energy -= extracted; + System.out.println(name + " extracted " + extracted + " energy"); + if (energy == 0) { + CoordinateMatchMaker.removeProvider(providerDetails); + } + return extracted; + } + + @Override + public void exists(EnergyReceiver receiver, ArrayList pointsOfPresence) { + System.out.println(name + " found other receiver"); + if (currentProvider != null) { + receiver.ready(currentProvider); + } + } + + @Override + public void ready(EnergyProvider provider) { + System.out.println(name + " found other provider"); + if (currentProvider == null) { + currentProvider = provider; + CoordinateMatchMaker.addProvider(providerDetails); + } + } + + @Override + public void unready(EnergyProvider provider) { + if (currentProvider == provider) { + System.out.println(name + " lost other provider"); + currentProvider = null; + CoordinateMatchMaker.removeProvider(providerDetails); + } + } + + @Override + public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) { + CoordinateMatchMaker.addReceiver(receiverDetails); + } + + @Override + public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) { + CoordinateMatchMaker.removeProvider(providerDetails); + CoordinateMatchMaker.removeReceiver(receiverDetails); + currentProvider = null; + } +}