Gave energy tests error of 1, made the matchmaker not panic if a provider is removed without being added, made the recommendation to use a set, added a timeout to energy tests, switched to concurrentHashMaps instead of copyOnWriteArraySets, applied my own recommendations, fixed some buggy behaviour relating to backflow, added cables.
This commit is contained in:
parent
69746e0faa
commit
2383562f0a
7 changed files with 363 additions and 69 deletions
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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<ProviderDetails> providers = new ArrayList<>();
|
||||
private static final ArrayList<ReceiverDetails> receivers = new ArrayList<>();
|
||||
private static final HashMap<ProviderDetails, ArrayList<ReceiverDetails>> matches = new HashMap<>();
|
||||
private static final CopyOnWriteArrayList<ProviderDetails> providers = new CopyOnWriteArrayList<>();
|
||||
private static final CopyOnWriteArrayList<ReceiverDetails> receivers = new CopyOnWriteArrayList<>();
|
||||
private static final ConcurrentHashMap<ProviderDetails, Set<ReceiverDetails>> 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);
|
||||
}
|
||||
|
|
|
@ -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<EnergyProvider> providers = new CopyOnWriteArrayList<>();
|
||||
private final Set<EnergyProvider> 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);
|
||||
}
|
||||
|
||||
|
|
88
src/main/java/arzumify/polyenergy/util/SimpleCable.java
Normal file
88
src/main/java/arzumify/polyenergy/util/SimpleCable.java
Normal file
|
@ -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<Vec3i> 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;
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
|
|
|
@ -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<EnergyProvider> providers = new CopyOnWriteArrayList<>();
|
||||
public final Set<EnergyProvider> 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);
|
||||
}
|
||||
|
||||
|
|
90
src/test/java/SimpleCodeOnlyCable.java
Normal file
90
src/test/java/SimpleCodeOnlyCable.java
Normal file
|
@ -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<Vec3i> 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;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue