Deserialization magic

This commit is contained in:
reidbhuntley 2022-01-02 12:16:16 -05:00
parent 44d59fe793
commit 6d3c3e0d1f
14 changed files with 365 additions and 521 deletions

View file

@ -9,7 +9,6 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour; import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
import com.simibubi.create.content.CreateItemGroup; import com.simibubi.create.content.CreateItemGroup;
import com.simibubi.create.content.contraptions.TorquePropagator;
import com.simibubi.create.content.contraptions.components.flywheel.engine.FurnaceEngineModifiers; import com.simibubi.create.content.contraptions.components.flywheel.engine.FurnaceEngineModifiers;
import com.simibubi.create.content.curiosities.weapons.BuiltinPotatoProjectileTypes; import com.simibubi.create.content.curiosities.weapons.BuiltinPotatoProjectileTypes;
import com.simibubi.create.content.logistics.RedstoneLinkNetworkHandler; import com.simibubi.create.content.logistics.RedstoneLinkNetworkHandler;
@ -71,7 +70,6 @@ public class Create {
public static final ServerSchematicLoader SCHEMATIC_RECEIVER = new ServerSchematicLoader(); public static final ServerSchematicLoader SCHEMATIC_RECEIVER = new ServerSchematicLoader();
public static final RedstoneLinkNetworkHandler REDSTONE_LINK_NETWORK_HANDLER = new RedstoneLinkNetworkHandler(); public static final RedstoneLinkNetworkHandler REDSTONE_LINK_NETWORK_HANDLER = new RedstoneLinkNetworkHandler();
public static final TorquePropagator TORQUE_PROPAGATOR = new TorquePropagator();
public static final ServerLagger LAGGER = new ServerLagger(); public static final ServerLagger LAGGER = new ServerLagger();
public static final Random RANDOM = new Random(); public static final Random RANDOM = new Random();

View file

@ -1,194 +0,0 @@
package com.simibubi.create.content.contraptions;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.components.flywheel.FlywheelTileEntity;
import com.simibubi.create.foundation.advancement.AllTriggers;
public class KineticNetwork {
public Long id;
public boolean initialized;
public boolean containsFlywheel;
public Map<KineticTileEntity, Float> sources;
public Map<KineticTileEntity, Float> members;
private float currentCapacity;
private float currentStress;
private float unloadedCapacity;
private float unloadedStress;
private int unloadedMembers;
public KineticNetwork() {
sources = new HashMap<>();
members = new HashMap<>();
containsFlywheel = false;
}
public void initFromTE(float maxStress, float currentStress, int members) {
unloadedCapacity = maxStress;
unloadedStress = currentStress;
unloadedMembers = members;
initialized = true;
updateStress();
updateCapacity();
}
public void addSilently(KineticTileEntity te, float lastCapacity, float lastStress) {
if (members.containsKey(te))
return;
if (te.isSource()) {
unloadedCapacity -= lastCapacity * getStressMultiplierForSpeed(te.getGeneratedSpeed());
float addedStressCapacity = te.calculateAddedStressCapacity();
sources.put(te, addedStressCapacity);
containsFlywheel |= te instanceof FlywheelTileEntity;
}
unloadedStress -= lastStress * getStressMultiplierForSpeed(te.getTheoreticalSpeed());
float stressApplied = te.calculateStressApplied();
members.put(te, stressApplied);
unloadedMembers--;
if (unloadedMembers < 0)
unloadedMembers = 0;
if (unloadedCapacity < 0)
unloadedCapacity = 0;
if (unloadedStress < 0)
unloadedStress = 0;
}
public void add(KineticTileEntity te) {
if (members.containsKey(te))
return;
if (te.isSource())
sources.put(te, te.calculateAddedStressCapacity());
members.put(te, te.calculateStressApplied());
updateFromNetwork(te);
//te.networkDirty = true;
}
public void updateCapacityFor(KineticTileEntity te, float capacity) {
sources.put(te, capacity);
updateCapacity();
}
public void updateStressFor(KineticTileEntity te, float stress) {
members.put(te, stress);
updateStress();
}
public void remove(KineticTileEntity te) {
if (!members.containsKey(te))
return;
if (te.isSource())
sources.remove(te);
members.remove(te);
te.updateFromNetwork(0, 0, 0);
if (members.isEmpty()) {
TorquePropagator.networks.get(te.getLevel())
.remove(this.id);
return;
}
// members.keySet()
// .stream()
// .findFirst()
// .map(member -> member.networkDirty = true);
}
public void sync() {
for (KineticTileEntity te : members.keySet())
updateFromNetwork(te);
}
private void updateFromNetwork(KineticTileEntity te) {
boolean wasOverStressed = te.isOverStressed();
te.updateFromNetwork(currentCapacity, currentStress, getSize());
if (!wasOverStressed && te.isOverStressed() && te.getTheoreticalSpeed() != 0) {
AllTriggers.triggerForNearbyPlayers(AllTriggers.OVERSTRESSED, te.getLevel(), te.getBlockPos(), 4);
if (containsFlywheel)
AllTriggers.triggerForNearbyPlayers(AllTriggers.OVERSTRESS_FLYWHEEL, te.getLevel(), te.getBlockPos(), 4);
}
}
public void updateCapacity() {
float newMaxStress = calculateCapacity();
if (currentCapacity != newMaxStress) {
currentCapacity = newMaxStress;
sync();
}
}
public void updateStress() {
float newStress = calculateStress();
if (currentStress != newStress) {
currentStress = newStress;
sync();
}
}
public void updateNetwork() {
float newStress = calculateStress();
float newMaxStress = calculateCapacity();
if (currentStress != newStress || currentCapacity != newMaxStress) {
currentStress = newStress;
currentCapacity = newMaxStress;
sync();
}
}
public float calculateCapacity() {
float presentCapacity = 0;
containsFlywheel = false;
for (Iterator<KineticTileEntity> iterator = sources.keySet()
.iterator(); iterator.hasNext();) {
KineticTileEntity te = iterator.next();
if (te.getLevel()
.getBlockEntity(te.getBlockPos()) != te) {
iterator.remove();
continue;
}
containsFlywheel |= te instanceof FlywheelTileEntity;
presentCapacity += getActualCapacityOf(te);
}
float newMaxStress = presentCapacity + unloadedCapacity;
return newMaxStress;
}
public float calculateStress() {
float presentStress = 0;
for (Iterator<KineticTileEntity> iterator = members.keySet()
.iterator(); iterator.hasNext();) {
KineticTileEntity te = iterator.next();
if (te.getLevel()
.getBlockEntity(te.getBlockPos()) != te) {
iterator.remove();
continue;
}
presentStress += getActualStressOf(te);
}
float newStress = presentStress + unloadedStress;
return newStress;
}
public float getActualCapacityOf(KineticTileEntity te) {
return sources.get(te) * getStressMultiplierForSpeed(te.getGeneratedSpeed());
}
public float getActualStressOf(KineticTileEntity te) {
return members.get(te) * getStressMultiplierForSpeed(te.getTheoreticalSpeed());
}
private static float getStressMultiplierForSpeed(float speed) {
return Math.abs(speed);
}
public int getSize() {
return unloadedMembers + members.size();
}
}

View file

@ -1,42 +0,0 @@
package com.simibubi.create.content.contraptions;
import java.util.HashMap;
import java.util.Map;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.foundation.utility.WorldHelper;
import net.minecraft.world.level.LevelAccessor;
public class TorquePropagator {
static Map<LevelAccessor, Map<Long, KineticNetwork>> networks = new HashMap<>();
public void onLoadWorld(LevelAccessor world) {
networks.put(world, new HashMap<>());
Create.LOGGER.debug("Prepared Kinetic Network Space for " + WorldHelper.getDimensionID(world));
}
public void onUnloadWorld(LevelAccessor world) {
networks.remove(world);
Create.LOGGER.debug("Removed Kinetic Network Space for " + WorldHelper.getDimensionID(world));
}
public KineticNetwork getOrCreateNetworkFor(KineticTileEntity te) {
Long id = te.network;
KineticNetwork network;
Map<Long, KineticNetwork> map = networks.get(te.getLevel());
if (id == null)
return null;
if (!map.containsKey(id)) {
network = new KineticNetwork();
network.id = te.network;
map.put(id, network);
}
network = map.get(id);
return network;
}
}

View file

@ -2,16 +2,8 @@ package com.simibubi.create.content.contraptions.base;
import java.util.List; import java.util.List;
import com.simibubi.create.content.contraptions.KineticNetwork;
import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel;
import com.simibubi.create.content.contraptions.goggles.IHaveGoggleInformation;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@ -23,28 +15,6 @@ public abstract class GeneratingKineticTileEntity extends KineticTileEntity {
super(typeIn, pos, state); super(typeIn, pos, state);
} }
protected void notifyStressCapacityChange(float capacity) {
getOrCreateNetwork().updateCapacityFor(this, capacity);
}
@Override
public void removeSource() {
if (hasSource() && isSource())
reActivateSource = true;
super.removeSource();
}
@Override
public void setSource(BlockPos source) {
super.setSource(source);
BlockEntity tileEntity = level.getBlockEntity(source);
if (!(tileEntity instanceof KineticTileEntity))
return;
KineticTileEntity sourceTe = (KineticTileEntity) tileEntity;
if (reActivateSource && Math.abs(sourceTe.getSpeed()) >= Math.abs(getGeneratedSpeed()))
reActivateSource = false;
}
@Override @Override
public void tick() { public void tick() {
super.tick(); super.tick();
@ -58,107 +28,59 @@ public abstract class GeneratingKineticTileEntity extends KineticTileEntity {
public boolean addToGoggleTooltip(List<Component> tooltip, boolean isPlayerSneaking) { public boolean addToGoggleTooltip(List<Component> tooltip, boolean isPlayerSneaking) {
boolean added = super.addToGoggleTooltip(tooltip, isPlayerSneaking); boolean added = super.addToGoggleTooltip(tooltip, isPlayerSneaking);
float stressBase = calculateAddedStressCapacity(); // float stressBase = calculateAddedStressCapacity();
if (stressBase != 0 && IRotate.StressImpact.isEnabled()) { // if (stressBase != 0 && IRotate.StressImpact.isEnabled()) {
tooltip.add(componentSpacing.plainCopy().append(Lang.translate("gui.goggles.generator_stats"))); // tooltip.add(componentSpacing.plainCopy().append(Lang.translate("gui.goggles.generator_stats")));
tooltip.add(componentSpacing.plainCopy().append(Lang.translate("tooltip.capacityProvided").withStyle(ChatFormatting.GRAY))); // tooltip.add(componentSpacing.plainCopy().append(Lang.translate("tooltip.capacityProvided").withStyle(ChatFormatting.GRAY)));
//
float speed = getTheoreticalSpeed(); // float speed = getTheoreticalSpeed();
if (speed != getGeneratedSpeed() && speed != 0) // if (speed != getGeneratedSpeed() && speed != 0)
stressBase *= getGeneratedSpeed() / speed; // stressBase *= getGeneratedSpeed() / speed;
//
speed = Math.abs(speed); // speed = Math.abs(speed);
float stressTotal = stressBase * speed; // float stressTotal = stressBase * speed;
//
tooltip.add( // tooltip.add(
componentSpacing.plainCopy() // componentSpacing.plainCopy()
.append(new TextComponent(" " + IHaveGoggleInformation.format(stressTotal)) // .append(new TextComponent(" " + IHaveGoggleInformation.format(stressTotal))
.append(Lang.translate("generic.unit.stress")) // .append(Lang.translate("generic.unit.stress"))
.withStyle(ChatFormatting.AQUA)) // .withStyle(ChatFormatting.AQUA))
.append(" ") // .append(" ")
.append(Lang.translate("gui.goggles.at_current_speed").withStyle(ChatFormatting.DARK_GRAY))); // .append(Lang.translate("gui.goggles.at_current_speed").withStyle(ChatFormatting.DARK_GRAY)));
//
added = true; // added = true;
} // }
return added; return added;
} }
public void updateGeneratedRotation() { public void updateGeneratedRotation() {
float speed = getGeneratedSpeed(); // float speed = getGeneratedSpeed();
float prevSpeed = this.speed; // float prevSpeed = this.speed;
//
if (level.isClientSide) // if (level.isClientSide)
return; // return;
//
if (prevSpeed != speed) { // if (prevSpeed != speed) {
if (!hasSource()) { // if (!hasSource()) {
SpeedLevel levelBefore = SpeedLevel.of(this.speed); // SpeedLevel levelBefore = SpeedLevel.of(this.speed);
SpeedLevel levelafter = SpeedLevel.of(speed); // SpeedLevel levelafter = SpeedLevel.of(speed);
if (levelBefore != levelafter) // if (levelBefore != levelafter)
effects.queueRotationIndicators(); // effects.queueRotationIndicators();
} // }
//
applyNewSpeed(prevSpeed, speed); // applyNewSpeed(prevSpeed, speed);
} // }
//
if (hasNetwork() && speed != 0) { // if (hasNetwork() && speed != 0) {
KineticNetwork network = getOrCreateNetwork(); // KineticNetwork network = getOrCreateNetwork();
notifyStressCapacityChange(calculateAddedStressCapacity()); // notifyStressCapacityChange(calculateAddedStressCapacity());
getOrCreateNetwork().updateStressFor(this, calculateStressApplied()); // getOrCreateNetwork().updateStressFor(this, calculateStressApplied());
network.updateStress(); // network.updateStress();
} // }
//
onSpeedChanged(prevSpeed); // onSpeedChanged(prevSpeed);
sendData(); // sendData();
}
public void applyNewSpeed(float prevSpeed, float speed) {
// Speed changed to 0
if (speed == 0) {
if (hasSource()) {
notifyStressCapacityChange(0);
getOrCreateNetwork().updateStressFor(this, calculateStressApplied());
return;
}
detachKinetics();
setSpeed(0);
setNetwork(null);
return;
}
// Now turning - create a new Network
if (prevSpeed == 0) {
setSpeed(speed);
setNetwork(createNetworkId());
attachKinetics();
return;
}
// Change speed when overpowered by other generator
if (hasSource()) {
// Staying below Overpowered speed
if (Math.abs(prevSpeed) >= Math.abs(speed)) {
if (Math.signum(prevSpeed) != Math.signum(speed))
level.destroyBlock(worldPosition, true);
return;
}
// Faster than attached network -> become the new source
detachKinetics();
setSpeed(speed);
source = null;
setNetwork(createNetworkId());
attachKinetics();
return;
}
// Reapply source
detachKinetics();
setSpeed(speed);
attachKinetics();
} }
public Long createNetworkId() { public Long createNetworkId() {

View file

@ -10,8 +10,6 @@ import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.FlywheelRendered; import com.jozufozu.flywheel.api.FlywheelRendered;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.KineticNetwork;
import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel; import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel;
import com.simibubi.create.content.contraptions.base.IRotate.StressImpact; import com.simibubi.create.content.contraptions.base.IRotate.StressImpact;
import com.simibubi.create.content.contraptions.goggles.IHaveGoggleInformation; import com.simibubi.create.content.contraptions.goggles.IHaveGoggleInformation;
@ -19,6 +17,7 @@ import com.simibubi.create.content.contraptions.goggles.IHaveHoveringInformation
import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel; import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel;
import com.simibubi.create.content.contraptions.relays.gearbox.GearboxBlock; import com.simibubi.create.content.contraptions.relays.gearbox.GearboxBlock;
import com.simibubi.create.content.contraptions.solver.AllConnections; import com.simibubi.create.content.contraptions.solver.AllConnections;
import com.simibubi.create.content.contraptions.solver.IKineticController;
import com.simibubi.create.content.contraptions.solver.KineticConnections; import com.simibubi.create.content.contraptions.solver.KineticConnections;
import com.simibubi.create.content.contraptions.solver.KineticSolver; import com.simibubi.create.content.contraptions.solver.KineticSolver;
import com.simibubi.create.foundation.block.BlockStressValues; import com.simibubi.create.foundation.block.BlockStressValues;
@ -51,7 +50,7 @@ import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.DistExecutor;
public class KineticTileEntity extends SmartTileEntity public class KineticTileEntity extends SmartTileEntity
implements IHaveGoggleInformation, IHaveHoveringInformation, FlywheelRendered { implements IHaveGoggleInformation, IHaveHoveringInformation, FlywheelRendered, IKineticController {
public @Nullable Long network = null; public @Nullable Long network = null;
public @Nullable BlockPos source = null; public @Nullable BlockPos source = null;
@ -69,35 +68,25 @@ public class KineticTileEntity extends SmartTileEntity
protected float lastStressApplied; protected float lastStressApplied;
protected float lastCapacityProvided; protected float lastCapacityProvided;
private KineticConnections connections = AllConnections.EMPTY; private final KineticConnections connections;
public KineticTileEntity(BlockEntityType<?> typeIn, BlockPos pos, BlockState state) { public KineticTileEntity(BlockEntityType<?> typeIn, BlockPos pos, BlockState state) {
super(typeIn, pos, state); super(typeIn, pos, state);
effects = new KineticEffectHandler(this); effects = new KineticEffectHandler(this);
if (state.getBlock() instanceof IRotate rotate) { if (state.getBlock() instanceof IRotate rotate) {
connections = rotate.getInitialConnections(state); connections = rotate.getInitialConnections(state);
} else {
connections = AllConnections.EMPTY;
} }
} }
public KineticConnections getConnections() { @Override public KineticConnections getConnections() { return connections; }
return connections;
}
public float getGeneratedSpeed() { @Override public float getStressImpact() { return getDefaultStressImpact(); }
return 0;
}
public float getStressImpact() { @Override public float getStressCapacity() { return getDefaultStressCapacity(); }
return getDefaultStressImpact();
}
public float getStressCapacity() {
return getDefaultStressCapacity();
}
public boolean isStressConstant() {
return false;
}
public float getDefaultStressImpact() { public float getDefaultStressImpact() {
return (float) BlockStressValues.getImpact(getStressConfigKey()); return (float) BlockStressValues.getImpact(getStressConfigKey());
@ -129,9 +118,9 @@ public class KineticTileEntity extends SmartTileEntity
super.tick(); super.tick();
effects.tick(); effects.tick();
if (!level.isClientSide) { // if (!level.isClientSide) {
KineticSolver.getSolver(level).updateNode(this); // KineticSolver.getSolver(level).updateNode(this);
} // }
if (level.isClientSide) { if (level.isClientSide) {
cachedBoundingBox = null; // cache the bounding box for every frame between ticks cachedBoundingBox = null; // cache the bounding box for every frame between ticks
@ -161,9 +150,7 @@ public class KineticTileEntity extends SmartTileEntity
super.onChunkUnloaded(); super.onChunkUnloaded();
if (!level.isClientSide) { if (!level.isClientSide) {
preKineticsUnloaded(); preKineticsUnloaded();
KineticSolver solver = KineticSolver.getSolver(level); KineticSolver.getSolver(level).unloadNode(this);
solver.updateNode(this);
solver.unloadNode(this);
} }
} }
@ -312,7 +299,8 @@ public class KineticTileEntity extends SmartTileEntity
} }
public boolean isSource() { public boolean isSource() {
return getGeneratedSpeed() != 0; //return getGeneratedSpeed() != 0;
return false;
} }
public float getSpeed() { public float getSpeed() {
@ -378,10 +366,6 @@ public class KineticTileEntity extends SmartTileEntity
// network.add(this); // network.add(this);
} }
public KineticNetwork getOrCreateNetwork() {
return Create.TORQUE_PROPAGATOR.getOrCreateNetworkFor(this);
}
public boolean hasNetwork() { public boolean hasNetwork() {
return network != null; return network != null;
} }

View file

@ -7,15 +7,20 @@ import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.components.motor.CreativeMotorTileEntity; import com.simibubi.create.content.contraptions.components.motor.CreativeMotorTileEntity;
import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock; import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock;
import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel; import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel;
import com.simibubi.create.content.contraptions.solver.KineticControllerSerial;
import com.simibubi.create.content.contraptions.solver.KineticNode;
import com.simibubi.create.content.contraptions.solver.KineticSolver;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform; import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform;
import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueBehaviour;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -57,12 +62,11 @@ public class SpeedControllerTileEntity extends KineticTileEntity {
return targetSpeed.getValue(); return targetSpeed.getValue();
} }
private void updateTargetRotation() { @Override
if (hasNetwork()) public void onUpdate(Level level, KineticSolver solver, KineticNode node) {
getOrCreateNetwork().remove(this); solver.getNode(node.getPos().above())
RotationPropagator.handleRemoved(level, worldPosition, this); .filter(n -> node.getActiveStressOnlyConnections().anyMatch(m -> m == n))
removeSource(); .ifPresent(n -> n.setController(node, KineticControllerSerial.SPEED_CONTROLLER_COG));
attachKinetics();
} }
public static float getConveyedSpeed(KineticTileEntity cogWheel, KineticTileEntity speedControllerIn, public static float getConveyedSpeed(KineticTileEntity cogWheel, KineticTileEntity speedControllerIn,

View file

@ -2,11 +2,7 @@ package com.simibubi.create.content.contraptions.relays.elementary;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerTileEntity;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
@ -27,13 +23,4 @@ public class SimpleKineticTileEntity extends KineticTileEntity {
return false; return false;
} }
@Override
public float getGeneratedSpeed() {
BlockPos belowPos = getBlockPos().below();
if (isStressOnlyConnected(belowPos)
&& level.getBlockEntity(belowPos) instanceof SpeedControllerTileEntity controller
&& controller.getSpeed() != 0)
return controller.getTargetSpeed();
return 0;
}
} }

View file

@ -57,7 +57,7 @@ public class StressGaugeTileEntity extends GaugeTileEntity {
return; return;
} }
updateFromNetwork(capacity, stress, getOrCreateNetwork().getSize()); updateFromNetwork(capacity, stress, 0);
} }
@Override @Override

View file

@ -0,0 +1,30 @@
package com.simibubi.create.content.contraptions.solver;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level;
public interface IKineticController {
default void onUpdate(Level level, KineticSolver solver, KineticNode node) { }
default KineticConnections getConnections() {
return AllConnections.EMPTY;
}
default float getGeneratedSpeed() {
return 0;
}
default float getStressImpact() {
return 0;
}
default float getStressCapacity() {
return 0;
}
default boolean isStressConstant() {
return false;
}
default CompoundTag save(CompoundTag tag) { return tag; }
}

View file

@ -0,0 +1,73 @@
package com.simibubi.create.content.contraptions.solver;
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerTileEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level;
import java.util.Optional;
public enum KineticControllerSerial {
SPEED_CONTROLLER_COG {
class Controller implements IKineticController {
private final KineticConnections connections;
private float targetSpeed;
private float generatedSpeed;
public Controller(KineticConnections connections, float targetSpeed) {
this.connections = connections;
this.targetSpeed = targetSpeed;
}
@Override
public KineticConnections getConnections() {
return connections;
}
@Override
public void onUpdate(Level level, KineticSolver solver, KineticNode node) {
BlockPos below = node.getPos().below();
if (level.getBlockEntity(below) instanceof SpeedControllerTileEntity se) {
targetSpeed = se.getTargetSpeed();
}
Optional<KineticNode> seNode = solver.getNode(below);
if (seNode.isPresent() && seNode.get().getTheoreticalSpeed() != 0) {
generatedSpeed = targetSpeed;
} else {
generatedSpeed = 0;
}
}
@Override
public float getGeneratedSpeed() {
return generatedSpeed;
}
@Override
public CompoundTag save(CompoundTag tag) {
tag.put("Connections", connections.save(new CompoundTag()));
tag.putFloat("Target", targetSpeed);
return tag;
}
}
@Override
public IKineticController init(KineticNode prev) {
return new Controller(prev.getConnections(), 0);
}
@Override
public IKineticController load(CompoundTag tag) {
KineticConnections connections = KineticConnections.load(tag.getCompound("Connections"));
float target = tag.getFloat("Target");
return new Controller(connections, target);
}
};
public abstract IKineticController init(KineticNode prev);
public abstract IKineticController load(CompoundTag tag);
}

View file

@ -10,6 +10,7 @@ import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class KineticNetwork { public class KineticNetwork {
@ -28,18 +29,18 @@ public class KineticNetwork {
private final Set<KineticNode> potentialNewBranches = new HashSet<>(); private final Set<KineticNode> potentialNewBranches = new HashSet<>();
private final ResetableLazy<Float> totalStressImpact = ResetableLazy.of(() -> private final ResetableLazy<Float> totalStressImpact = ResetableLazy.of(() ->
(float) members.stream().mapToDouble(n -> n.getTotalStressImpact(rootTheoreticalSpeed)).sum()); (float) members.stream().mapToDouble(KineticNode::getTotalStressImpact).sum());
private final ResetableLazy<Float> totalStressCapacity = ResetableLazy.of(() -> private final ResetableLazy<Float> totalStressCapacity = ResetableLazy.of(() ->
(float) members.stream().mapToDouble(KineticNode::getStressCapacity).sum()); (float) members.stream().mapToDouble(KineticNode::getStressCapacity).sum());
private boolean ticked; private boolean ticked;
private final Set<KineticNode> stressConnectors = new HashSet<>(); private final Set<KineticNode> stressConnectors = new HashSet<>();
public KineticNetwork(KineticNode root) { protected KineticNetwork(KineticNode root) {
addMember(root); addMember(root);
} }
public void addMember(KineticNode node) { protected void addMember(KineticNode node) {
members.add(node); members.add(node);
potentialNewBranches.add(node); potentialNewBranches.add(node);
if (node.getConnections().hasStressOnlyConnections()) stressConnectors.add(node); if (node.getConnections().hasStressOnlyConnections()) stressConnectors.add(node);
@ -52,11 +53,11 @@ public class KineticNetwork {
if (node.hasStressCapacity()) onMemberStressCapacityUpdated(); if (node.hasStressCapacity()) onMemberStressCapacityUpdated();
} }
public void onMemberLoaded(KineticNode node) { protected void onMemberLoaded(KineticNode node) {
potentialNewBranches.add(node); potentialNewBranches.add(node);
} }
public void onMemberGeneratedSpeedUpdated(KineticNode node) { protected void onMemberGeneratedSpeedUpdated(KineticNode node) {
if (node.isGenerator()) { if (node.isGenerator()) {
generators.add(node); generators.add(node);
} else { } else {
@ -66,15 +67,15 @@ public class KineticNetwork {
onMemberStressCapacityUpdated(); onMemberStressCapacityUpdated();
} }
public void onMemberStressImpactUpdated() { protected void onMemberStressImpactUpdated() {
totalStressImpact.reset(); totalStressImpact.reset();
} }
public void onMemberStressCapacityUpdated() { protected void onMemberStressCapacityUpdated() {
totalStressCapacity.reset(); totalStressCapacity.reset();
} }
public void removeMember(KineticNode node) { protected void removeMember(KineticNode node) {
if (node.isGenerator() && generators.contains(node)) { if (node.isGenerator() && generators.contains(node)) {
generators.remove(node); generators.remove(node);
rootSpeedDirty = true; rootSpeedDirty = true;
@ -87,25 +88,25 @@ public class KineticNetwork {
conflictingCycles.removeIf(p -> p.getFirst() == node || p.getSecond() == node); conflictingCycles.removeIf(p -> p.getFirst() == node || p.getSecond() == node);
} }
public void markConflictingCycle(KineticNode from, KineticNode to) { protected void markConflictingCycle(KineticNode from, KineticNode to) {
if (!members.contains(from) || !members.contains(to)) throw new IllegalArgumentException(); if (!members.contains(from) || !members.contains(to)) throw new IllegalArgumentException();
conflictingCycles.add(Pair.of(from, to)); conflictingCycles.add(Pair.of(from, to));
} }
public boolean isStopped() { return generators.isEmpty() || overstressed; } protected boolean isStopped() { return generators.isEmpty() || overstressed; }
/** /**
* Recalculates the speed at the root node of this network. * Recalculates the speed at the root node of this network.
* @return CONTRADICTION if the network has cycles with conflicting speed ratios or generators turning against * @return CONTRADICTION if the network has cycles with conflicting speed ratios or generators turning against
* each other, and OK otherwise * each other, and OK otherwise
*/ */
public SolveResult tryRecalculateSpeed() { protected SolveResult tryRecalculateSpeed() {
SolveResult result = tryRecalculateTheoreticalSpeed(); SolveResult result = tryRecalculateTheoreticalSpeed();
if (isStopped()) return SolveResult.OK; if (isStopped()) return SolveResult.OK;
return result; return result;
} }
private SolveResult tryRecalculateTheoreticalSpeed() { protected SolveResult tryRecalculateTheoreticalSpeed() {
SolveResult result = conflictingCycles.isEmpty() ? SolveResult.OK : SolveResult.CONTRADICTION; SolveResult result = conflictingCycles.isEmpty() ? SolveResult.OK : SolveResult.CONTRADICTION;
if (!rootSpeedDirty) return result; if (!rootSpeedDirty) return result;
@ -152,7 +153,11 @@ public class KineticNetwork {
return totalStressCapacity.get(); return totalStressCapacity.get();
} }
private float getRootSpeed() { public float getRootTheoreticalSpeed() {
return rootTheoreticalSpeed;
}
public float getRootSpeed() {
return isStopped() ? 0 : rootTheoreticalSpeed; return isStopped() ? 0 : rootTheoreticalSpeed;
} }
@ -161,45 +166,52 @@ public class KineticNetwork {
onMemberStressImpactUpdated(); onMemberStressImpactUpdated();
} }
public void untick() { protected void untick() {
ticked = false; ticked = false;
} }
public void tick(List<KineticNetwork> newNetworks) { protected void tick(List<KineticNetwork> newNetworks) {
if (ticked) return; if (ticked) return;
Set<KineticNetwork> stressConnected = stressConnectors.stream() Set<KineticNetwork> stressConnected = stressConnectors.stream()
.flatMap(KineticNode::getActiveStressOnlyConnections) .flatMap(KineticNode::getActiveStressOnlyConnections)
.map(KineticNode::getNetwork)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
stressConnected.add(this); stressConnected.add(this);
float stressImpact = 0; float stressImpact = 0;
float stressCapacity = 0; float stressCapacity = 0;
Set<KineticNode> popQueue = new HashSet<>();
Consumer<KineticNode> pop = n -> { n.popBlock(); newNetworks.add(n.getNetwork()); };
for (KineticNetwork cur : stressConnected) { for (KineticNetwork cur : stressConnected) {
cur.ticked = true; cur.ticked = true;
cur.updateMemberSpeeds(newNetworks); cur.updateMemberSpeeds(popQueue::add);
stressImpact += cur.getTotalStressImpact(); stressImpact += cur.getTotalStressImpact();
stressCapacity += cur.getTotalStressCapacity(); stressCapacity += cur.getTotalStressCapacity();
} }
boolean nowOverstressed = stressImpact > stressCapacity; boolean nowOverstressed = stressImpact > stressCapacity;
if (!nowOverstressed) {
// we should only pop speeding nodes if the network isn't actually overstressed now
popQueue.forEach(pop);
}
for (KineticNetwork cur : stressConnected) { for (KineticNetwork cur : stressConnected) {
if (cur.generators.isEmpty()) { if (nowOverstressed) {
cur.overstressed = false;
} else if (nowOverstressed) {
if (!cur.overstressed) { if (!cur.overstressed) {
// just became overstressed
cur.overstressed = true; cur.overstressed = true;
cur.onRootSpeedChanged(); cur.onRootSpeedChanged();
cur.members.forEach(KineticNode::stop); cur.members.forEach(KineticNode::stop);
} }
} else { } else {
if (cur.overstressed) { if (cur.overstressed) {
// just became non-overstressed
cur.overstressed = false; cur.overstressed = false;
cur.onRootSpeedChanged(); cur.onRootSpeedChanged();
cur.bulldozeContradictingMembers(newNetworks); cur.bulldozeContradictingMembers(newNetworks);
cur.updateMemberSpeeds(newNetworks); cur.updateMemberSpeeds(pop);
} }
} }
@ -208,10 +220,10 @@ public class KineticNetwork {
} }
/** /**
* Update the speed of every member, starting from the main generator and popping off speeding nodes along the way * Update the speed of every member, starting from the main generator and checking for speeding nodes along the way
* @param newNetworks a List that any new networks created during this call will be added to * @param onSpeeding a function to call whenever a speeding node is found and should be popped
*/ */
private void updateMemberSpeeds(List<KineticNetwork> newNetworks) { private void updateMemberSpeeds(Consumer<KineticNode> onSpeeding) {
SolveResult recalculateSpeedResult = tryRecalculateSpeed(); SolveResult recalculateSpeedResult = tryRecalculateSpeed();
// generators should not be turning against each other or have conflicting cycles by now // generators should not be turning against each other or have conflicting cycles by now
assert(recalculateSpeedResult.isOk()); assert(recalculateSpeedResult.isOk());
@ -225,17 +237,17 @@ public class KineticNetwork {
if (rootSpeedChanged) { if (rootSpeedChanged) {
// root speed changed, update all nodes starting from the main generator // root speed changed, update all nodes starting from the main generator
rootSpeedChanged = false; rootSpeedChanged = false;
bfs(mainGenerator, newNetworks, false); bfs(mainGenerator, onSpeeding, false);
} else if (!potentialNewBranches.isEmpty()) { } else if (!potentialNewBranches.isEmpty()) {
// new nodes added, update only the new network branches // new nodes added, update only the new network branches
potentialNewBranches.stream() potentialNewBranches.stream()
.filter(n -> !potentialNewBranches.contains(n.getSource())) .filter(n -> !potentialNewBranches.contains(n.getSource()))
.forEach(n -> bfs(n, newNetworks, true)); .forEach(n -> bfs(n, onSpeeding, true));
} }
potentialNewBranches.clear(); potentialNewBranches.clear();
} }
private void bfs(KineticNode root, List<KineticNetwork> newNetworks, boolean followSource) { private void bfs(KineticNode root, Consumer<KineticNode> onSpeeding, boolean followSource) {
// update node speeds in a breadth-first order, checking for speeding nodes along the way // update node speeds in a breadth-first order, checking for speeding nodes along the way
Set<KineticNode> visited = new HashSet<>(); Set<KineticNode> visited = new HashSet<>();
List<KineticNode> frontier = new LinkedList<>(); List<KineticNode> frontier = new LinkedList<>();
@ -246,15 +258,14 @@ public class KineticNetwork {
if (!members.contains(cur) || visited.contains(cur)) continue; if (!members.contains(cur) || visited.contains(cur)) continue;
visited.add(cur); visited.add(cur);
if (cur.tryUpdateSpeed(getRootSpeed()).isOk()) { if (cur.tryUpdateSpeed().isOk()) {
cur.getActiveConnections() cur.getActiveConnections()
.map(Pair::getFirst) .map(Pair::getFirst)
.filter(n -> !followSource || n.getSource() == cur) .filter(n -> !followSource || n.getSource() == cur)
.forEach(frontier::add); .forEach(frontier::add);
} else { } else {
// stop searching on this branch once a speeding node is found // stop searching on this branch once a speeding node is found
cur.popBlock(); onSpeeding.accept(cur);
newNetworks.add(cur.getNetwork());
} }
} }
} }

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.contraptions.solver;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -11,9 +12,11 @@ import net.minecraft.util.Mth;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -22,6 +25,10 @@ public class KineticNode {
private final KineticSolver solver; private final KineticSolver solver;
private @Nullable KineticTileEntity entity; private @Nullable KineticTileEntity entity;
private @Nullable IKineticController controller;
private @Nullable KineticControllerSerial controllerType;
private @Nullable Set<BlockPos> controlling;
private @Nullable KineticNode source; private @Nullable KineticNode source;
private KineticNetwork network; private KineticNetwork network;
private float speedRatio = 1; private float speedRatio = 1;
@ -35,18 +42,22 @@ public class KineticNode {
private float stressImpact; private float stressImpact;
private final boolean constantStress; private final boolean constantStress;
protected KineticNode regen() {
return new KineticNode(solver, pos, entity, getController().get(), controllerType);
}
public KineticNode(KineticSolver solver, KineticTileEntity entity) { public KineticNode(KineticSolver solver, KineticTileEntity entity) {
this.solver = solver; this(solver, entity.getBlockPos(), entity, entity, null);
this.controller = null;
}
private KineticNode(KineticSolver solver, BlockPos pos, @Nullable KineticTileEntity entity,
IKineticController controller, @Nullable KineticControllerSerial controllerType) {
this(solver, pos, controller.getConnections(), controller.getGeneratedSpeed(), controller.getStressCapacity(),
controller.getStressImpact(), controller.isStressConstant());
this.entity = entity; this.entity = entity;
this.controller = controller;
this.pos = entity.getBlockPos(); this.controllerType = controllerType;
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, private KineticNode(KineticSolver solver, BlockPos pos, KineticConnections connections, float generatedSpeed,
@ -65,17 +76,29 @@ public class KineticNode {
public CompoundTag save(CompoundTag tag) { public CompoundTag save(CompoundTag tag) {
tag.put("Pos", NbtUtils.writeBlockPos(pos)); tag.put("Pos", NbtUtils.writeBlockPos(pos));
if (controller != null && controllerType != null) {
NBTHelper.writeEnum(tag, "ControllerType", controllerType);
tag.put("Controller", controller.save(new CompoundTag()));
return tag;
}
tag.put("Connections", connections.save(new CompoundTag())); tag.put("Connections", connections.save(new CompoundTag()));
tag.putFloat("Generated", generatedSpeed); tag.putFloat("Generated", generatedSpeed);
tag.putFloat("Capacity", stressCapacity); tag.putFloat("Capacity", stressCapacity);
tag.putFloat("Impact", stressImpact); tag.putFloat("Impact", stressImpact);
if (constantStress) if (constantStress) {
tag.putBoolean("ConstantStress", true); tag.putBoolean("ConstantStress", true);
}
return tag; return tag;
} }
public static KineticNode load(KineticSolver solver, CompoundTag tag) { public static KineticNode load(KineticSolver solver, CompoundTag tag) {
BlockPos pos = NbtUtils.readBlockPos(tag.getCompound("Pos")); BlockPos pos = NbtUtils.readBlockPos(tag.getCompound("Pos"));
if (tag.contains("Controller") && tag.contains("ControllerType")) {
KineticControllerSerial type = NBTHelper.readEnum(tag, "ControllerType", KineticControllerSerial.class);
return new KineticNode(solver, pos, null, type.load(tag.getCompound("Controller")), type);
}
KineticConnections connections = KineticConnections.load(tag.getCompound("Connections")); KineticConnections connections = KineticConnections.load(tag.getCompound("Connections"));
float generatedSpeed = tag.getFloat("Generated"); float generatedSpeed = tag.getFloat("Generated");
float stressCapacity = tag.getFloat("Capacity"); float stressCapacity = tag.getFloat("Capacity");
@ -88,16 +111,38 @@ public class KineticNode {
return entity != null; return entity != null;
} }
public void onLoaded(KineticTileEntity entity) { protected void onLoaded(KineticTileEntity entity) {
this.entity = entity; this.entity = entity;
network.onMemberLoaded(this); network.onMemberLoaded(this);
if (speedCur != 0) entity.setSpeed(speedCur); if (speedCur != 0)
entity.setSpeed(speedCur);
} }
public void onUnloaded() { protected void onUnloaded() {
this.entity = null; this.entity = null;
} }
public Optional<IKineticController> getController() {
if (controller != null) return Optional.of(controller);
if (entity != null) return Optional.of(entity);
return Optional.empty();
}
public boolean setController(KineticNode source, KineticControllerSerial controller) {
if (this.controller != null) return false;
this.controller = controller.init(this);
this.controllerType = controller;
if (source.controlling == null)
source.controlling = new HashSet<>();
source.controlling.add(pos);
return true;
}
protected void removeController() {
controller = null;
controllerType = null;
}
public KineticConnections getConnections() { public KineticConnections getConnections() {
return connections; return connections;
} }
@ -127,12 +172,15 @@ public class KineticNode {
return getActiveConnections().collect(Collectors.toList()); return getActiveConnections().collect(Collectors.toList());
} }
public Stream<KineticNetwork> getActiveStressOnlyConnections() { public Stream<KineticNode> getActiveStressOnlyConnections() {
return connections.getDirections().stream() return connections.getDirections().stream()
.map(d -> solver.getNode(pos.offset(d)) .map(d -> solver.getNode(pos.offset(d))
.filter(n -> connections.checkStressOnlyConnection(n.connections, d))) .filter(n -> connections.checkStressOnlyConnection(n.connections, d)))
.flatMap(Optional::stream) .flatMap(Optional::stream);
.map(KineticNode::getNetwork); }
public float getGeneratedSpeed() {
return generatedSpeed;
} }
public float getGeneratedSpeedAtRoot() { public float getGeneratedSpeedAtRoot() {
@ -143,36 +191,42 @@ public class KineticNode {
return generatedSpeed != 0; return generatedSpeed != 0;
} }
public boolean onUpdated() { protected enum UpdateResult {
if (entity == null) return false; UNCHANGED, CHANGED, NEEDS_REGEN, NEEDS_POP
}
protected UpdateResult onUpdated() {
return getController().map(ctl -> {
if (!getConnections().equals(ctl.getConnections()) || constantStress != ctl.isStressConstant())
return UpdateResult.NEEDS_REGEN;
boolean changed = false; UpdateResult result = UpdateResult.UNCHANGED;
float generatedSpeedNew = entity.getGeneratedSpeed(); float generatedSpeedNew = ctl.getGeneratedSpeed();
if (this.generatedSpeed != generatedSpeedNew) { if (this.generatedSpeed != generatedSpeedNew) {
this.generatedSpeed = generatedSpeedNew; this.generatedSpeed = generatedSpeedNew;
changed = true;
network.onMemberGeneratedSpeedUpdated(this); network.onMemberGeneratedSpeedUpdated(this);
if (network.tryRecalculateSpeed().isContradiction()) { if (network.tryRecalculateSpeed().isContradiction()) {
popBlock(); return UpdateResult.NEEDS_POP;
} }
result = UpdateResult.CHANGED;
} }
float stressImpactNew = entity.getStressImpact(); float stressImpactNew = ctl.getStressImpact();
if (this.stressImpact != stressImpactNew) { if (this.stressImpact != stressImpactNew) {
this.stressImpact = stressImpactNew; this.stressImpact = stressImpactNew;
changed = true;
network.onMemberStressImpactUpdated(); network.onMemberStressImpactUpdated();
result = UpdateResult.CHANGED;
} }
float stressCapacityNew = entity.getStressCapacity(); float stressCapacityNew = ctl.getStressCapacity();
if (this.stressCapacity != stressCapacityNew) { if (this.stressCapacity != stressCapacityNew) {
this.stressCapacity = stressCapacityNew; this.stressCapacity = stressCapacityNew;
changed = true;
network.onMemberStressCapacityUpdated(); network.onMemberStressCapacityUpdated();
result = UpdateResult.CHANGED;
} }
return changed; return result;
}).orElse(UpdateResult.UNCHANGED);
} }
public boolean hasStressCapacity() { public boolean hasStressCapacity() {
@ -183,16 +237,20 @@ public class KineticNode {
return stressImpact != 0; return stressImpact != 0;
} }
public float getTheoreticalSpeed(float speedAtRoot) { public float getSpeed() {
return speedAtRoot * speedRatio; return network.getRootSpeed() * speedRatio;
}
public float getTheoreticalSpeed() {
return network.getRootTheoreticalSpeed() * speedRatio;
} }
public float getStressCapacity() { public float getStressCapacity() {
return constantStress ? stressCapacity : stressCapacity * Math.abs(generatedSpeed); return constantStress ? stressCapacity : stressCapacity * Math.abs(generatedSpeed);
} }
public float getTotalStressImpact(float speedAtRoot) { public float getTotalStressImpact() {
return constantStress ? stressImpact : stressImpact * Math.abs(getTheoreticalSpeed(speedAtRoot)); return constantStress ? stressImpact : stressImpact * Math.abs(getTheoreticalSpeed());
} }
private SolveResult setNetwork(KineticNetwork network) { private SolveResult setNetwork(KineticNetwork network) {
@ -202,7 +260,7 @@ public class KineticNode {
return network.tryRecalculateSpeed(); return network.tryRecalculateSpeed();
} }
public @Nullable KineticNode getSource() { protected @Nullable KineticNode getSource() {
return source; return source;
} }
@ -212,7 +270,7 @@ public class KineticNode {
return setNetwork(from.network); return setNetwork(from.network);
} }
public void onAdded() { protected void onAdded() {
getActiveConnections() getActiveConnections()
.findAny() .findAny()
.ifPresent(e -> { .ifPresent(e -> {
@ -264,12 +322,18 @@ public class KineticNode {
} }
} }
public void onRemoved() { protected void onRemoved() {
network.removeMember(this); network.removeMember(this);
getActiveConnections() getActiveConnections()
.map(Pair::getFirst) .map(Pair::getFirst)
.filter(n -> n.source == this) .filter(n -> n.source == this)
.forEach(KineticNode::rerootHere); .forEach(KineticNode::rerootHere);
if (controlling != null) {
controlling.stream()
.map(solver::getNode)
.flatMap(Optional::stream)
.forEach(KineticNode::removeController);
}
} }
private void rerootHere() { private void rerootHere() {
@ -282,11 +346,10 @@ public class KineticNode {
/** /**
* Updates the speed of this node based on its network's root speed and its own speed ratio. * Updates the speed of this node based on its network's root speed and its own speed ratio.
* @param speedAtRoot Current speed at the root of this node's network
* @return CONTRADICTION if the node's new speed exceeds the maximum value, and OK otherwise * @return CONTRADICTION if the node's new speed exceeds the maximum value, and OK otherwise
*/ */
protected SolveResult tryUpdateSpeed(float speedAtRoot) { protected SolveResult tryUpdateSpeed() {
speedNext = getTheoreticalSpeed(speedAtRoot); speedNext = getSpeed();
if (Math.abs(speedNext) > AllConfigs.SERVER.kinetics.maxRotationSpeed.get()) if (Math.abs(speedNext) > AllConfigs.SERVER.kinetics.maxRotationSpeed.get())
return SolveResult.CONTRADICTION; return SolveResult.CONTRADICTION;
return SolveResult.OK; return SolveResult.OK;
@ -296,7 +359,7 @@ public class KineticNode {
speedNext = 0; speedNext = 0;
} }
public void flushChangedSpeed() { protected void flushChangedSpeed() {
if (speedCur != speedNext) { if (speedCur != speedNext) {
speedCur = speedNext; speedCur = speedNext;
if (entity != null) { if (entity != null) {
@ -305,7 +368,7 @@ public class KineticNode {
} }
} }
public void popBlock() { protected void popBlock() {
if (entity != null) { if (entity != null) {
solver.removeAndPopNow(entity); solver.removeAndPopNow(entity);
} else { } else {

View file

@ -109,28 +109,22 @@ public class KineticSolver extends SavedData {
setDirty(); setDirty();
} }
public void updateNode(KineticTileEntity entity) { private void regenNode(KineticNode node) {
KineticNode node = nodes.get(entity.getBlockPos()); BlockPos pos = node.getPos();
if (node == null) return; nodes.remove(pos);
node.onRemoved();
if (!node.getConnections().equals(entity.getConnections())) { KineticNode newNode = node.regen();
// connections changed, so things could've been disconnected nodes.put(pos, newNode);
removeNode(entity); newNode.onAdded();
addNode(entity);
} else {
// connections are the same, so just update in case other properties changed
if (node.onUpdated()) {
setDirty(); setDirty();
} }
}
}
public void unloadNode(KineticTileEntity entity) { public void unloadNode(KineticTileEntity entity) {
KineticNode node = nodes.get(entity.getBlockPos()); KineticNode node = nodes.get(entity.getBlockPos());
if (node != null) node.onUnloaded(); if (node != null) node.onUnloaded();
} }
protected Optional<KineticNode> getNode(BlockPos pos) { public Optional<KineticNode> getNode(BlockPos pos) {
return Optional.ofNullable(nodes.get(pos)); return Optional.ofNullable(nodes.get(pos));
} }
@ -164,9 +158,25 @@ public class KineticSolver extends SavedData {
level.destroyBlock(pos, true); level.destroyBlock(pos, true);
} }
public void tick() { public void tick(Level level) {
Set<KineticNetwork> networks = nodes.values().stream().map(KineticNode::getNetwork).collect(Collectors.toSet()); Set<KineticNode> popQueue = new HashSet<>();
networks.forEach(KineticNetwork::untick); Set<KineticNode> regenQueue = new HashSet<>();
for (KineticNode node : nodes.values()) {
node.getController().ifPresent(c -> c.onUpdate(level, this, node));
switch (node.onUpdated()) {
case NEEDS_POP -> popQueue.add(node);
case NEEDS_REGEN -> regenQueue.add(node);
case CHANGED -> setDirty();
}
}
popQueue.forEach(KineticNode::popBlock);
regenQueue.forEach(this::regenNode);
Set<KineticNetwork> networks = new HashSet<>();
for (KineticNode node : nodes.values()) {
networks.add(node.getNetwork());
node.getNetwork().untick();
}
List<KineticNetwork> frontier = new LinkedList<>(); List<KineticNetwork> frontier = new LinkedList<>();

View file

@ -112,7 +112,7 @@ public class CommonEvents {
CouplingPhysics.tick(world); CouplingPhysics.tick(world);
LinkedControllerServerHandler.tick(world); LinkedControllerServerHandler.tick(world);
KineticSolver.getSolver(world).tick(); KineticSolver.getSolver(world).tick(world);
} }
@SubscribeEvent @SubscribeEvent
@ -169,14 +169,12 @@ public class CommonEvents {
public static void onLoadWorld(WorldEvent.Load event) { public static void onLoadWorld(WorldEvent.Load event) {
LevelAccessor world = event.getWorld(); LevelAccessor world = event.getWorld();
Create.REDSTONE_LINK_NETWORK_HANDLER.onLoadWorld(world); Create.REDSTONE_LINK_NETWORK_HANDLER.onLoadWorld(world);
Create.TORQUE_PROPAGATOR.onLoadWorld(world);
} }
@SubscribeEvent @SubscribeEvent
public static void onUnloadWorld(WorldEvent.Unload event) { public static void onUnloadWorld(WorldEvent.Unload event) {
LevelAccessor world = event.getWorld(); LevelAccessor world = event.getWorld();
Create.REDSTONE_LINK_NETWORK_HANDLER.onUnloadWorld(world); Create.REDSTONE_LINK_NETWORK_HANDLER.onUnloadWorld(world);
Create.TORQUE_PROPAGATOR.onUnloadWorld(world);
WorldAttached.invalidateWorld(world); WorldAttached.invalidateWorld(world);
} }