This commit is contained in:
Tracker-Friendly 2025-03-05 18:48:10 +00:00
parent 5719a08701
commit 15ff8a1181
14 changed files with 109 additions and 665 deletions

View File

@ -37,13 +37,11 @@ dependencies {
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// Presence API
implementation "arzumify:presence:${project.presence_version}"
// Testing
testImplementation(platform('org.junit:junit-bom:5.12.0'))
testImplementation('org.junit.jupiter:junit-jupiter')
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
}
tasks.named("test", Test) {

View File

@ -7,9 +7,8 @@ minecraft_version=1.20
yarn_mappings=1.20+build.1
loader_version=0.16.10
# Mod Properties
mod_version=2.0.1
maven_group=arzumify.polyenergy
mod_version=1.0.0
maven_group=arzumify.polyenergy-cursed-edition
archives_base_name=polyenergy
# Dependencies
fabric_version=0.118.0+1.21.4
presence_version=1.1.1

0
logs/latest.log Normal file
View File

View File

@ -0,0 +1,4 @@
package arzumify.polyenergy.api;
public interface EnergyBlock {
}

View File

@ -1,7 +0,0 @@
package arzumify.polyenergy.api;
import arzumify.presence.presences.IntegerPresence;
import arzumify.presence.presences.PresenceProvider;
public interface EnergyPresence extends PresenceProvider<IntegerPresence> {
}

View File

@ -1,23 +1,14 @@
package arzumify.polyenergy.api;
// Provides energy to EnergyReceivers. Can be implemented by anything able to tick and provide energy!
public interface EnergyProvider extends EnergyPresence {
/**
* 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.
* The receiver may attempt to remove itself from the provider if it is no longer able to receive energy; this will be done via unready.
*
* @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.
*/
long extract(long amount, EnergyReceiver receiver);
public interface EnergyProvider extends EnergyBlock {
/**
* 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.
*/
void exists(EnergyReceiver receiver);
void ready(EnergyReceiver receiver);
void unready(EnergyReceiver receiver);
}

View File

@ -1,7 +1,7 @@
package arzumify.polyenergy.api;
// Receives energy from EnergyProviders. Can be implemented by anything able to tick and receive energy!
public interface EnergyReceiver extends EnergyPresence {
public interface EnergyReceiver extends EnergyBlock {
/**
* 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.
@ -19,4 +19,6 @@ public interface EnergyReceiver extends EnergyPresence {
* @param provider The provider that is no longer able to provide energy to this receiver.
*/
void unready(EnergyProvider provider);
long receive(long amount, EnergyProvider provider);
}

View File

@ -0,0 +1,21 @@
package arzumify.polyenergy.api;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import java.util.HashSet;
import java.util.Set;
public class SearchForNeighbours {
public static Set<EnergyBlock> searchForNeighbours(World world, BlockPos pos) {
var neighbours = new HashSet<EnergyBlock>();
Direction.stream().forEach(direction -> {
var entity = world.getBlockEntity(pos.offset(direction));
if (entity instanceof EnergyBlock) {
neighbours.add((EnergyBlock) entity);
}
});
return neighbours;
}
}

View File

@ -1,67 +0,0 @@
package arzumify.polyenergy.impl;
import arzumify.polyenergy.api.EnergyPresence;
import arzumify.polyenergy.api.EnergyProvider;
import arzumify.polyenergy.api.EnergyReceiver;
import arzumify.presence.matchmaker.Channel;
import arzumify.presence.matchmaker.MatchMaker;
import arzumify.presence.presences.IntegerPresence;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
// CoordinateMatchMaker is now implemented using the Presence API
public class CoordinateMatchMaker {
private static final Channel receivers = new Channel();
private static final Channel providers = new Channel();
private static final ConcurrentHashMap<EnergyProvider, Set<EnergyReceiver>> matches = new ConcurrentHashMap<>();
private static MatchMaker<IntegerPresence, EnergyPresence> matchMaker = new MatchMaker<>();
public static void reset() {
matchMaker = new MatchMaker<>();
}
public static void addProvider(EnergyProvider provider) {
matchMaker.Add(provider, providers);
// Search for receivers within range of this provider
var receivers = matchMaker.Search(provider, CoordinateMatchMaker.receivers);
if (receivers != null) {
for (EnergyPresence receiver : receivers) {
if (receiver instanceof EnergyReceiver) {
provider.exists((EnergyReceiver) receiver);
}
}
}
}
public static void addReceiver(EnergyReceiver receiver) {
matchMaker.Add(receiver, receivers);
// Search for providers within range of this receiver
var providers = matchMaker.Search(receiver, CoordinateMatchMaker.providers);
if (providers != null) {
for (EnergyPresence provider : providers) {
if (provider instanceof EnergyProvider) {
((EnergyProvider) provider).exists(receiver);
matches.putIfAbsent((EnergyProvider) provider, ConcurrentHashMap.newKeySet());
matches.get(provider).add(receiver);
}
}
}
}
public static void removeProvider(EnergyProvider provider) {
matchMaker.Remove(provider, providers);
if (matches.get(provider) != null) {
for (EnergyReceiver receiver : matches.get(provider)) {
receiver.unready(provider);
}
}
matches.remove(provider);
}
public static void removeReceiver(EnergyReceiver receiver) {
matchMaker.Remove(receiver, receivers);
}
}

View File

@ -1,15 +1,15 @@
{
"schemaVersion": 1,
"id": "polyenergy",
"id": "polyenergy-cursed-edition",
"version": "${version}",
"name": "Polyenergy",
"description": "A dead-simple serverside energy api",
"description": "A very cursed and broken serverside energy api",
"authors": [
"Arzumify"
],
"contact": {
"homepage": "https://git.ailur.dev/arzumify/polyenergy/",
"sources": "https://git.ailur.dev/arzumify/polyenergy"
"homepage": "https://git.ailur.dev/arzumify/polyenergy-cursed-edition/",
"sources": "https://git.ailur.dev/arzumify/polyenergy-cursed-edition/"
},
"license": "GPL-3.0",
"icon": "assets/polyenergy/icon-scaled.png",

View File

@ -2,18 +2,18 @@
"schema_version": 1,
"quilt_loader": {
"group": "${group}",
"id": "polyenergy",
"id": "polyenergy-cursed-edition",
"version": "${version}",
"metadata": {
"name": "Polyenergy",
"description": "A dead-simple serverside energy api",
"description": "A cursed serverside energy api",
"contributors": {
"Arzumify": "Owner"
},
"contact": {
"homepage": "https://git.ailur.dev/arzumify/polyenergy/",
"sources": "https://git.ailur.dev/arzumify/polyenergy",
"issues": "https://git.ailur.dev/arzumify/polyenergy/issues"
"homepage": "https://git.ailur.dev/arzumify/polyenergy-cursed-edition/",
"sources": "https://git.ailur.dev/arzumify/polyenergy-cursed-edition/",
"issues": "https://git.ailur.dev/arzumify/polyenergy-cursed-edition/issues"
},
"icon": "assets/polyenergy/icon-scaled.png",
"license": "GPL-3.0"

View File

@ -1,412 +1,4 @@
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.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
CoordinateMatchMaker.reset();
System.out.println("Reset CoordinateMatchMaker");
}
@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");
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
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");
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(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 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");
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
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");
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(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
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");
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
assertWithError(6, battery1.energy);
assertWithError(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 11 times
for (int i = 0; i < 11; 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
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");
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
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");
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 17 times
for (int i = 0; i < 17; 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
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");
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
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];
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);
}
}
}
/*
TESTS ARE BROKEN IN CURSED EDITION
DO NOT TRY TO TEST
*/

View File

@ -1,110 +0,0 @@
import arzumify.polyenergy.api.EnergyProvider;
import arzumify.polyenergy.api.EnergyReceiver;
import arzumify.polyenergy.impl.CoordinateMatchMaker;
import arzumify.presence.presences.IntegerPresence;
import arzumify.presence.presences.Presence;
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.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 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 IntegerPresence presence;
public final String name;
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;
this.inputRate = inputRate;
this.outputRate = outputRate;
this.name = name;
this.presence = new IntegerPresence((short) 1, new arzumify.presence.maths.Vec3i(pos.getX(), pos.getY(), pos.getZ()));
CoordinateMatchMaker.addProvider(this);
CoordinateMatchMaker.addReceiver(this);
}
public static void tick(SimpleCodeOnlyBattery battery) {
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...
if (leftToFill <= 0) {
break;
}
long extracted = provider.extract(leftToFill, battery);
battery.energy += extracted;
leftToFill -= extracted;
}
if (!battery.providing && battery.energy > 0) {
CoordinateMatchMaker.addProvider(battery);
battery.providing = true;
}
}
@Override
public long extract(long amount, EnergyReceiver receiver) {
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(this);
}
return extracted;
} else {
return 0;
}
}
@Override
public void exists(EnergyReceiver receiver) {
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) {
System.out.println(name + " lost other provider");
providers.remove(provider);
}
@Override
public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) {
if (energy > 0) {
CoordinateMatchMaker.addProvider(this);
providing = true;
}
CoordinateMatchMaker.addReceiver(this);
}
@Override
public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) {
CoordinateMatchMaker.removeProvider(this);
CoordinateMatchMaker.removeReceiver(this);
providers.clear();
}
@Override
public Presence<IntegerPresence> presence() {
return presence;
}
}

View File

@ -1,97 +1,118 @@
import arzumify.polyenergy.api.EnergyProvider;
import arzumify.polyenergy.api.EnergyReceiver;
import arzumify.polyenergy.impl.CoordinateMatchMaker;
import arzumify.presence.presences.IntegerPresence;
import arzumify.presence.presences.Presence;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.Vec3i;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
// Cables can have ONE provider and MANY receivers.
public class SimpleCodeOnlyCable implements EnergyProvider, EnergyReceiver, ServerBlockEntityEvents.Unload, ServerBlockEntityEvents.Load {
import static arzumify.polyenergy.api.SearchForNeighbours.searchForNeighbours;
/**
* SimpleCable, but implemented using only the API and not the actual block entity for testing purposes.
* All fields are public for testing purposes.
*/
public class SimpleCodeOnlyCable implements EnergyProvider, EnergyReceiver, ServerBlockEntityEvents.Unload, ServerBlockEntityEvents.Load, ServerTickEvents.EndTick {
public final long capacity;
public final long inputRate;
public final long outputRate;
public final IntegerPresence presence;
public final String name;
public long energy = 0;
public final Set<EnergyProvider> providers = ConcurrentHashMap.newKeySet();
public final Set<EnergyProvider> blackListed = ConcurrentHashMap.newKeySet();
public final Set<EnergyReceiver> receivers = ConcurrentHashMap.newKeySet();
public long energy = 0;
public SimpleCodeOnlyCable(Vec3i pos, long capacity, long inputRate, long outputRate, String name) {
this.capacity = capacity;
this.inputRate = inputRate;
this.outputRate = outputRate;
this.name = name;
this.presence = new IntegerPresence((short) 1, new arzumify.presence.maths.Vec3i(pos.getX(), pos.getY(), pos.getZ()));
CoordinateMatchMaker.addReceiver(this);
}
public static void tick(SimpleCodeOnlyCable cable) {
System.out.println(cable.name + " ticking");
var leftToFill = Math.min(cable.inputRate, cable.capacity - cable.energy);
for (EnergyProvider provider : cable.providers) {
// Less than zero shouldn't be possible, but just in case...
if (leftToFill <= 0) {
break;
public static void tick(SimpleCodeOnlyCable Cable) {
System.out.println(Cable.name + " ticking");
// Split the energy between all receivers
var energyToGive = Cable.energy / Cable.receivers.size();
for (EnergyReceiver receiver : Cable.receivers) {
var taken = receiver.receive(energyToGive, Cable);
Cable.energy -= taken;
if (receiver instanceof EnergyProvider) {
Cable.blackListed.add((EnergyProvider) receiver);
}
long extracted = provider.extract(leftToFill, cable);
cable.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");
return extracted;
public long receive(long amount, EnergyProvider provider) {
if (!blackListed.contains(provider)) {
System.out.println(name + " receiving " + amount + " energy");
long received = Math.min(Math.min(inputRate, amount), capacity - energy);
energy += received;
System.out.println(name + " received " + received + " energy");
blackListed.add(provider);
return received;
} else {
return 0;
}
}
@Override
public void exists(EnergyReceiver receiver) {
public void ready(EnergyReceiver receiver) {
System.out.println(name + " found other receiver");
if (!providers.isEmpty()) {
receiver.ready(this);
}
receivers.add(receiver);
receiver.ready(this);
}
@Override
public void ready(EnergyProvider provider) {
System.out.println(name + " found other provider");
providers.add(provider);
if (providers.size() == 1) {
CoordinateMatchMaker.addProvider(this);
}
}
@Override
public void unready(EnergyReceiver receiver) {
System.out.println(name + " lost other receiver");
receivers.remove(receiver);
}
@Override
public void unready(EnergyProvider provider) {
System.out.println(name + " lost other provider");
providers.remove(provider);
if (providers.isEmpty()) {
CoordinateMatchMaker.removeProvider(this);
}
}
@Override
public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) {
CoordinateMatchMaker.addReceiver(this);
var energyBlocks = searchForNeighbours(serverWorld, blockEntity.getPos());
for (var block : energyBlocks) {
if (block instanceof EnergyProvider) {
((EnergyProvider) block).ready(this);
}
if (block instanceof EnergyReceiver) {
ready((EnergyReceiver) block);
}
}
}
@Override
public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) {
CoordinateMatchMaker.removeProvider(this);
CoordinateMatchMaker.removeReceiver(this);
for (var provider : providers) {
provider.unready(this);
}
providers.clear();
for (var receiver : receivers) {
receiver.unready(this);
}
receivers.clear();
}
@Override
public Presence<IntegerPresence> presence() {
return presence;
public void onEndTick(MinecraftServer server) {
blackListed.clear();
}
}