mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-02-11 12:55:00 +01:00
Persistence is key
This commit is contained in:
parent
f4514b3e5e
commit
44d59fe793
7 changed files with 376 additions and 89 deletions
|
@ -4,6 +4,7 @@ import static net.minecraft.ChatFormatting.GOLD;
|
|||
import static net.minecraft.ChatFormatting.GRAY;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
@ -78,14 +79,6 @@ public class KineticTileEntity extends SmartTileEntity
|
|||
}
|
||||
}
|
||||
|
||||
private void addToSolver() {
|
||||
KineticSolver.getSolver(level).addNode(this);
|
||||
}
|
||||
|
||||
private void removeFromSolver() {
|
||||
KineticSolver.getSolver(level).removeNode(this);
|
||||
}
|
||||
|
||||
public KineticConnections getConnections() {
|
||||
return connections;
|
||||
}
|
||||
|
@ -102,6 +95,10 @@ public class KineticTileEntity extends SmartTileEntity
|
|||
return getDefaultStressCapacity();
|
||||
}
|
||||
|
||||
public boolean isStressConstant() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public float getDefaultStressImpact() {
|
||||
return (float) BlockStressValues.getImpact(getStressConfigKey());
|
||||
}
|
||||
|
@ -110,11 +107,18 @@ public class KineticTileEntity extends SmartTileEntity
|
|||
return (float) BlockStressValues.getCapacity(getStressConfigKey());
|
||||
}
|
||||
|
||||
public Optional<Float> isConnected(BlockPos to) {
|
||||
return KineticSolver.getSolver(level).isConnected(this.getBlockPos(), to);
|
||||
}
|
||||
|
||||
public boolean isStressOnlyConnected(BlockPos to) {
|
||||
return KineticSolver.getSolver(level).isStressOnlyConnected(this.getBlockPos(), to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (!level.isClientSide) {
|
||||
addToSolver();
|
||||
KineticSolver.getSolver(level).addNode(this);
|
||||
}
|
||||
|
||||
super.initialize();
|
||||
|
@ -125,7 +129,7 @@ public class KineticTileEntity extends SmartTileEntity
|
|||
super.tick();
|
||||
effects.tick();
|
||||
|
||||
if (!level.isClientSide && !isRemoved()) {
|
||||
if (!level.isClientSide) {
|
||||
KineticSolver.getSolver(level).updateNode(this);
|
||||
}
|
||||
|
||||
|
@ -144,6 +148,29 @@ public class KineticTileEntity extends SmartTileEntity
|
|||
flickerTally = getFlickerScore() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setRemovedNotDueToChunkUnload() {
|
||||
if (!level.isClientSide) {
|
||||
KineticSolver.getSolver(level).removeNode(this);
|
||||
}
|
||||
super.setRemovedNotDueToChunkUnload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkUnloaded() {
|
||||
super.onChunkUnloaded();
|
||||
if (!level.isClientSide) {
|
||||
preKineticsUnloaded();
|
||||
KineticSolver solver = KineticSolver.getSolver(level);
|
||||
solver.updateNode(this);
|
||||
solver.unloadNode(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void preKineticsUnloaded() {}
|
||||
|
||||
|
||||
|
||||
private void validateKinetics() {
|
||||
// if (hasSource()) {
|
||||
// if (!hasNetwork()) {
|
||||
|
@ -215,14 +242,6 @@ public class KineticTileEntity extends SmartTileEntity
|
|||
super.setRemoved();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setRemovedNotDueToChunkUnload() {
|
||||
if (!level.isClientSide) {
|
||||
removeFromSolver();
|
||||
}
|
||||
super.setRemovedNotDueToChunkUnload();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(CompoundTag compound, boolean clientPacket) {
|
||||
compound.putFloat("Speed", speed);
|
||||
|
|
|
@ -29,10 +29,10 @@ public class SimpleKineticTileEntity extends KineticTileEntity {
|
|||
|
||||
@Override
|
||||
public float getGeneratedSpeed() {
|
||||
Block block = getBlockState().getBlock();
|
||||
BlockEntity below = level.getBlockEntity(getBlockPos().below());
|
||||
if (block instanceof ICogWheel cog && cog.isLargeCog()
|
||||
&& below instanceof SpeedControllerTileEntity controller && controller.getSpeed() != 0)
|
||||
BlockPos belowPos = getBlockPos().below();
|
||||
if (isStressOnlyConnected(belowPos)
|
||||
&& level.getBlockEntity(belowPos) instanceof SpeedControllerTileEntity controller
|
||||
&& controller.getSpeed() != 0)
|
||||
return controller.getTargetSpeed();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,40 +1,24 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import com.simibubi.create.foundation.utility.DirectionHelper;
|
||||
import com.simibubi.create.foundation.utility.LazyMap;
|
||||
import com.simibubi.create.content.contraptions.solver.KineticConnections.Type;
|
||||
import com.simibubi.create.content.contraptions.solver.KineticConnections.Types;
|
||||
import com.simibubi.create.content.contraptions.solver.KineticConnections.Entry;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Direction.Axis;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.AXIS;
|
||||
|
||||
public class AllConnections {
|
||||
|
||||
public static record ValueType(Object value) implements Type {
|
||||
@Override
|
||||
public boolean compatible(Type other) {
|
||||
return this == other;
|
||||
}
|
||||
|
||||
public static <T> LazyMap<T, Type> map() {
|
||||
return new LazyMap<>(ValueType::new);
|
||||
}
|
||||
}
|
||||
|
||||
public static final LazyMap<Axis, Type>
|
||||
TYPE_SHAFT = ValueType.map(),
|
||||
TYPE_LARGE_COG = ValueType.map(),
|
||||
TYPE_SMALL_COG = ValueType.map(),
|
||||
TYPE_SPEED_CONTROLLER_TOP = ValueType.map();
|
||||
|
||||
|
||||
private static Direction pos(Axis axis) {
|
||||
return Direction.get(Direction.AxisDirection.POSITIVE, axis);
|
||||
}
|
||||
|
@ -47,7 +31,7 @@ public class AllConnections {
|
|||
int fromDiff = from.choose(diff.getX(), diff.getY(), diff.getZ());
|
||||
int toDiff = to.choose(diff.getX(), diff.getY(), diff.getZ());
|
||||
float ratio = fromDiff > 0 ^ toDiff > 0 ? -1 : 1;
|
||||
return new Entry(diff, TYPE_LARGE_COG.apply(from), TYPE_LARGE_COG.apply(to), ratio);
|
||||
return new Entry(diff, Type.of(Types.LARGE_COG, from), Type.of(Types.LARGE_COG, to), ratio);
|
||||
}
|
||||
|
||||
private static Optional<Axis> oppAxis(Axis axis) {
|
||||
|
@ -63,14 +47,14 @@ public class AllConnections {
|
|||
|
||||
public static final LazyMap<Direction, KineticConnections>
|
||||
HALF_SHAFT = new LazyMap<>(dir ->
|
||||
new KineticConnections(new Entry(dir.getNormal(), TYPE_SHAFT.apply(dir.getAxis()))));
|
||||
new KineticConnections(new Entry(dir.getNormal(), Type.of(Types.SHAFT, dir.getAxis()))));
|
||||
|
||||
public static final LazyMap<Axis, KineticConnections>
|
||||
FULL_SHAFT = new LazyMap<>(axis -> HALF_SHAFT.apply(pos(axis)).merge(HALF_SHAFT.apply(neg(axis)))),
|
||||
|
||||
LARGE_COG = new LazyMap<>(axis -> {
|
||||
Type large = TYPE_LARGE_COG.apply(axis);
|
||||
Type small = TYPE_SMALL_COG.apply(axis);
|
||||
Type large = Type.of(Types.LARGE_COG, axis);
|
||||
Type small = Type.of(Types.SMALL_COG, axis);
|
||||
|
||||
List<Entry> out = new LinkedList<>();
|
||||
Direction cur = DirectionHelper.getPositivePerpendicular(axis);
|
||||
|
@ -83,15 +67,15 @@ public class AllConnections {
|
|||
}
|
||||
|
||||
oppAxis(axis).ifPresent(opp -> {
|
||||
Type sc = TYPE_SPEED_CONTROLLER_TOP.apply(opp);
|
||||
Type sc = Type.of(Types.SPEED_CONTROLLER_TOP, opp);
|
||||
out.add(new Entry(Direction.DOWN.getNormal(), large, sc).stressOnly());
|
||||
});
|
||||
return new KineticConnections(out);
|
||||
}),
|
||||
|
||||
SMALL_COG = new LazyMap<>(axis -> {
|
||||
Type large = TYPE_LARGE_COG.apply(axis);
|
||||
Type small = TYPE_SMALL_COG.apply(axis);
|
||||
Type large = Type.of(Types.LARGE_COG, axis);
|
||||
Type small = Type.of(Types.SMALL_COG, axis);
|
||||
|
||||
List<Entry> out = new LinkedList<>();
|
||||
Direction cur = DirectionHelper.getPositivePerpendicular(axis);
|
||||
|
@ -110,8 +94,8 @@ public class AllConnections {
|
|||
SMALL_COG_FULL_SHAFT = new LazyMap<>(axis -> SMALL_COG.apply(axis).merge(FULL_SHAFT.apply(axis))),
|
||||
|
||||
SPEED_CONTROLLER = new LazyMap<>(axis -> {
|
||||
Type sc = TYPE_SPEED_CONTROLLER_TOP.apply(axis);
|
||||
Type large = TYPE_LARGE_COG.apply(oppAxis(axis).get());
|
||||
Type sc = Type.of(Types.SPEED_CONTROLLER_TOP, axis);
|
||||
Type large = Type.of(Types.LARGE_COG, oppAxis(axis).get());
|
||||
Vec3i up = Direction.UP.getNormal();
|
||||
return new KineticConnections(new Entry(up, sc, large).stressOnly()).merge(FULL_SHAFT.apply(axis));
|
||||
});
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import com.simibubi.create.foundation.utility.NBTHelper;
|
||||
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -15,8 +23,51 @@ import java.util.stream.Stream;
|
|||
|
||||
public class KineticConnections {
|
||||
|
||||
public interface Type {
|
||||
boolean compatible(Type other);
|
||||
public enum Types {
|
||||
SHAFT, LARGE_COG, SMALL_COG, SPEED_CONTROLLER_TOP
|
||||
}
|
||||
|
||||
public static class Type {
|
||||
private final Types name;
|
||||
private final String value;
|
||||
|
||||
private Type(Types name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private final static Interner<Type> cachedTypes = Interners.newStrongInterner();
|
||||
|
||||
public static Type of(Types name, String value) {
|
||||
return cachedTypes.intern(new Type(name, value));
|
||||
}
|
||||
|
||||
public static Type of(Types name, StringRepresentable value) {
|
||||
return of(name, value.getSerializedName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Type type1 = (Type) o;
|
||||
return name == type1.name && Objects.equals(value, type1.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, value);
|
||||
}
|
||||
|
||||
public CompoundTag save(CompoundTag tag) {
|
||||
NBTHelper.writeEnum(tag, "Name", name);
|
||||
tag.putString("Value", value);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static Type load(CompoundTag tag) {
|
||||
return Type.of(NBTHelper.readEnum(tag, "Name", Types.class), tag.getString("Value"));
|
||||
}
|
||||
}
|
||||
|
||||
public static record Entry(Vec3i offset, Value value) {
|
||||
|
@ -74,7 +125,7 @@ public class KineticConnections {
|
|||
|
||||
if (fromValue.isStressOnly() || toValue.isStressOnly()) return Optional.empty();
|
||||
|
||||
if (fromValue.from.compatible(toValue.to) && fromValue.to.compatible(toValue.from)
|
||||
if (fromValue.from.equals(toValue.to) && fromValue.to.equals(toValue.from)
|
||||
&& (Mth.equal(fromValue.ratio, 1/toValue.ratio) || (Mth.equal(toValue.ratio, 1/fromValue.ratio))))
|
||||
return Optional.of(fromValue.ratio);
|
||||
return Optional.empty();
|
||||
|
@ -89,7 +140,7 @@ public class KineticConnections {
|
|||
|
||||
if (!fromValue.isStressOnly() || !toValue.isStressOnly()) return false;
|
||||
|
||||
return fromValue.from.compatible(toValue.to) && fromValue.to.compatible(toValue.from);
|
||||
return fromValue.from.equals(toValue.to) && fromValue.to.equals(toValue.from);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,5 +167,32 @@ public class KineticConnections {
|
|||
return connections.values().stream().anyMatch(Value::isStressOnly);
|
||||
}
|
||||
|
||||
public CompoundTag save(CompoundTag tag) {
|
||||
ListTag connectionsTags = new ListTag();
|
||||
for (Map.Entry<Vec3i, Value> entry : connections.entrySet()) {
|
||||
CompoundTag entryTag = new CompoundTag();
|
||||
entryTag.put("Off", NBTHelper.writeVec3i(entry.getKey()));
|
||||
entryTag.put("From", entry.getValue().from().save(new CompoundTag()));
|
||||
entryTag.put("To", entry.getValue().to().save(new CompoundTag()));
|
||||
entryTag.putFloat("Ratio", entry.getValue().ratio());
|
||||
connectionsTags.add(entryTag);
|
||||
}
|
||||
tag.put("Connections", connectionsTags);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static KineticConnections load(CompoundTag tag) {
|
||||
Map<Vec3i, Value> connections = new HashMap<>();
|
||||
tag.getList("Connections", Tag.TAG_COMPOUND).forEach(c -> {
|
||||
CompoundTag comp = (CompoundTag) c;
|
||||
Vec3i offset = NBTHelper.readVec3i(comp.getList("Off", Tag.TAG_INT));
|
||||
Type from = Type.load(comp.getCompound("From"));
|
||||
Type to = Type.load(comp.getCompound("To"));
|
||||
float ratio = comp.getFloat("Ratio");
|
||||
connections.put(offset, new Value(from, to, ratio));
|
||||
});
|
||||
return new KineticConnections(connections);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -47,12 +47,15 @@ public class KineticNetwork {
|
|||
if (node.isGenerator() && !generators.contains(node)) {
|
||||
generators.add(node);
|
||||
rootSpeedDirty = true;
|
||||
rootSpeedChanged = true;
|
||||
}
|
||||
if (node.hasStressImpact()) onMemberStressImpactUpdated();
|
||||
if (node.hasStressCapacity()) onMemberStressCapacityUpdated();
|
||||
}
|
||||
|
||||
public void onMemberLoaded(KineticNode node) {
|
||||
potentialNewBranches.add(node);
|
||||
}
|
||||
|
||||
public void onMemberGeneratedSpeedUpdated(KineticNode node) {
|
||||
if (node.isGenerator()) {
|
||||
generators.add(node);
|
||||
|
@ -60,7 +63,7 @@ public class KineticNetwork {
|
|||
generators.remove(node);
|
||||
}
|
||||
rootSpeedDirty = true;
|
||||
rootSpeedChanged = true;
|
||||
onMemberStressCapacityUpdated();
|
||||
}
|
||||
|
||||
public void onMemberStressImpactUpdated() {
|
||||
|
@ -75,7 +78,6 @@ public class KineticNetwork {
|
|||
if (node.isGenerator() && generators.contains(node)) {
|
||||
generators.remove(node);
|
||||
rootSpeedDirty = true;
|
||||
rootSpeedChanged = true;
|
||||
}
|
||||
if (node.hasStressImpact()) onMemberStressImpactUpdated();
|
||||
if (node.hasStressCapacity()) onMemberStressCapacityUpdated();
|
||||
|
@ -131,7 +133,11 @@ public class KineticNetwork {
|
|||
}
|
||||
}
|
||||
|
||||
rootTheoreticalSpeed = newSpeed * sign;
|
||||
if (rootTheoreticalSpeed != newSpeed * sign) {
|
||||
rootTheoreticalSpeed = newSpeed * sign;
|
||||
onRootSpeedChanged();
|
||||
}
|
||||
|
||||
mainGenerator = newGenerator;
|
||||
rootSpeedDirty = false;
|
||||
|
||||
|
@ -150,6 +156,11 @@ public class KineticNetwork {
|
|||
return isStopped() ? 0 : rootTheoreticalSpeed;
|
||||
}
|
||||
|
||||
private void onRootSpeedChanged() {
|
||||
rootSpeedChanged = true;
|
||||
onMemberStressImpactUpdated();
|
||||
}
|
||||
|
||||
public void untick() {
|
||||
ticked = false;
|
||||
}
|
||||
|
@ -180,13 +191,13 @@ public class KineticNetwork {
|
|||
} else if (nowOverstressed) {
|
||||
if (!cur.overstressed) {
|
||||
cur.overstressed = true;
|
||||
rootSpeedChanged = true;
|
||||
cur.onRootSpeedChanged();
|
||||
cur.members.forEach(KineticNode::stop);
|
||||
}
|
||||
} else {
|
||||
if (cur.overstressed) {
|
||||
cur.overstressed = false;
|
||||
rootSpeedChanged = true;
|
||||
cur.onRootSpeedChanged();
|
||||
cur.bulldozeContradictingMembers(newNetworks);
|
||||
cur.updateMemberSpeeds(newNetworks);
|
||||
}
|
||||
|
@ -201,16 +212,16 @@ public class KineticNetwork {
|
|||
* @param newNetworks a List that any new networks created during this call will be added to
|
||||
*/
|
||||
private void updateMemberSpeeds(List<KineticNetwork> newNetworks) {
|
||||
SolveResult recalculateSpeedResult = tryRecalculateSpeed();
|
||||
// generators should not be turning against each other or have conflicting cycles by now
|
||||
assert(recalculateSpeedResult.isOk());
|
||||
|
||||
// if we're stopped, then all members' speeds will be 0, so no need to check for speeding nodes
|
||||
if (isStopped()) {
|
||||
members.forEach(KineticNode::stop);
|
||||
return;
|
||||
}
|
||||
|
||||
SolveResult recalculateSpeedResult = tryRecalculateSpeed();
|
||||
// generators should not be turning against each other or have conflicting cycles by now
|
||||
assert(recalculateSpeedResult.isOk());
|
||||
|
||||
if (rootSpeedChanged) {
|
||||
// root speed changed, update all nodes starting from the main generator
|
||||
rootSpeedChanged = false;
|
||||
|
@ -220,8 +231,8 @@ public class KineticNetwork {
|
|||
potentialNewBranches.stream()
|
||||
.filter(n -> !potentialNewBranches.contains(n.getSource()))
|
||||
.forEach(n -> bfs(n, newNetworks, true));
|
||||
potentialNewBranches.clear();
|
||||
}
|
||||
potentialNewBranches.clear();
|
||||
}
|
||||
|
||||
private void bfs(KineticNode root, List<KineticNetwork> newNetworks, boolean followSource) {
|
||||
|
|
|
@ -5,6 +5,8 @@ import com.simibubi.create.foundation.config.AllConfigs;
|
|||
import com.simibubi.create.foundation.utility.Pair;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -12,39 +14,90 @@ import javax.annotation.Nullable;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class KineticNode {
|
||||
|
||||
private final Function<BlockPos, Optional<KineticNode>> nodeAccessor;
|
||||
private final KineticTileEntity entity;
|
||||
private final KineticSolver solver;
|
||||
private @Nullable KineticTileEntity entity;
|
||||
|
||||
private @Nullable KineticNode source;
|
||||
private KineticNetwork network;
|
||||
private float speedRatio = 1;
|
||||
private float speedCur;
|
||||
private float speedNext;
|
||||
|
||||
private final BlockPos pos;
|
||||
private final KineticConnections connections;
|
||||
private float generatedSpeed;
|
||||
private float stressCapacity;
|
||||
private float stressImpact;
|
||||
private final boolean constantStress;
|
||||
|
||||
private float speedCur;
|
||||
private float speedNext;
|
||||
|
||||
public KineticNode(KineticTileEntity entity, Function<BlockPos, Optional<KineticNode>> nodeAccessor) {
|
||||
this.nodeAccessor = nodeAccessor;
|
||||
public KineticNode(KineticSolver solver, KineticTileEntity entity) {
|
||||
this.solver = solver;
|
||||
this.entity = entity;
|
||||
|
||||
this.pos = entity.getBlockPos();
|
||||
this.connections = entity.getConnections();
|
||||
this.generatedSpeed = entity.getGeneratedSpeed();
|
||||
this.stressImpact = entity.getStressImpact();
|
||||
this.stressCapacity = entity.getStressCapacity();
|
||||
this.constantStress = entity.isStressConstant();
|
||||
|
||||
this.network = new KineticNetwork(this);
|
||||
}
|
||||
|
||||
private KineticNode(KineticSolver solver, BlockPos pos, KineticConnections connections, float generatedSpeed,
|
||||
float stressCapacity, float stressImpact, boolean constantStress) {
|
||||
this.solver = solver;
|
||||
|
||||
this.pos = pos;
|
||||
this.connections = connections;
|
||||
this.generatedSpeed = generatedSpeed;
|
||||
this.stressImpact = stressImpact;
|
||||
this.stressCapacity = stressCapacity;
|
||||
this.constantStress = constantStress;
|
||||
|
||||
this.network = new KineticNetwork(this);
|
||||
}
|
||||
|
||||
public CompoundTag save(CompoundTag tag) {
|
||||
tag.put("Pos", NbtUtils.writeBlockPos(pos));
|
||||
tag.put("Connections", connections.save(new CompoundTag()));
|
||||
tag.putFloat("Generated", generatedSpeed);
|
||||
tag.putFloat("Capacity", stressCapacity);
|
||||
tag.putFloat("Impact", stressImpact);
|
||||
if (constantStress)
|
||||
tag.putBoolean("ConstantStress", true);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static KineticNode load(KineticSolver solver, CompoundTag tag) {
|
||||
BlockPos pos = NbtUtils.readBlockPos(tag.getCompound("Pos"));
|
||||
KineticConnections connections = KineticConnections.load(tag.getCompound("Connections"));
|
||||
float generatedSpeed = tag.getFloat("Generated");
|
||||
float stressCapacity = tag.getFloat("Capacity");
|
||||
float stressImpact = tag.getFloat("Impact");
|
||||
boolean constantStress = tag.getBoolean("ConstantStress");
|
||||
return new KineticNode(solver, pos, connections, generatedSpeed, stressCapacity, stressImpact, constantStress);
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return entity != null;
|
||||
}
|
||||
|
||||
public void onLoaded(KineticTileEntity entity) {
|
||||
this.entity = entity;
|
||||
network.onMemberLoaded(this);
|
||||
if (speedCur != 0) entity.setSpeed(speedCur);
|
||||
}
|
||||
|
||||
public void onUnloaded() {
|
||||
this.entity = null;
|
||||
}
|
||||
|
||||
public KineticConnections getConnections() {
|
||||
return connections;
|
||||
}
|
||||
|
@ -53,13 +106,17 @@ public class KineticNode {
|
|||
return network;
|
||||
}
|
||||
|
||||
public BlockPos getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a Stream containing a pair for each compatible connection with this node, where the first value is
|
||||
* the connecting node and the second value is the speed ratio of the connection
|
||||
*/
|
||||
public Stream<Pair<KineticNode, Float>> getActiveConnections() {
|
||||
return connections.getDirections().stream()
|
||||
.map(d -> nodeAccessor.apply(entity.getBlockPos().offset(d))
|
||||
.map(d -> solver.getNode(pos.offset(d))
|
||||
.map(n -> connections.checkConnection(n.connections, d)
|
||||
.map(r -> Pair.of(n, r))))
|
||||
.flatMap(Optional::stream)
|
||||
|
@ -72,7 +129,7 @@ public class KineticNode {
|
|||
|
||||
public Stream<KineticNetwork> getActiveStressOnlyConnections() {
|
||||
return connections.getDirections().stream()
|
||||
.map(d -> nodeAccessor.apply(entity.getBlockPos().offset(d))
|
||||
.map(d -> solver.getNode(pos.offset(d))
|
||||
.filter(n -> connections.checkStressOnlyConnection(n.connections, d)))
|
||||
.flatMap(Optional::stream)
|
||||
.map(KineticNode::getNetwork);
|
||||
|
@ -86,10 +143,15 @@ public class KineticNode {
|
|||
return generatedSpeed != 0;
|
||||
}
|
||||
|
||||
public void onUpdated() {
|
||||
public boolean onUpdated() {
|
||||
if (entity == null) return false;
|
||||
|
||||
boolean changed = false;
|
||||
|
||||
float generatedSpeedNew = entity.getGeneratedSpeed();
|
||||
if (this.generatedSpeed != generatedSpeedNew) {
|
||||
this.generatedSpeed = generatedSpeedNew;
|
||||
changed = true;
|
||||
network.onMemberGeneratedSpeedUpdated(this);
|
||||
if (network.tryRecalculateSpeed().isContradiction()) {
|
||||
popBlock();
|
||||
|
@ -99,14 +161,18 @@ public class KineticNode {
|
|||
float stressImpactNew = entity.getStressImpact();
|
||||
if (this.stressImpact != stressImpactNew) {
|
||||
this.stressImpact = stressImpactNew;
|
||||
changed = true;
|
||||
network.onMemberStressImpactUpdated();
|
||||
}
|
||||
|
||||
float stressCapacityNew = entity.getStressCapacity();
|
||||
if (this.stressCapacity != stressCapacityNew) {
|
||||
this.stressCapacity = stressCapacityNew;
|
||||
changed = true;
|
||||
network.onMemberStressCapacityUpdated();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
public boolean hasStressCapacity() {
|
||||
|
@ -122,11 +188,11 @@ public class KineticNode {
|
|||
}
|
||||
|
||||
public float getStressCapacity() {
|
||||
return Math.abs(stressCapacity * generatedSpeed);
|
||||
return constantStress ? stressCapacity : stressCapacity * Math.abs(generatedSpeed);
|
||||
}
|
||||
|
||||
public float getTotalStressImpact(float speedAtRoot) {
|
||||
return Math.abs(stressImpact * getTheoreticalSpeed(speedAtRoot));
|
||||
return constantStress ? stressImpact : stressImpact * Math.abs(getTheoreticalSpeed(speedAtRoot));
|
||||
}
|
||||
|
||||
private SolveResult setNetwork(KineticNetwork network) {
|
||||
|
@ -233,13 +299,18 @@ public class KineticNode {
|
|||
public void flushChangedSpeed() {
|
||||
if (speedCur != speedNext) {
|
||||
speedCur = speedNext;
|
||||
entity.setSpeed(speedCur);
|
||||
if (entity != null) {
|
||||
entity.setSpeed(speedCur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void popBlock() {
|
||||
// this should cause the node to get removed from the solver and lead to onRemoved() being called
|
||||
entity.getLevel().destroyBlock(entity.getBlockPos(), true);
|
||||
if (entity != null) {
|
||||
solver.removeAndPopNow(entity);
|
||||
} else {
|
||||
solver.removeAndQueuePop(pos);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,30 +10,108 @@ import java.util.Set;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
|
||||
import com.simibubi.create.foundation.utility.Pair;
|
||||
import com.simibubi.create.foundation.utility.WorldAttached;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.saveddata.SavedData;
|
||||
|
||||
public class KineticSolver {
|
||||
import org.checkerframework.checker.units.qual.C;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
private static final WorldAttached<KineticSolver> SOLVERS = new WorldAttached<>($ -> new KineticSolver());
|
||||
public class KineticSolver extends SavedData {
|
||||
|
||||
public static final String DATA_FILE_NAME = "kinetics";
|
||||
|
||||
private static final WorldAttached<KineticSolver> SOLVERS = new WorldAttached<>(levelAccessor -> {
|
||||
if (levelAccessor instanceof ServerLevel level)
|
||||
return level.getDataStorage().computeIfAbsent(KineticSolver::load, KineticSolver::new, DATA_FILE_NAME);
|
||||
return new KineticSolver();
|
||||
});
|
||||
|
||||
public static KineticSolver getSolver(Level level) {
|
||||
return SOLVERS.get(level);
|
||||
}
|
||||
|
||||
private final Map<BlockPos, KineticNode> nodes = new HashMap<>();
|
||||
private final Set<BlockPos> popQueue = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public @NotNull CompoundTag save(@NotNull CompoundTag tag) {
|
||||
ListTag popQueueTag = new ListTag();
|
||||
for (BlockPos pos : popQueue) {
|
||||
popQueueTag.add(NbtUtils.writeBlockPos(pos));
|
||||
}
|
||||
tag.put("PopQueue", popQueueTag);
|
||||
|
||||
ListTag nodesTag = new ListTag();
|
||||
for (KineticNode node : nodes.values()) {
|
||||
nodesTag.add(node.save(new CompoundTag()));
|
||||
}
|
||||
tag.put("Nodes", nodesTag);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
private static KineticSolver load(CompoundTag tag) {
|
||||
KineticSolver out = new KineticSolver();
|
||||
|
||||
tag.getList("PopQueue", Tag.TAG_COMPOUND).forEach(c ->
|
||||
out.popQueue.add(NbtUtils.readBlockPos((CompoundTag) c)));
|
||||
|
||||
tag.getList("Nodes", Tag.TAG_COMPOUND).forEach(c ->
|
||||
out.addUnloadedNode(KineticNode.load(out, (CompoundTag) c)));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private void addUnloadedNode(KineticNode node) {
|
||||
KineticNode nodePrev = nodes.remove(node.getPos());
|
||||
if (nodePrev != null) nodePrev.onRemoved();
|
||||
|
||||
nodes.put(node.getPos(), node);
|
||||
node.onAdded();
|
||||
}
|
||||
|
||||
public void addNode(KineticTileEntity entity) {
|
||||
removeNode(entity);
|
||||
KineticNode node = new KineticNode(entity, this::getNode);
|
||||
nodes.put(entity.getBlockPos(), node);
|
||||
BlockPos pos = entity.getBlockPos();
|
||||
if (popQueue.contains(pos)) {
|
||||
popQueue.remove(pos);
|
||||
popBlock(entity.getLevel(), pos);
|
||||
setDirty();
|
||||
return;
|
||||
}
|
||||
|
||||
KineticNode nodePrev = nodes.get(pos);
|
||||
if (nodePrev != null) {
|
||||
if (nodePrev.isLoaded()) {
|
||||
nodes.remove(pos);
|
||||
nodePrev.onRemoved();
|
||||
} else {
|
||||
// a node exists here but is unloaded, so just load it with this entity instead of replacing
|
||||
nodePrev.onLoaded(entity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
KineticNode node = new KineticNode(this, entity);
|
||||
nodes.put(pos, node);
|
||||
node.onAdded();
|
||||
setDirty();
|
||||
}
|
||||
|
||||
public void updateNode(KineticTileEntity entity) {
|
||||
KineticNode node = nodes.get(entity.getBlockPos());
|
||||
if (node == null) return;
|
||||
|
||||
if (!node.getConnections().equals(entity.getConnections())) {
|
||||
// connections changed, so things could've been disconnected
|
||||
|
@ -41,17 +119,49 @@ public class KineticSolver {
|
|||
addNode(entity);
|
||||
} else {
|
||||
// connections are the same, so just update in case other properties changed
|
||||
node.onUpdated();
|
||||
if (node.onUpdated()) {
|
||||
setDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unloadNode(KineticTileEntity entity) {
|
||||
KineticNode node = nodes.get(entity.getBlockPos());
|
||||
if (node != null) node.onUnloaded();
|
||||
}
|
||||
|
||||
protected Optional<KineticNode> getNode(BlockPos pos) {
|
||||
return Optional.ofNullable(nodes.get(pos));
|
||||
}
|
||||
|
||||
public void removeNode(KineticTileEntity entity) {
|
||||
KineticNode node = nodes.remove(entity.getBlockPos());
|
||||
if (node != null) node.onRemoved();
|
||||
if (node != null) {
|
||||
node.onRemoved();
|
||||
setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeAndQueuePop(BlockPos pos) {
|
||||
KineticNode node = nodes.remove(pos);
|
||||
if (node != null) {
|
||||
popQueue.add(pos);
|
||||
node.onRemoved();
|
||||
setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeAndPopNow(KineticTileEntity entity) {
|
||||
KineticNode node = nodes.remove(entity.getBlockPos());
|
||||
if (node != null) {
|
||||
popBlock(entity.getLevel(), entity.getBlockPos());
|
||||
node.onRemoved();
|
||||
setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
private void popBlock(Level level, BlockPos pos) {
|
||||
level.destroyBlock(pos, true);
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
|
@ -69,4 +179,18 @@ public class KineticSolver {
|
|||
}
|
||||
}
|
||||
|
||||
public Optional<Float> isConnected(BlockPos from, BlockPos to) {
|
||||
return getNode(from).flatMap(fromNode ->
|
||||
getNode(to).flatMap(toNode ->
|
||||
fromNode.getConnections()
|
||||
.checkConnection(toNode.getConnections(), to.subtract(from))));
|
||||
}
|
||||
|
||||
public boolean isStressOnlyConnected(BlockPos from, BlockPos to) {
|
||||
return getNode(from).flatMap(fromNode ->
|
||||
getNode(to).map(toNode ->
|
||||
fromNode.getConnections()
|
||||
.checkStressOnlyConnection(toNode.getConnections(), to.subtract(from)))
|
||||
).orElse(false);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue