Significantly optimised the MatchMaker, added unit tests, added support for chunk unloading, added a maven repository, added a modrinth, added an icon, bumped the version.

This commit is contained in:
Tracker-Friendly 2025-02-23 14:50:16 +00:00
parent b66a76217c
commit 69746e0faa
13 changed files with 547 additions and 53 deletions

View file

@ -13,10 +13,30 @@ A dead-simple energy mod for Fabric.
### Installation
This mod is in early development and is not yet available for download.
The mod is available on [Modrinth](https://modrinth.com/project/polyenergy). You can download it from there or build it
yourself.
### API
#### Installation
To use the API, add the following to your `build.gradle`:
```groovy
repositories {
maven {
name = "Arzumify's Maven"
url = "https://maven.ailur.dev"
}
}
dependencies {
modImplementation "dev.ailur.polyenergy:polyenergy:{version}"
}
```
To get the latest version, check the [Maven repository](https://maven.ailur.dev/arzumify/polyenergy/).
#### Providers
Providers are the source of energy in the game. They can be blocks, entities, or any other object that can provide

View file

@ -38,6 +38,16 @@ dependencies {
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// Testing
testImplementation(platform('org.junit:junit-bom:5.12.0'))
testImplementation('org.junit.jupiter:junit-jupiter')
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
}
tasks.named("test", Test) {
useJUnitPlatform()
}
processResources {

View file

@ -7,7 +7,7 @@ minecraft_version=1.21.4
yarn_mappings=1.21.4+build.8
loader_version=0.16.10
# Mod Properties
mod_version=1.0.0
mod_version=1.1.0
maven_group=arzumify.polyenergy
archives_base_name=polyenergy
# Dependencies

View file

@ -3,10 +3,6 @@ package arzumify.polyenergy;
import net.fabricmc.api.ModInitializer;
public class Polyenergy implements ModInitializer {
// We don't actually use either of these, so it's commented out.
// public static final String MOD_ID = "polyenergy";
// public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
@Override
public void onInitialize() {
}

View file

@ -9,4 +9,12 @@ public interface EnergyReceiver {
* @param provider The provider that exists and is ready to provide energy to this receiver.
*/
void ready(EnergyProvider provider);
/**
* When called by the server matchmaker, indicates that this provider is no longer able to provide energy to this receiver.
* Wait for another call to {@link EnergyReceiver#ready(EnergyProvider)} before attempting to extract energy from this provider.
*
* @param provider The provider that is no longer able to provide energy to this receiver.
*/
void unready(EnergyProvider provider);
}

View file

@ -1,31 +1,61 @@
package arzumify.polyenergy.impl;
import java.util.ArrayList;
import java.util.HashMap;
// TODO: This seems very inefficient. Measure performance and then try improving this? This is O(n * m) which is very bad!
// It's slightly more optimized now, as in it could realistically run in Minecraft.
// Reset: O(1) constant time, we're just deleting everything
// Add Provider: O(m * k^2) where m is the number of receivers and k is the number of points of presence
// Add Receiver: O(n * k^2) where n is the number of providers and k is the number of points of presence
// Remove Provider: O(n + m) where n is the number of providers and m is the number of matches for the provider
// Remove Receiver: O(m) where m is the number of receivers
// I think we stop the optimisations here, the bottleneck is no longer the matchmaker but Minecraft itself.
// 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<>();
public static void update() {
for (ReceiverDetails receiver : receivers) {
for (ProviderDetails provider : providers) {
if (provider.isInRange(receiver.pointsOfPresence())) {
provider.provider().exists(receiver.receiver(), provider.pointsOfPresence());
}
}
}
public static void reset() {
providers.clear();
receivers.clear();
matches.clear();
}
public static void addProvider(ProviderDetails provider) {
providers.add(provider);
update();
matches.put(provider, new ArrayList<>());
// Search for receivers within range of this provider
for (ReceiverDetails receiver : receivers) {
if (provider.isInRange(receiver.pointsOfPresence())) {
provider.provider().exists(receiver.receiver(), provider.pointsOfPresence());
matches.get(provider).add(receiver);
}
}
}
public static void addReceiver(ReceiverDetails receiver) {
receivers.add(receiver);
update();
// Search for providers within range of this receiver
for (ProviderDetails provider : providers) {
if (provider.isInRange(receiver.pointsOfPresence())) {
provider.provider().exists(receiver.receiver(), provider.pointsOfPresence());
matches.get(provider).add(receiver);
}
}
}
public static void removeProvider(ProviderDetails provider) {
providers.remove(provider);
for (ReceiverDetails receiver : matches.get(provider)) {
receiver.receiver().unready(provider.provider());
}
matches.remove(provider);
}
public static void removeReceiver(ReceiverDetails receiver) {
receivers.remove(receiver);
}
}

View file

@ -15,7 +15,10 @@ public record ProviderDetails(ArrayList<Vec3i> pointsOfPresence, EnergyProvider
public boolean isInRange(ArrayList<Vec3i> pointsOfPresence) {
for (Vec3i point : pointsOfPresence) {
for (Vec3i myPoint : this.pointsOfPresence) {
if (point.isWithinDistance(myPoint, this.range)) {
var manhattanDistance = point.getManhattanDistance(myPoint);
if (manhattanDistance == 0) {
return false;
} else if (manhattanDistance <= range) {
return true;
}
}

View file

@ -5,22 +5,28 @@ 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;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* A simple battery that can provide and receive energy. Has a range of 1 block in all directions (directly adjacent blocks).
*/
public class SimpleBattery extends BlockEntity implements EnergyProvider, EnergyReceiver {
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 ArrayList<EnergyProvider> providers = new ArrayList<>();
private final ProviderDetails providerDetails = ProviderDetails.NewSimple(pos, this);
private final ReceiverDetails receiverDetails = ReceiverDetails.NewSimple(pos, this);
private final CopyOnWriteArrayList<EnergyProvider> providers = new CopyOnWriteArrayList<>();
private long energy = 0;
public SimpleBattery(BlockEntityType<?> type, BlockPos pos, BlockState state, long capacity, long inputRate, long outputRate) {
@ -28,18 +34,10 @@ public class SimpleBattery extends BlockEntity implements EnergyProvider, Energy
this.capacity = capacity;
this.inputRate = inputRate;
this.outputRate = outputRate;
CoordinateMatchMaker.addProvider(ProviderDetails.NewSimple(pos, this));
CoordinateMatchMaker.addReceiver(ReceiverDetails.NewSimple(pos, this));
CoordinateMatchMaker.addProvider(providerDetails);
CoordinateMatchMaker.addReceiver(receiverDetails);
}
/**
* When called by a receiver, attempts to remove energy from the provider.
* Do not attempt to extract energy until {@link EnergyReceiver#ready(EnergyProvider)} is called by the receiver.
*
* @param amount The target amount of energy to extract
* @param receiver The receiver that is attempting to extract energy
* @return The amount of energy actually extracted. If zero, the receiver will not attempt to extract energy until {@link EnergyReceiver#ready(EnergyProvider)} is called.
*/
@Override
public long extract(long amount, EnergyReceiver receiver) {
long extracted = Math.min(Math.min(outputRate, amount), energy);
@ -47,41 +45,44 @@ public class SimpleBattery extends BlockEntity implements EnergyProvider, Energy
return extracted;
}
/**
* When called by the server matchmaker, indicates that this is a possible receiver to call {@link EnergyReceiver#ready(EnergyProvider)} on.
* This particular overload is for coordinate-based matchmaking.
*
* @param receiver The receiver that exists.
* @param pointsOfPresence The points of presence of the receiver (can be multiple, e.g. for a network of wires).
*/
@Override
public void exists(EnergyReceiver receiver, ArrayList<Vec3i> pointsOfPresence) {
receiver.ready(this);
}
/**
* When called by a provider, indicates that the provider is ready to have energy extracted from it.
* Do not attempt to call {@link EnergyProvider#extract(long, EnergyReceiver)} on any provider until this method is called.
*
* @param provider The provider that is ready to provide energy to this receiver
*/
@Override
public void ready(EnergyProvider provider) {
providers.add(provider);
}
/**
* Called every tick to update the battery's energy level.
*/
public void tick() {
var leftToFill = Math.min(inputRate, capacity - energy);
for (EnergyProvider provider : providers) {
if (leftToFill == 0) {
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, this);
energy += extracted;
long extracted = provider.extract(leftToFill, battery);
battery.energy += extracted;
leftToFill -= extracted;
}
}
@Override
public void unready(EnergyProvider provider) {
providers.remove(provider);
}
@Override
public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) {
CoordinateMatchMaker.addProvider(providerDetails);
CoordinateMatchMaker.addReceiver(receiverDetails);
}
@Override
public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) {
CoordinateMatchMaker.removeProvider(providerDetails);
CoordinateMatchMaker.removeReceiver(receiverDetails);
providers.clear();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 633 B

After

Width:  |  Height:  |  Size: 410 B

View file

@ -12,7 +12,7 @@
"sources": "https://git.ailur.dev/arzumify/polyenergy"
},
"license": "GPL-3.0",
"icon": "assets/polyenergy/icon.png",
"icon": "assets/polyenergy/icon-scaled.png",
"environment": "*",
"entrypoints": {
"main": [

View file

@ -0,0 +1,329 @@
import arzumify.polyenergy.impl.CoordinateMatchMaker;
import net.minecraft.util.math.Vec3i;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class EnergyTests {
@BeforeEach
public void setup() {
// Delete all batteries
CoordinateMatchMaker.reset();
System.out.println("Reset CoordinateMatchMaker");
}
@Test
public void testEnergyTransfer() {
// 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");
var battery2 = new SimpleCodeOnlyBattery(new Vec3i(1, 0, 0), 16, 1, 0, "Battery2");
// Give battery1 some energy
battery1.energy = 16;
// Tick the batteries 10 times
for (int i = 0; i < 10; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Output the energy of the batteries
System.out.println("Battery1 energy: " + battery1.energy);
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);
}
@Test
public void testUnloadBattery1() {
// 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");
var battery2 = new SimpleCodeOnlyBattery(new Vec3i(1, 0, 0), 16, 1, 0, "Battery2");
// Give battery1 some energy
battery1.energy = 16;
// Tick 5 times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Unload battery1
System.out.println("Unloading battery1");
battery1.onUnload(null, null);
// Tick 5 more times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Output the energy of the batteries
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);
}
@Test
public void testUnloadBattery2() {
// 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");
var battery2 = new SimpleCodeOnlyBattery(new Vec3i(1, 0, 0), 16, 1, 0, "Battery2");
// Give battery1 some energy
battery1.energy = 16;
// Tick 5 times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Unload battery2
System.out.println("Unloading battery2");
battery2.onUnload(null, null);
// Tick 5 more times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Output the energy of the batteries
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);
}
@Test
public void testUnloadThenReloadBattery1() {
// 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");
var battery2 = new SimpleCodeOnlyBattery(new Vec3i(1, 0, 0), 16, 1, 0, "Battery2");
// Give battery1 some energy
battery1.energy = 16;
// Tick 5 times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Unload battery1
System.out.println("Unloading battery1");
battery1.onUnload(null, null);
// Tick 5 more times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Reload battery1
System.out.println("Reloading battery1");
battery1.onLoad(null, null);
// Tick 5 more times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Output the energy of the batteries
System.out.println("Battery1 energy: " + battery1.energy);
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);
}
@Test
public void testUnloadThenReloadBattery2() {
// 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");
var battery2 = new SimpleCodeOnlyBattery(new Vec3i(1, 0, 0), 16, 1, 0, "Battery2");
// Give battery1 some energy
battery1.energy = 16;
// Tick 5 times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Unload battery2
System.out.println("Unloading battery2");
battery2.onUnload(null, null);
// Tick 5 more times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Reload battery2
System.out.println("Reloading battery2");
battery2.onLoad(null, null);
// Tick 5 more times
for (int i = 0; i < 5; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Output the energy of the batteries
System.out.println("Battery1 energy: " + battery1.energy);
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);
}
@Test
public void twoHalfFullBatteries() {
System.out.println("Two half-full batteries");
// Create two batteries with coordinates (0, 0, 0) and (1, 0, 0)
// They should both provide energy to each other and stay at equilibrium
var battery1 = new SimpleCodeOnlyBattery(new Vec3i(0, 0, 0), 16, 1, 1, "Battery1");
var battery2 = new SimpleCodeOnlyBattery(new Vec3i(1, 0, 0), 16, 1, 1, "Battery2");
// Give battery1 and battery2 some energy
battery1.energy = 8;
battery2.energy = 8;
// Tick 10 times
for (int i = 0; i < 10; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Output the energy of the batteries
System.out.println("Battery1 energy: " + battery1.energy);
System.out.println("Battery2 energy: " + battery2.energy);
// Battery1 and battery2 should have lost no energy
assertEquals(8, battery1.energy);
assertEquals(8, battery2.energy);
}
@Test
public void middleOneCharges() {
// 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");
var battery2 = new SimpleCodeOnlyBattery(new Vec3i(1, 0, 0), 16, 0, 1, "Battery2");
var battery3 = new SimpleCodeOnlyBattery(new Vec3i(2, 0, 0), 16, 1, 0, "Battery3");
// Give battery2 some energy
battery2.energy = 16;
// Tick 10 times
for (int i = 0; i < 10; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
SimpleCodeOnlyBattery.tick(battery3);
}
// Output the energy of the batteries
System.out.println("Battery1 energy: " + battery1.energy);
System.out.println("Battery2 energy: " + battery2.energy);
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);
}
@Test
public void middleOneReceives() {
// 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");
var battery2 = new SimpleCodeOnlyBattery(new Vec3i(1, 0, 0), 16, 1, 0, "Battery2");
var battery3 = new SimpleCodeOnlyBattery(new Vec3i(2, 0, 0), 16, 0, 1, "Battery3");
// Give battery1 and battery3 some energy
battery1.energy = 8;
battery3.energy = 8;
// Tick 10 times
for (int i = 0; i < 16; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
SimpleCodeOnlyBattery.tick(battery3);
}
// Output the energy of the batteries
System.out.println("Battery1 energy: " + battery1.energy);
System.out.println("Battery2 energy: " + battery2.energy);
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);
}
@Test
public void testEnergyTransferWithRange() {
// 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");
var battery2 = new SimpleCodeOnlyBattery(new Vec3i(2, 0, 0), 16, 1, 0, "Battery2");
// Give battery1 some energy
battery1.energy = 16;
// Tick the batteries 10 times
for (int i = 0; i < 10; i++) {
SimpleCodeOnlyBattery.tick(battery1);
SimpleCodeOnlyBattery.tick(battery2);
}
// Output the energy of the batteries
System.out.println("Battery1 energy: " + battery1.energy);
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);
}
@Test
public void stressTest() {
// 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];
for (int i = 0; i < 1000; i++) {
batteries[i] = new SimpleCodeOnlyBattery(new Vec3i(i, 0, 0), 16, 1, 2, "Battery" + i);
}
// Give the first battery some energy
batteries[0].energy = 16;
// Tick once
for (SimpleCodeOnlyBattery battery : batteries) {
SimpleCodeOnlyBattery.tick(battery);
}
}
}

View file

@ -0,0 +1,97 @@
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;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* SimpleBattery, but implemented using only the API and not the actual block entity for testing purposes.
* All fields are public for testing purposes.
*/
public class SimpleCodeOnlyBattery 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 final CopyOnWriteArrayList<EnergyProvider> providers = new CopyOnWriteArrayList<>();
public long energy = 0;
public SimpleCodeOnlyBattery(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.addProvider(providerDetails);
CoordinateMatchMaker.addReceiver(receiverDetails);
}
public static void tick(SimpleCodeOnlyBattery battery) {
System.out.println(battery.name + " ticking");
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);
if (extracted == 0) {
battery.providers.remove(provider);
}
battery.energy += extracted;
leftToFill -= extracted;
}
}
@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");
receiver.ready(this);
}
@Override
public void ready(EnergyProvider provider) {
System.out.println(name + " found other provider");
providers.add(provider);
}
@Override
public void unready(EnergyProvider provider) {
providers.remove(provider);
}
@Override
public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) {
CoordinateMatchMaker.addProvider(providerDetails);
CoordinateMatchMaker.addReceiver(receiverDetails);
}
@Override
public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) {
CoordinateMatchMaker.removeProvider(providerDetails);
CoordinateMatchMaker.removeReceiver(receiverDetails);
providers.clear();
}
}