Compare commits
10 commits
2383562f0a
...
15ff8a1181
Author | SHA1 | Date | |
---|---|---|---|
15ff8a1181 | |||
5719a08701 | |||
523a69c325 | |||
f88227e8a8 | |||
85300d60e2 | |||
d45e501c42 | |||
b8f4f7e2f9 | |||
c28c6ccee0 | |||
b45e79029e | |||
111f9cbdad |
19 changed files with 164 additions and 930 deletions
|
@ -6,8 +6,7 @@ A dead-simple energy mod for Fabric.
|
||||||
|
|
||||||
- Totally server-side
|
- Totally server-side
|
||||||
- Polymer-compatible
|
- Polymer-compatible
|
||||||
- Does not rely on Minecraft APIs (portable to other Vector3I-based games, in theory)
|
- Supports entities and networks of any size
|
||||||
- As an extension of the above, supports entities and networks of any size
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
modImplementation "dev.ailur.polyenergy:polyenergy:{version}"
|
modImplementation "arzumify.polyenergy:polyenergy:{version}"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -44,10 +43,10 @@ energy.
|
||||||
|
|
||||||
To create a provider, implement the `EnergyProvider` interface. This interface has two methods:
|
To create a provider, implement the `EnergyProvider` interface. This interface has two methods:
|
||||||
|
|
||||||
- `void exists(EnergyReceiver receiver, ArrayList<Vec3i> pointsOfPresence);`
|
- `void exists(EnergyReceiver receiver);`
|
||||||
|
|
||||||
This method is called when the matchmaking service finds a receiver that can receive energy from this provider. The
|
This method is called when the matchmaking service finds a receiver that can receive energy from this provider. The
|
||||||
provider should check if this receiver meets all of it's requirements, then call the Ready() method on the receiver to
|
provider should check if this receiver meets all of its requirements, then call the Ready() method on the receiver to
|
||||||
make a connection.
|
make a connection.
|
||||||
|
|
||||||
- `long extract(long amount, EnergyReceiver receiver);`
|
- `long extract(long amount, EnergyReceiver receiver);`
|
||||||
|
|
28
build.gradle
28
build.gradle
|
@ -11,11 +11,10 @@ base {
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// Add repositories to retrieve artifacts from in here.
|
maven {
|
||||||
// You should only use this when depending on other mods because
|
name = "Arzumify's Maven"
|
||||||
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
|
url = "https://maven.ailur.dev"
|
||||||
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
|
}
|
||||||
// for more information about repositories.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loom {
|
loom {
|
||||||
|
@ -42,6 +41,7 @@ dependencies {
|
||||||
testImplementation(platform('org.junit:junit-bom:5.12.0'))
|
testImplementation(platform('org.junit:junit-bom:5.12.0'))
|
||||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||||
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
|
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
|
||||||
|
testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named("test", Test) {
|
tasks.named("test", Test) {
|
||||||
|
@ -79,21 +79,3 @@ jar {
|
||||||
rename { "${it}_${inputs.properties.archivesName}" }
|
rename { "${it}_${inputs.properties.archivesName}" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure the maven publication
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
create("mavenJava", MavenPublication) {
|
|
||||||
artifactId = project.archives_base_name
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
|
||||||
repositories {
|
|
||||||
// Add repositories to publish to here.
|
|
||||||
// Notice: This block does NOT have the same function as the block in the top level.
|
|
||||||
// The repositories here will be used for publishing your artifact, not for
|
|
||||||
// retrieving dependencies.
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,12 +3,12 @@ org.gradle.jvmargs=-Xmx1G
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
# Fabric Properties
|
# Fabric Properties
|
||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.4
|
minecraft_version=1.20
|
||||||
yarn_mappings=1.21.4+build.8
|
yarn_mappings=1.20+build.1
|
||||||
loader_version=0.16.10
|
loader_version=0.16.10
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=1.1.0
|
mod_version=1.0.0
|
||||||
maven_group=arzumify.polyenergy
|
maven_group=arzumify.polyenergy-cursed-edition
|
||||||
archives_base_name=polyenergy
|
archives_base_name=polyenergy
|
||||||
# Dependencies
|
# Dependencies
|
||||||
fabric_version=0.118.0+1.21.4
|
fabric_version=0.118.0+1.21.4
|
0
logs/latest.log
Normal file
0
logs/latest.log
Normal file
|
@ -1,9 +0,0 @@
|
||||||
package arzumify.polyenergy;
|
|
||||||
|
|
||||||
import net.fabricmc.api.ModInitializer;
|
|
||||||
|
|
||||||
public class Polyenergy implements ModInitializer {
|
|
||||||
@Override
|
|
||||||
public void onInitialize() {
|
|
||||||
}
|
|
||||||
}
|
|
4
src/main/java/arzumify/polyenergy/api/EnergyBlock.java
Normal file
4
src/main/java/arzumify/polyenergy/api/EnergyBlock.java
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package arzumify.polyenergy.api;
|
||||||
|
|
||||||
|
public interface EnergyBlock {
|
||||||
|
}
|
|
@ -1,27 +1,14 @@
|
||||||
package arzumify.polyenergy.api;
|
package arzumify.polyenergy.api;
|
||||||
|
|
||||||
import net.minecraft.util.math.Vec3i;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
// Provides energy to EnergyReceivers. Can be implemented by anything able to tick and provide energy!
|
// Provides energy to EnergyReceivers. Can be implemented by anything able to tick and provide energy!
|
||||||
public interface EnergyProvider {
|
public interface EnergyProvider extends EnergyBlock {
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
long extract(long amount, EnergyReceiver receiver);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When called by the server matchmaker, indicates that this is a possible receiver to call {@link EnergyReceiver#ready(EnergyProvider)} on.
|
* 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.
|
* This particular overload is for coordinate-based matchmaking.
|
||||||
*
|
*
|
||||||
* @param receiver The receiver that exists.
|
* @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 or a large entity).
|
|
||||||
*/
|
*/
|
||||||
void exists(EnergyReceiver receiver, ArrayList<Vec3i> pointsOfPresence);
|
void ready(EnergyReceiver receiver);
|
||||||
|
|
||||||
|
void unready(EnergyReceiver receiver);
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package arzumify.polyenergy.api;
|
package arzumify.polyenergy.api;
|
||||||
|
|
||||||
// Receives energy from EnergyProviders. Can be implemented by anything able to tick and receive energy!
|
// Receives energy from EnergyProviders. Can be implemented by anything able to tick and receive energy!
|
||||||
public interface EnergyReceiver {
|
public interface EnergyReceiver extends EnergyBlock {
|
||||||
/**
|
/**
|
||||||
* When called by a provider, indicates that the provider exists and is ready to have energy extracted from.
|
* 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.
|
* 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 {
|
||||||
* @param provider The provider that is no longer able to provide energy to this receiver.
|
* @param provider The provider that is no longer able to provide energy to this receiver.
|
||||||
*/
|
*/
|
||||||
void unready(EnergyProvider provider);
|
void unready(EnergyProvider provider);
|
||||||
|
|
||||||
|
long receive(long amount, EnergyProvider provider);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,64 +0,0 @@
|
||||||
package arzumify.polyenergy.impl;
|
|
||||||
|
|
||||||
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
|
|
||||||
// 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 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();
|
|
||||||
receivers.clear();
|
|
||||||
matches.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addProvider(ProviderDetails provider) {
|
|
||||||
providers.add(provider);
|
|
||||||
matches.put(provider, ConcurrentHashMap.newKeySet());
|
|
||||||
// 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);
|
|
||||||
// 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);
|
|
||||||
if (matches.get(provider) != null) {
|
|
||||||
for (ReceiverDetails receiver : matches.get(provider)) {
|
|
||||||
receiver.receiver().unready(provider.provider());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
matches.remove(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removeReceiver(ReceiverDetails receiver) {
|
|
||||||
receivers.remove(receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
package arzumify.polyenergy.impl;
|
|
||||||
|
|
||||||
import arzumify.polyenergy.api.EnergyProvider;
|
|
||||||
import net.minecraft.util.math.Vec3i;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public record ProviderDetails(ArrayList<Vec3i> pointsOfPresence, EnergyProvider provider, int range) {
|
|
||||||
public static ProviderDetails NewSimple(Vec3i position, EnergyProvider provider) {
|
|
||||||
var pointsOfPresence = new ArrayList<Vec3i>();
|
|
||||||
pointsOfPresence.add(position);
|
|
||||||
return new ProviderDetails(pointsOfPresence, provider, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isInRange(ArrayList<Vec3i> pointsOfPresence) {
|
|
||||||
for (Vec3i point : pointsOfPresence) {
|
|
||||||
for (Vec3i myPoint : this.pointsOfPresence) {
|
|
||||||
var manhattanDistance = point.getManhattanDistance(myPoint);
|
|
||||||
if (manhattanDistance == 0) {
|
|
||||||
return false;
|
|
||||||
} else if (manhattanDistance <= range) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package arzumify.polyenergy.impl;
|
|
||||||
|
|
||||||
import arzumify.polyenergy.api.EnergyReceiver;
|
|
||||||
import net.minecraft.util.math.Vec3i;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public record ReceiverDetails(ArrayList<Vec3i> pointsOfPresence, EnergyReceiver receiver) {
|
|
||||||
public static ReceiverDetails NewSimple(Vec3i position, EnergyReceiver receiver) {
|
|
||||||
var pointsOfPresence = new ArrayList<Vec3i>();
|
|
||||||
pointsOfPresence.add(position);
|
|
||||||
return new ReceiverDetails(pointsOfPresence, receiver);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
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;
|
|
||||||
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 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);
|
|
||||||
this.capacity = capacity;
|
|
||||||
this.inputRate = inputRate;
|
|
||||||
this.outputRate = outputRate;
|
|
||||||
CoordinateMatchMaker.addProvider(providerDetails);
|
|
||||||
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) {
|
|
||||||
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
|
|
||||||
public void exists(EnergyReceiver receiver, ArrayList<Vec3i> pointsOfPresence) {
|
|
||||||
receiver.ready(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ready(EnergyProvider provider) {
|
|
||||||
providers.add(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unready(EnergyProvider provider) {
|
|
||||||
providers.remove(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) {
|
|
||||||
if (energy > 0) {
|
|
||||||
CoordinateMatchMaker.addProvider(providerDetails);
|
|
||||||
providing = true;
|
|
||||||
}
|
|
||||||
CoordinateMatchMaker.addReceiver(receiverDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) {
|
|
||||||
CoordinateMatchMaker.removeProvider(providerDetails);
|
|
||||||
CoordinateMatchMaker.removeReceiver(receiverDetails);
|
|
||||||
providers.clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +1,22 @@
|
||||||
{
|
{
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 1,
|
||||||
"id": "polyenergy",
|
"id": "polyenergy-cursed-edition",
|
||||||
"version": "${version}",
|
"version": "${version}",
|
||||||
"name": "Polyenergy",
|
"name": "Polyenergy",
|
||||||
"description": "A dead-simple serverside energy api",
|
"description": "A very cursed and broken serverside energy api",
|
||||||
"authors": [
|
"authors": [
|
||||||
"Arzumify"
|
"Arzumify"
|
||||||
],
|
],
|
||||||
"contact": {
|
"contact": {
|
||||||
"homepage": "https://git.ailur.dev/arzumify/polyenergy/",
|
"homepage": "https://git.ailur.dev/arzumify/polyenergy-cursed-edition/",
|
||||||
"sources": "https://git.ailur.dev/arzumify/polyenergy"
|
"sources": "https://git.ailur.dev/arzumify/polyenergy-cursed-edition/"
|
||||||
},
|
},
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"icon": "assets/polyenergy/icon-scaled.png",
|
"icon": "assets/polyenergy/icon-scaled.png",
|
||||||
"environment": "*",
|
"environment": "*",
|
||||||
"entrypoints": {
|
|
||||||
"main": [
|
|
||||||
"arzumify.polyenergy.Polyenergy"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.16.10",
|
"fabricloader": ">=0.16.10",
|
||||||
"minecraft": "~1.21.4",
|
"minecraft": ">1.20",
|
||||||
"java": ">=21",
|
"java": ">=21",
|
||||||
"fabric-api": "*"
|
"fabric-api": "*"
|
||||||
}
|
}
|
||||||
|
|
40
src/main/resources/quilt.mod.json
Normal file
40
src/main/resources/quilt.mod.json
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"quilt_loader": {
|
||||||
|
"group": "${group}",
|
||||||
|
"id": "polyenergy-cursed-edition",
|
||||||
|
"version": "${version}",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Polyenergy",
|
||||||
|
"description": "A cursed serverside energy api",
|
||||||
|
"contributors": {
|
||||||
|
"Arzumify": "Owner"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"intermediate_mappings": "net.fabricmc:intermediary",
|
||||||
|
"repositories": [
|
||||||
|
"https://maven.ailur.dev/"
|
||||||
|
],
|
||||||
|
"depends": [
|
||||||
|
{
|
||||||
|
"id": "quilt_loader",
|
||||||
|
"versions": ">=0.19.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "quilted_fabric_api",
|
||||||
|
"versions": ">=7.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "minecraft",
|
||||||
|
"versions": ">=1.20"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,412 +1,4 @@
|
||||||
import arzumify.polyenergy.impl.CoordinateMatchMaker;
|
/*
|
||||||
import net.minecraft.util.math.Vec3i;
|
TESTS ARE BROKEN IN CURSED EDITION
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
DO NOT TRY TO TEST
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
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.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 String name;
|
|
||||||
public final ProviderDetails providerDetails;
|
|
||||||
public final ReceiverDetails receiverDetails;
|
|
||||||
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.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: 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.providerDetails);
|
|
||||||
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(providerDetails);
|
|
||||||
}
|
|
||||||
return extracted;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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) {
|
|
||||||
System.out.println(name + " lost other provider");
|
|
||||||
providers.remove(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) {
|
|
||||||
if (energy > 0) {
|
|
||||||
CoordinateMatchMaker.addProvider(providerDetails);
|
|
||||||
providing = true;
|
|
||||||
}
|
|
||||||
CoordinateMatchMaker.addReceiver(receiverDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) {
|
|
||||||
CoordinateMatchMaker.removeProvider(providerDetails);
|
|
||||||
CoordinateMatchMaker.removeReceiver(receiverDetails);
|
|
||||||
providers.clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,90 +1,118 @@
|
||||||
import arzumify.polyenergy.api.EnergyProvider;
|
import arzumify.polyenergy.api.EnergyProvider;
|
||||||
import arzumify.polyenergy.api.EnergyReceiver;
|
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.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.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.math.Vec3i;
|
import net.minecraft.util.math.Vec3i;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
// Cables can have ONE provider and MANY receivers.
|
import static arzumify.polyenergy.api.SearchForNeighbours.searchForNeighbours;
|
||||||
public class SimpleCodeOnlyCable implements EnergyProvider, EnergyReceiver, ServerBlockEntityEvents.Unload, ServerBlockEntityEvents.Load {
|
|
||||||
|
/**
|
||||||
|
* 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 capacity;
|
||||||
public final long inputRate;
|
public final long inputRate;
|
||||||
public final long outputRate;
|
public final long outputRate;
|
||||||
public final String name;
|
public final String name;
|
||||||
public final ProviderDetails providerDetails;
|
public final Set<EnergyProvider> providers = ConcurrentHashMap.newKeySet();
|
||||||
public final ReceiverDetails receiverDetails;
|
public final Set<EnergyProvider> blackListed = ConcurrentHashMap.newKeySet();
|
||||||
|
public final Set<EnergyReceiver> receivers = ConcurrentHashMap.newKeySet();
|
||||||
public long energy = 0;
|
public long energy = 0;
|
||||||
public EnergyProvider currentProvider;
|
|
||||||
|
|
||||||
public SimpleCodeOnlyCable(Vec3i pos, long capacity, long inputRate, long outputRate, String name) {
|
public SimpleCodeOnlyCable(Vec3i pos, long capacity, long inputRate, long outputRate, String name) {
|
||||||
this.capacity = capacity;
|
this.capacity = capacity;
|
||||||
this.inputRate = inputRate;
|
this.inputRate = inputRate;
|
||||||
this.outputRate = outputRate;
|
this.outputRate = outputRate;
|
||||||
this.providerDetails = ProviderDetails.NewSimple(pos, this);
|
|
||||||
this.receiverDetails = ReceiverDetails.NewSimple(pos, this);
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
CoordinateMatchMaker.addReceiver(receiverDetails);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void tick(SimpleCodeOnlyCable cable) {
|
public static void tick(SimpleCodeOnlyCable Cable) {
|
||||||
System.out.println(cable.name + " ticking");
|
System.out.println(Cable.name + " ticking");
|
||||||
if (cable.currentProvider != null && cable.energy < cable.capacity) {
|
// Split the energy between all receivers
|
||||||
cable.energy += cable.currentProvider.extract(Math.min(cable.capacity - cable.energy, cable.inputRate), cable);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long extract(long amount, EnergyReceiver receiver) {
|
public long receive(long amount, EnergyProvider provider) {
|
||||||
System.out.println(name + " extracting " + amount + " energy");
|
if (!blackListed.contains(provider)) {
|
||||||
long extracted = Math.min(Math.min(outputRate, amount), energy);
|
System.out.println(name + " receiving " + amount + " energy");
|
||||||
energy -= extracted;
|
long received = Math.min(Math.min(inputRate, amount), capacity - energy);
|
||||||
System.out.println(name + " extracted " + extracted + " energy");
|
energy += received;
|
||||||
if (energy == 0) {
|
System.out.println(name + " received " + received + " energy");
|
||||||
CoordinateMatchMaker.removeProvider(providerDetails);
|
blackListed.add(provider);
|
||||||
|
return received;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return extracted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exists(EnergyReceiver receiver, ArrayList<Vec3i> pointsOfPresence) {
|
public void ready(EnergyReceiver receiver) {
|
||||||
System.out.println(name + " found other receiver");
|
System.out.println(name + " found other receiver");
|
||||||
if (currentProvider != null) {
|
receivers.add(receiver);
|
||||||
receiver.ready(currentProvider);
|
receiver.ready(this);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void ready(EnergyProvider provider) {
|
public void ready(EnergyProvider provider) {
|
||||||
System.out.println(name + " found other provider");
|
System.out.println(name + " found other provider");
|
||||||
if (currentProvider == null) {
|
providers.add(provider);
|
||||||
currentProvider = provider;
|
|
||||||
CoordinateMatchMaker.addProvider(providerDetails);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unready(EnergyReceiver receiver) {
|
||||||
|
System.out.println(name + " lost other receiver");
|
||||||
|
receivers.remove(receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unready(EnergyProvider provider) {
|
public void unready(EnergyProvider provider) {
|
||||||
if (currentProvider == provider) {
|
|
||||||
System.out.println(name + " lost other provider");
|
System.out.println(name + " lost other provider");
|
||||||
currentProvider = null;
|
providers.remove(provider);
|
||||||
CoordinateMatchMaker.removeProvider(providerDetails);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) {
|
public void onLoad(BlockEntity blockEntity, ServerWorld serverWorld) {
|
||||||
CoordinateMatchMaker.addReceiver(receiverDetails);
|
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
|
@Override
|
||||||
public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) {
|
public void onUnload(BlockEntity blockEntity, ServerWorld serverWorld) {
|
||||||
CoordinateMatchMaker.removeProvider(providerDetails);
|
for (var provider : providers) {
|
||||||
CoordinateMatchMaker.removeReceiver(receiverDetails);
|
provider.unready(this);
|
||||||
currentProvider = null;
|
}
|
||||||
|
providers.clear();
|
||||||
|
for (var receiver : receivers) {
|
||||||
|
receiver.unready(this);
|
||||||
|
}
|
||||||
|
receivers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEndTick(MinecraftServer server) {
|
||||||
|
blackListed.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue