diff --git a/src/main/java/com/simibubi/create/Create.java b/src/main/java/com/simibubi/create/Create.java index 5fcb90d8e..23a54a7ec 100644 --- a/src/main/java/com/simibubi/create/Create.java +++ b/src/main/java/com/simibubi/create/Create.java @@ -9,7 +9,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour; 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.curiosities.weapons.BuiltinPotatoProjectileTypes; 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 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 Random RANDOM = new Random(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/KineticNetwork.java b/src/main/java/com/simibubi/create/content/contraptions/KineticNetwork.java deleted file mode 100644 index 4701657f2..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/KineticNetwork.java +++ /dev/null @@ -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 sources; - public Map 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 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 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(); - } - -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/TorquePropagator.java b/src/main/java/com/simibubi/create/content/contraptions/TorquePropagator.java deleted file mode 100644 index ecaac86c3..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/TorquePropagator.java +++ /dev/null @@ -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> 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 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; - } - -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/base/GeneratingKineticTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/base/GeneratingKineticTileEntity.java index ccabf1a74..d156a9cb2 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/base/GeneratingKineticTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/base/GeneratingKineticTileEntity.java @@ -2,16 +2,8 @@ package com.simibubi.create.content.contraptions.base; 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.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.state.BlockState; @@ -23,28 +15,6 @@ public abstract class GeneratingKineticTileEntity extends KineticTileEntity { 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 public void tick() { super.tick(); @@ -58,107 +28,59 @@ public abstract class GeneratingKineticTileEntity extends KineticTileEntity { public boolean addToGoggleTooltip(List tooltip, boolean isPlayerSneaking) { boolean added = super.addToGoggleTooltip(tooltip, isPlayerSneaking); - float stressBase = calculateAddedStressCapacity(); - if (stressBase != 0 && IRotate.StressImpact.isEnabled()) { - tooltip.add(componentSpacing.plainCopy().append(Lang.translate("gui.goggles.generator_stats"))); - tooltip.add(componentSpacing.plainCopy().append(Lang.translate("tooltip.capacityProvided").withStyle(ChatFormatting.GRAY))); - - float speed = getTheoreticalSpeed(); - if (speed != getGeneratedSpeed() && speed != 0) - stressBase *= getGeneratedSpeed() / speed; - - speed = Math.abs(speed); - float stressTotal = stressBase * speed; - - tooltip.add( - componentSpacing.plainCopy() - .append(new TextComponent(" " + IHaveGoggleInformation.format(stressTotal)) - .append(Lang.translate("generic.unit.stress")) - .withStyle(ChatFormatting.AQUA)) - .append(" ") - .append(Lang.translate("gui.goggles.at_current_speed").withStyle(ChatFormatting.DARK_GRAY))); - - added = true; - } +// float stressBase = calculateAddedStressCapacity(); +// if (stressBase != 0 && IRotate.StressImpact.isEnabled()) { +// tooltip.add(componentSpacing.plainCopy().append(Lang.translate("gui.goggles.generator_stats"))); +// tooltip.add(componentSpacing.plainCopy().append(Lang.translate("tooltip.capacityProvided").withStyle(ChatFormatting.GRAY))); +// +// float speed = getTheoreticalSpeed(); +// if (speed != getGeneratedSpeed() && speed != 0) +// stressBase *= getGeneratedSpeed() / speed; +// +// speed = Math.abs(speed); +// float stressTotal = stressBase * speed; +// +// tooltip.add( +// componentSpacing.plainCopy() +// .append(new TextComponent(" " + IHaveGoggleInformation.format(stressTotal)) +// .append(Lang.translate("generic.unit.stress")) +// .withStyle(ChatFormatting.AQUA)) +// .append(" ") +// .append(Lang.translate("gui.goggles.at_current_speed").withStyle(ChatFormatting.DARK_GRAY))); +// +// added = true; +// } return added; } public void updateGeneratedRotation() { - float speed = getGeneratedSpeed(); - float prevSpeed = this.speed; - - if (level.isClientSide) - return; - - if (prevSpeed != speed) { - if (!hasSource()) { - SpeedLevel levelBefore = SpeedLevel.of(this.speed); - SpeedLevel levelafter = SpeedLevel.of(speed); - if (levelBefore != levelafter) - effects.queueRotationIndicators(); - } - - applyNewSpeed(prevSpeed, speed); - } - - if (hasNetwork() && speed != 0) { - KineticNetwork network = getOrCreateNetwork(); - notifyStressCapacityChange(calculateAddedStressCapacity()); - getOrCreateNetwork().updateStressFor(this, calculateStressApplied()); - network.updateStress(); - } - - onSpeedChanged(prevSpeed); - 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(); +// float speed = getGeneratedSpeed(); +// float prevSpeed = this.speed; +// +// if (level.isClientSide) +// return; +// +// if (prevSpeed != speed) { +// if (!hasSource()) { +// SpeedLevel levelBefore = SpeedLevel.of(this.speed); +// SpeedLevel levelafter = SpeedLevel.of(speed); +// if (levelBefore != levelafter) +// effects.queueRotationIndicators(); +// } +// +// applyNewSpeed(prevSpeed, speed); +// } +// +// if (hasNetwork() && speed != 0) { +// KineticNetwork network = getOrCreateNetwork(); +// notifyStressCapacityChange(calculateAddedStressCapacity()); +// getOrCreateNetwork().updateStressFor(this, calculateStressApplied()); +// network.updateStress(); +// } +// +// onSpeedChanged(prevSpeed); +// sendData(); } public Long createNetworkId() { diff --git a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java index cad96a0c7..13e82f913 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java @@ -10,8 +10,6 @@ import javax.annotation.Nullable; import com.jozufozu.flywheel.api.FlywheelRendered; 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.StressImpact; 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.gearbox.GearboxBlock; 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.KineticSolver; import com.simibubi.create.foundation.block.BlockStressValues; @@ -51,7 +50,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.DistExecutor; public class KineticTileEntity extends SmartTileEntity - implements IHaveGoggleInformation, IHaveHoveringInformation, FlywheelRendered { + implements IHaveGoggleInformation, IHaveHoveringInformation, FlywheelRendered, IKineticController { public @Nullable Long network = null; public @Nullable BlockPos source = null; @@ -69,35 +68,25 @@ public class KineticTileEntity extends SmartTileEntity protected float lastStressApplied; protected float lastCapacityProvided; - private KineticConnections connections = AllConnections.EMPTY; + private final KineticConnections connections; public KineticTileEntity(BlockEntityType typeIn, BlockPos pos, BlockState state) { super(typeIn, pos, state); + effects = new KineticEffectHandler(this); + if (state.getBlock() instanceof IRotate rotate) { connections = rotate.getInitialConnections(state); + } else { + connections = AllConnections.EMPTY; } } - public KineticConnections getConnections() { - return connections; - } + @Override public KineticConnections getConnections() { return connections; } - public float getGeneratedSpeed() { - return 0; - } + @Override public float getStressImpact() { return getDefaultStressImpact(); } - public float getStressImpact() { - return getDefaultStressImpact(); - } - - public float getStressCapacity() { - return getDefaultStressCapacity(); - } - - public boolean isStressConstant() { - return false; - } + @Override public float getStressCapacity() { return getDefaultStressCapacity(); } public float getDefaultStressImpact() { return (float) BlockStressValues.getImpact(getStressConfigKey()); @@ -129,9 +118,9 @@ public class KineticTileEntity extends SmartTileEntity super.tick(); effects.tick(); - if (!level.isClientSide) { - KineticSolver.getSolver(level).updateNode(this); - } +// if (!level.isClientSide) { +// KineticSolver.getSolver(level).updateNode(this); +// } if (level.isClientSide) { cachedBoundingBox = null; // cache the bounding box for every frame between ticks @@ -161,9 +150,7 @@ public class KineticTileEntity extends SmartTileEntity super.onChunkUnloaded(); if (!level.isClientSide) { preKineticsUnloaded(); - KineticSolver solver = KineticSolver.getSolver(level); - solver.updateNode(this); - solver.unloadNode(this); + KineticSolver.getSolver(level).unloadNode(this); } } @@ -312,7 +299,8 @@ public class KineticTileEntity extends SmartTileEntity } public boolean isSource() { - return getGeneratedSpeed() != 0; + //return getGeneratedSpeed() != 0; + return false; } public float getSpeed() { @@ -378,10 +366,6 @@ public class KineticTileEntity extends SmartTileEntity // network.add(this); } - public KineticNetwork getOrCreateNetwork() { - return Create.TORQUE_PROPAGATOR.getOrCreateNetworkFor(this); - } - public boolean hasNetwork() { return network != null; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerTileEntity.java index 5fecca80f..3ec3b7dfc 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerTileEntity.java @@ -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.relays.elementary.CogWheelBlock; 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.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform; import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueBehaviour; import com.simibubi.create.foundation.utility.Lang; +import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.core.BlockPos; 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.state.BlockState; import net.minecraft.world.phys.Vec3; @@ -57,16 +62,15 @@ public class SpeedControllerTileEntity extends KineticTileEntity { return targetSpeed.getValue(); } - private void updateTargetRotation() { - if (hasNetwork()) - getOrCreateNetwork().remove(this); - RotationPropagator.handleRemoved(level, worldPosition, this); - removeSource(); - attachKinetics(); + @Override + public void onUpdate(Level level, KineticSolver solver, KineticNode node) { + solver.getNode(node.getPos().above()) + .filter(n -> node.getActiveStressOnlyConnections().anyMatch(m -> m == n)) + .ifPresent(n -> n.setController(node, KineticControllerSerial.SPEED_CONTROLLER_COG)); } public static float getConveyedSpeed(KineticTileEntity cogWheel, KineticTileEntity speedControllerIn, - boolean targetingController) { + boolean targetingController) { if (!(speedControllerIn instanceof SpeedControllerTileEntity)) return 0; diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/SimpleKineticTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/SimpleKineticTileEntity.java index 6f7d05e9f..7bb5e2c2d 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/SimpleKineticTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/SimpleKineticTileEntity.java @@ -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.relays.advanced.SpeedControllerTileEntity; - 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.state.BlockState; import net.minecraft.world.phys.AABB; @@ -27,13 +23,4 @@ public class SimpleKineticTileEntity extends KineticTileEntity { 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; - } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/StressGaugeTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/StressGaugeTileEntity.java index 95b78174d..7d82f003d 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/StressGaugeTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/StressGaugeTileEntity.java @@ -57,7 +57,7 @@ public class StressGaugeTileEntity extends GaugeTileEntity { return; } - updateFromNetwork(capacity, stress, getOrCreateNetwork().getSize()); + updateFromNetwork(capacity, stress, 0); } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/IKineticController.java b/src/main/java/com/simibubi/create/content/contraptions/solver/IKineticController.java new file mode 100644 index 000000000..874eef13c --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/IKineticController.java @@ -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; } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/KineticControllerSerial.java b/src/main/java/com/simibubi/create/content/contraptions/solver/KineticControllerSerial.java new file mode 100644 index 000000000..b068f4041 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/KineticControllerSerial.java @@ -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 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); + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/KineticNetwork.java b/src/main/java/com/simibubi/create/content/contraptions/solver/KineticNetwork.java index db7e6a386..83e59c9ad 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/solver/KineticNetwork.java +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/KineticNetwork.java @@ -10,6 +10,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; public class KineticNetwork { @@ -28,18 +29,18 @@ public class KineticNetwork { private final Set potentialNewBranches = new HashSet<>(); private final ResetableLazy totalStressImpact = ResetableLazy.of(() -> - (float) members.stream().mapToDouble(n -> n.getTotalStressImpact(rootTheoreticalSpeed)).sum()); + (float) members.stream().mapToDouble(KineticNode::getTotalStressImpact).sum()); private final ResetableLazy totalStressCapacity = ResetableLazy.of(() -> (float) members.stream().mapToDouble(KineticNode::getStressCapacity).sum()); private boolean ticked; private final Set stressConnectors = new HashSet<>(); - public KineticNetwork(KineticNode root) { + protected KineticNetwork(KineticNode root) { addMember(root); } - public void addMember(KineticNode node) { + protected void addMember(KineticNode node) { members.add(node); potentialNewBranches.add(node); if (node.getConnections().hasStressOnlyConnections()) stressConnectors.add(node); @@ -52,11 +53,11 @@ public class KineticNetwork { if (node.hasStressCapacity()) onMemberStressCapacityUpdated(); } - public void onMemberLoaded(KineticNode node) { + protected void onMemberLoaded(KineticNode node) { potentialNewBranches.add(node); } - public void onMemberGeneratedSpeedUpdated(KineticNode node) { + protected void onMemberGeneratedSpeedUpdated(KineticNode node) { if (node.isGenerator()) { generators.add(node); } else { @@ -66,15 +67,15 @@ public class KineticNetwork { onMemberStressCapacityUpdated(); } - public void onMemberStressImpactUpdated() { + protected void onMemberStressImpactUpdated() { totalStressImpact.reset(); } - public void onMemberStressCapacityUpdated() { + protected void onMemberStressCapacityUpdated() { totalStressCapacity.reset(); } - public void removeMember(KineticNode node) { + protected void removeMember(KineticNode node) { if (node.isGenerator() && generators.contains(node)) { generators.remove(node); rootSpeedDirty = true; @@ -87,25 +88,25 @@ public class KineticNetwork { 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(); 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. * @return CONTRADICTION if the network has cycles with conflicting speed ratios or generators turning against * each other, and OK otherwise */ - public SolveResult tryRecalculateSpeed() { + protected SolveResult tryRecalculateSpeed() { SolveResult result = tryRecalculateTheoreticalSpeed(); if (isStopped()) return SolveResult.OK; return result; } - private SolveResult tryRecalculateTheoreticalSpeed() { + protected SolveResult tryRecalculateTheoreticalSpeed() { SolveResult result = conflictingCycles.isEmpty() ? SolveResult.OK : SolveResult.CONTRADICTION; if (!rootSpeedDirty) return result; @@ -152,7 +153,11 @@ public class KineticNetwork { return totalStressCapacity.get(); } - private float getRootSpeed() { + public float getRootTheoreticalSpeed() { + return rootTheoreticalSpeed; + } + + public float getRootSpeed() { return isStopped() ? 0 : rootTheoreticalSpeed; } @@ -161,45 +166,52 @@ public class KineticNetwork { onMemberStressImpactUpdated(); } - public void untick() { + protected void untick() { ticked = false; } - public void tick(List newNetworks) { + protected void tick(List newNetworks) { if (ticked) return; Set stressConnected = stressConnectors.stream() .flatMap(KineticNode::getActiveStressOnlyConnections) + .map(KineticNode::getNetwork) .collect(Collectors.toSet()); stressConnected.add(this); float stressImpact = 0; float stressCapacity = 0; + Set popQueue = new HashSet<>(); + Consumer pop = n -> { n.popBlock(); newNetworks.add(n.getNetwork()); }; for (KineticNetwork cur : stressConnected) { cur.ticked = true; - cur.updateMemberSpeeds(newNetworks); + cur.updateMemberSpeeds(popQueue::add); stressImpact += cur.getTotalStressImpact(); stressCapacity += cur.getTotalStressCapacity(); } 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) { - if (cur.generators.isEmpty()) { - cur.overstressed = false; - } else if (nowOverstressed) { + if (nowOverstressed) { if (!cur.overstressed) { + // just became overstressed cur.overstressed = true; cur.onRootSpeedChanged(); cur.members.forEach(KineticNode::stop); } } else { if (cur.overstressed) { + // just became non-overstressed cur.overstressed = false; cur.onRootSpeedChanged(); 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 - * @param newNetworks a List that any new networks created during this call will be added to + * Update the speed of every member, starting from the main generator and checking for speeding nodes along the way + * @param onSpeeding a function to call whenever a speeding node is found and should be popped */ - private void updateMemberSpeeds(List newNetworks) { + private void updateMemberSpeeds(Consumer onSpeeding) { SolveResult recalculateSpeedResult = tryRecalculateSpeed(); // generators should not be turning against each other or have conflicting cycles by now assert(recalculateSpeedResult.isOk()); @@ -225,17 +237,17 @@ public class KineticNetwork { if (rootSpeedChanged) { // root speed changed, update all nodes starting from the main generator rootSpeedChanged = false; - bfs(mainGenerator, newNetworks, false); + bfs(mainGenerator, onSpeeding, false); } else if (!potentialNewBranches.isEmpty()) { // new nodes added, update only the new network branches potentialNewBranches.stream() .filter(n -> !potentialNewBranches.contains(n.getSource())) - .forEach(n -> bfs(n, newNetworks, true)); + .forEach(n -> bfs(n, onSpeeding, true)); } potentialNewBranches.clear(); } - private void bfs(KineticNode root, List newNetworks, boolean followSource) { + private void bfs(KineticNode root, Consumer onSpeeding, boolean followSource) { // update node speeds in a breadth-first order, checking for speeding nodes along the way Set visited = new HashSet<>(); List frontier = new LinkedList<>(); @@ -246,15 +258,14 @@ public class KineticNetwork { if (!members.contains(cur) || visited.contains(cur)) continue; visited.add(cur); - if (cur.tryUpdateSpeed(getRootSpeed()).isOk()) { + if (cur.tryUpdateSpeed().isOk()) { cur.getActiveConnections() .map(Pair::getFirst) .filter(n -> !followSource || n.getSource() == cur) .forEach(frontier::add); } else { // stop searching on this branch once a speeding node is found - cur.popBlock(); - newNetworks.add(cur.getNetwork()); + onSpeeding.accept(cur); } } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/KineticNode.java b/src/main/java/com/simibubi/create/content/contraptions/solver/KineticNode.java index 62213d689..e44c6c9fa 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/solver/KineticNode.java +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/KineticNode.java @@ -2,6 +2,7 @@ package com.simibubi.create.content.contraptions.solver; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.foundation.config.AllConfigs; +import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.Pair; import net.minecraft.core.BlockPos; @@ -11,9 +12,11 @@ import net.minecraft.util.Mth; import javax.annotation.Nullable; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -22,6 +25,10 @@ public class KineticNode { private final KineticSolver solver; private @Nullable KineticTileEntity entity; + private @Nullable IKineticController controller; + private @Nullable KineticControllerSerial controllerType; + private @Nullable Set controlling; + private @Nullable KineticNode source; private KineticNetwork network; private float speedRatio = 1; @@ -35,18 +42,22 @@ public class KineticNode { private float stressImpact; private final boolean constantStress; + protected KineticNode regen() { + return new KineticNode(solver, pos, entity, getController().get(), controllerType); + } + 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.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); + this.controller = controller; + this.controllerType = controllerType; } private KineticNode(KineticSolver solver, BlockPos pos, KineticConnections connections, float generatedSpeed, @@ -65,17 +76,29 @@ public class KineticNode { public CompoundTag save(CompoundTag tag) { 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.putFloat("Generated", generatedSpeed); tag.putFloat("Capacity", stressCapacity); tag.putFloat("Impact", stressImpact); - if (constantStress) + if (constantStress) { tag.putBoolean("ConstantStress", true); + } return tag; } public static KineticNode load(KineticSolver solver, CompoundTag tag) { 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")); float generatedSpeed = tag.getFloat("Generated"); float stressCapacity = tag.getFloat("Capacity"); @@ -88,16 +111,38 @@ public class KineticNode { return entity != null; } - public void onLoaded(KineticTileEntity entity) { + protected void onLoaded(KineticTileEntity entity) { this.entity = entity; network.onMemberLoaded(this); - if (speedCur != 0) entity.setSpeed(speedCur); + if (speedCur != 0) + entity.setSpeed(speedCur); } - public void onUnloaded() { + protected void onUnloaded() { this.entity = null; } + public Optional 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() { return connections; } @@ -127,12 +172,15 @@ public class KineticNode { return getActiveConnections().collect(Collectors.toList()); } - public Stream getActiveStressOnlyConnections() { + public Stream getActiveStressOnlyConnections() { return connections.getDirections().stream() .map(d -> solver.getNode(pos.offset(d)) .filter(n -> connections.checkStressOnlyConnection(n.connections, d))) - .flatMap(Optional::stream) - .map(KineticNode::getNetwork); + .flatMap(Optional::stream); + } + + public float getGeneratedSpeed() { + return generatedSpeed; } public float getGeneratedSpeedAtRoot() { @@ -143,36 +191,42 @@ public class KineticNode { return generatedSpeed != 0; } - public boolean onUpdated() { - if (entity == null) return false; + protected enum UpdateResult { + 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(); - if (this.generatedSpeed != generatedSpeedNew) { - this.generatedSpeed = generatedSpeedNew; - changed = true; - network.onMemberGeneratedSpeedUpdated(this); - if (network.tryRecalculateSpeed().isContradiction()) { - popBlock(); + float generatedSpeedNew = ctl.getGeneratedSpeed(); + if (this.generatedSpeed != generatedSpeedNew) { + this.generatedSpeed = generatedSpeedNew; + network.onMemberGeneratedSpeedUpdated(this); + if (network.tryRecalculateSpeed().isContradiction()) { + return UpdateResult.NEEDS_POP; + } + result = UpdateResult.CHANGED; } - } - float stressImpactNew = entity.getStressImpact(); - if (this.stressImpact != stressImpactNew) { - this.stressImpact = stressImpactNew; - changed = true; - network.onMemberStressImpactUpdated(); - } + float stressImpactNew = ctl.getStressImpact(); + if (this.stressImpact != stressImpactNew) { + this.stressImpact = stressImpactNew; + network.onMemberStressImpactUpdated(); + result = UpdateResult.CHANGED; + } - float stressCapacityNew = entity.getStressCapacity(); - if (this.stressCapacity != stressCapacityNew) { - this.stressCapacity = stressCapacityNew; - changed = true; - network.onMemberStressCapacityUpdated(); - } + float stressCapacityNew = ctl.getStressCapacity(); + if (this.stressCapacity != stressCapacityNew) { + this.stressCapacity = stressCapacityNew; + network.onMemberStressCapacityUpdated(); + result = UpdateResult.CHANGED; + } - return changed; + return result; + }).orElse(UpdateResult.UNCHANGED); } public boolean hasStressCapacity() { @@ -183,16 +237,20 @@ public class KineticNode { return stressImpact != 0; } - public float getTheoreticalSpeed(float speedAtRoot) { - return speedAtRoot * speedRatio; + public float getSpeed() { + return network.getRootSpeed() * speedRatio; + } + + public float getTheoreticalSpeed() { + return network.getRootTheoreticalSpeed() * speedRatio; } public float getStressCapacity() { return constantStress ? stressCapacity : stressCapacity * Math.abs(generatedSpeed); } - public float getTotalStressImpact(float speedAtRoot) { - return constantStress ? stressImpact : stressImpact * Math.abs(getTheoreticalSpeed(speedAtRoot)); + public float getTotalStressImpact() { + return constantStress ? stressImpact : stressImpact * Math.abs(getTheoreticalSpeed()); } private SolveResult setNetwork(KineticNetwork network) { @@ -202,7 +260,7 @@ public class KineticNode { return network.tryRecalculateSpeed(); } - public @Nullable KineticNode getSource() { + protected @Nullable KineticNode getSource() { return source; } @@ -212,7 +270,7 @@ public class KineticNode { return setNetwork(from.network); } - public void onAdded() { + protected void onAdded() { getActiveConnections() .findAny() .ifPresent(e -> { @@ -264,12 +322,18 @@ public class KineticNode { } } - public void onRemoved() { + protected void onRemoved() { network.removeMember(this); getActiveConnections() .map(Pair::getFirst) .filter(n -> n.source == this) .forEach(KineticNode::rerootHere); + if (controlling != null) { + controlling.stream() + .map(solver::getNode) + .flatMap(Optional::stream) + .forEach(KineticNode::removeController); + } } 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. - * @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) { - speedNext = getTheoreticalSpeed(speedAtRoot); + protected SolveResult tryUpdateSpeed() { + speedNext = getSpeed(); if (Math.abs(speedNext) > AllConfigs.SERVER.kinetics.maxRotationSpeed.get()) return SolveResult.CONTRADICTION; return SolveResult.OK; @@ -296,7 +359,7 @@ public class KineticNode { speedNext = 0; } - public void flushChangedSpeed() { + protected void flushChangedSpeed() { if (speedCur != speedNext) { speedCur = speedNext; if (entity != null) { @@ -305,7 +368,7 @@ public class KineticNode { } } - public void popBlock() { + protected void popBlock() { if (entity != null) { solver.removeAndPopNow(entity); } else { diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/KineticSolver.java b/src/main/java/com/simibubi/create/content/contraptions/solver/KineticSolver.java index 64f6ed50b..93a930e0e 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/solver/KineticSolver.java +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/KineticSolver.java @@ -109,20 +109,14 @@ public class KineticSolver extends SavedData { 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 - removeNode(entity); - addNode(entity); - } else { - // connections are the same, so just update in case other properties changed - if (node.onUpdated()) { - setDirty(); - } - } + private void regenNode(KineticNode node) { + BlockPos pos = node.getPos(); + nodes.remove(pos); + node.onRemoved(); + KineticNode newNode = node.regen(); + nodes.put(pos, newNode); + newNode.onAdded(); + setDirty(); } public void unloadNode(KineticTileEntity entity) { @@ -130,7 +124,7 @@ public class KineticSolver extends SavedData { if (node != null) node.onUnloaded(); } - protected Optional getNode(BlockPos pos) { + public Optional getNode(BlockPos pos) { return Optional.ofNullable(nodes.get(pos)); } @@ -164,9 +158,25 @@ public class KineticSolver extends SavedData { level.destroyBlock(pos, true); } - public void tick() { - Set networks = nodes.values().stream().map(KineticNode::getNetwork).collect(Collectors.toSet()); - networks.forEach(KineticNetwork::untick); + public void tick(Level level) { + Set popQueue = new HashSet<>(); + Set 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 networks = new HashSet<>(); + for (KineticNode node : nodes.values()) { + networks.add(node.getNetwork()); + node.getNetwork().untick(); + } List frontier = new LinkedList<>(); diff --git a/src/main/java/com/simibubi/create/events/CommonEvents.java b/src/main/java/com/simibubi/create/events/CommonEvents.java index ec5c9e431..deda0e06b 100644 --- a/src/main/java/com/simibubi/create/events/CommonEvents.java +++ b/src/main/java/com/simibubi/create/events/CommonEvents.java @@ -112,7 +112,7 @@ public class CommonEvents { CouplingPhysics.tick(world); LinkedControllerServerHandler.tick(world); - KineticSolver.getSolver(world).tick(); + KineticSolver.getSolver(world).tick(world); } @SubscribeEvent @@ -169,14 +169,12 @@ public class CommonEvents { public static void onLoadWorld(WorldEvent.Load event) { LevelAccessor world = event.getWorld(); Create.REDSTONE_LINK_NETWORK_HANDLER.onLoadWorld(world); - Create.TORQUE_PROPAGATOR.onLoadWorld(world); } @SubscribeEvent public static void onUnloadWorld(WorldEvent.Unload event) { LevelAccessor world = event.getWorld(); Create.REDSTONE_LINK_NETWORK_HANDLER.onUnloadWorld(world); - Create.TORQUE_PROPAGATOR.onUnloadWorld(world); WorldAttached.invalidateWorld(world); }