From 69d33525f6fb0cda9181c287f5a4b89fa14f9f0b Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Thu, 23 Dec 2021 17:52:01 -0800 Subject: [PATCH] Continue work on solver --- .../content/contraptions/KineticSolver.java | 78 ------------ .../contraptions/RotationPropagator.java | 22 ++-- .../contraptions/base/KineticTileEntity.java | 8 ++ .../components/motor/CreativeMotorBlock.java | 14 ++- .../relays/elementary/ShaftBlock.java | 15 ++- .../contraptions/solver/Connection.java | 28 +++++ .../contraptions/solver/ConnectionGoal.java | 43 +++++++ .../contraptions/solver/GeneratorGoal.java | 24 ++++ .../content/contraptions/solver/Goal.java | 17 +++ .../contraptions/solver/KineticSolver.java | 111 ++++++++++++++++++ .../contraptions/solver/SolverBlock.java | 8 ++ .../content/contraptions/solver/Value.java | 21 ++++ .../simibubi/create/events/CommonEvents.java | 3 + 13 files changed, 294 insertions(+), 98 deletions(-) delete mode 100644 src/main/java/com/simibubi/create/content/contraptions/KineticSolver.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/solver/Connection.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/solver/ConnectionGoal.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/solver/GeneratorGoal.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/solver/Goal.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/solver/KineticSolver.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/solver/SolverBlock.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/solver/Value.java diff --git a/src/main/java/com/simibubi/create/content/contraptions/KineticSolver.java b/src/main/java/com/simibubi/create/content/contraptions/KineticSolver.java deleted file mode 100644 index e6fb470d1..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/KineticSolver.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.simibubi.create.content.contraptions; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; - -public class KineticSolver { - - private final Map connectionsFrom = new HashMap<>(); - private final Map connectionsTo = new HashMap<>(); - - private final List goals = new ArrayList<>(); - - private boolean needsUpdate; - - public void solve() { - if (!needsUpdate) return; - needsUpdate = false; - - - } - - public void addFact(BlockPos pos) { - - } - - public void addGoal(Goal goal) { - goals.add(goal); - needsUpdate = true; - } - - public interface SolverBlock { - void created(KineticSolver solver, Level level, BlockPos pos); - } - - public static abstract class Connection { - public final BlockPos from; - public final BlockPos to; - - public Connection(BlockPos from, BlockPos to) { - this.from = from; - this.to = to; - } - - public abstract boolean isCompatible(Connection that); - - public static final class Shaft extends Connection { - - public Shaft(BlockPos pos, Direction face) { - super(pos, pos.relative(face)); - } - - @Override - public boolean isCompatible(Connection that) { - return that instanceof Shaft; - } - } - } - - public static class Goal { - public final Connection connection; - - public Goal(Connection connection) { - this.connection = connection; - } - - public static final class EqualSpeed extends Goal { - public EqualSpeed(Connection connection) { - super(connection); - } - } - } -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/RotationPropagator.java b/src/main/java/com/simibubi/create/content/contraptions/RotationPropagator.java index 7b0bd897f..1d94ebf23 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/RotationPropagator.java +++ b/src/main/java/com/simibubi/create/content/contraptions/RotationPropagator.java @@ -36,7 +36,7 @@ public class RotationPropagator { * Determines the change in rotation between two attached kinetic entities. For * instance, an axis connection returns 1 while a 1-to-1 gear connection * reverses the rotation and therefore returns -1. - * + * * @param from * @param to * @return @@ -201,21 +201,21 @@ public class RotationPropagator { /** * Insert the added position to the kinetic network. - * + * * @param worldIn * @param pos */ public static void handleAdded(Level worldIn, BlockPos pos, KineticTileEntity addedTE) { - if (worldIn.isClientSide) - return; - if (!worldIn.isLoaded(pos)) - return; - propagateNewSource(addedTE); +// if (worldIn.isClientSide) +// return; +// if (!worldIn.isLoaded(pos)) +// return; +// propagateNewSource(addedTE); } /** * Search for sourceless networks attached to the given entity and update them. - * + * * @param currentTE */ private static void propagateNewSource(KineticTileEntity currentTE) { @@ -230,7 +230,7 @@ public class RotationPropagator { if (newSpeed == 0 && oppositeSpeed == 0) continue; - + boolean incompatible = Math.signum(newSpeed) != Math.signum(speedOfNeighbour) && (newSpeed != 0 && speedOfNeighbour != 0); @@ -300,7 +300,7 @@ public class RotationPropagator { /** * Remove the given entity from the network. - * + * * @param worldIn * @param pos * @param removedTE @@ -333,7 +333,7 @@ public class RotationPropagator { /** * Clear the entire subnetwork depending on the given entity and find a new * source - * + * * @param updateTE */ private static void propagateMissingSource(KineticTileEntity updateTE) { 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 5b78108e3..44945670e 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 @@ -18,6 +18,8 @@ import com.simibubi.create.content.contraptions.goggles.IHaveGoggleInformation; 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.KineticSolver; +import com.simibubi.create.content.contraptions.solver.SolverBlock; import com.simibubi.create.foundation.block.BlockStressValues; import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.item.TooltipHelper; @@ -344,6 +346,12 @@ public class KineticTileEntity extends SmartTileEntity public void attachKinetics() { updateSpeed = false; + KineticSolver solver = KineticSolver.getSolver(level); + + BlockState state = getBlockState(); + if (state.getBlock() instanceof SolverBlock sb) { + sb.created(solver, level, worldPosition); + } RotationPropagator.handleAdded(level, worldPosition, this); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/motor/CreativeMotorBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/motor/CreativeMotorBlock.java index 8c5a2f971..530a5a082 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/motor/CreativeMotorBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/motor/CreativeMotorBlock.java @@ -3,6 +3,9 @@ package com.simibubi.create.content.contraptions.components.motor; import com.simibubi.create.AllShapes; import com.simibubi.create.AllTileEntities; import com.simibubi.create.content.contraptions.base.DirectionalKineticBlock; +import com.simibubi.create.content.contraptions.solver.GeneratorGoal; +import com.simibubi.create.content.contraptions.solver.KineticSolver; +import com.simibubi.create.content.contraptions.solver.SolverBlock; import com.simibubi.create.foundation.block.ITE; import net.minecraft.core.BlockPos; @@ -10,6 +13,7 @@ import net.minecraft.core.Direction; import net.minecraft.core.Direction.Axis; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -17,7 +21,7 @@ import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; -public class CreativeMotorBlock extends DirectionalKineticBlock implements ITE { +public class CreativeMotorBlock extends DirectionalKineticBlock implements ITE, SolverBlock { public CreativeMotorBlock(Properties properties) { super(properties); @@ -54,7 +58,7 @@ public class CreativeMotorBlock extends DirectionalKineticBlock implements ITE getTileEntityType() { return AllTileEntities.MOTOR.get(); } - + + @Override + public void created(KineticSolver solver, Level level, BlockPos pos) { + solver.addGoal(new GeneratorGoal(pos, 16)); + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/ShaftBlock.java b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/ShaftBlock.java index ab3601d06..85768e425 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/ShaftBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/ShaftBlock.java @@ -4,9 +4,12 @@ import java.util.function.Predicate; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllShapes; -import com.simibubi.create.content.contraptions.KineticSolver; +import com.simibubi.create.content.contraptions.solver.KineticSolver; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.relays.encased.EncasedShaftBlock; +import com.simibubi.create.content.contraptions.solver.Connection; +import com.simibubi.create.content.contraptions.solver.ConnectionGoal; +import com.simibubi.create.content.contraptions.solver.SolverBlock; import com.simibubi.create.foundation.advancement.AllTriggers; import com.simibubi.create.foundation.utility.placement.IPlacementHelper; import com.simibubi.create.foundation.utility.placement.PlacementHelpers; @@ -27,7 +30,7 @@ import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; -public class ShaftBlock extends AbstractShaftBlock implements KineticSolver.SolverBlock { +public class ShaftBlock extends AbstractShaftBlock implements SolverBlock { private static final int placementHelperId = PlacementHelpers.register(new PlacementHelper()); @@ -92,11 +95,11 @@ public class ShaftBlock extends AbstractShaftBlock implements KineticSolver.Solv Direction positive = Direction.fromAxisAndDirection(axis, Direction.AxisDirection.POSITIVE); - KineticSolver.Connection.Shaft c1 = new KineticSolver.Connection.Shaft(pos, positive); - KineticSolver.Connection.Shaft c2 = new KineticSolver.Connection.Shaft(pos, positive.getOpposite()); + Connection.Shaft c1 = new Connection.Shaft(pos, positive); + Connection.Shaft c2 = new Connection.Shaft(pos, positive.getOpposite()); - solver.addGoal(new KineticSolver.Goal.EqualSpeed(c1)); - solver.addGoal(new KineticSolver.Goal.EqualSpeed(c2)); + solver.addGoal(new ConnectionGoal.EqualSpeed(c1)); + solver.addGoal(new ConnectionGoal.EqualSpeed(c2)); } @MethodsReturnNonnullByDefault diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/Connection.java b/src/main/java/com/simibubi/create/content/contraptions/solver/Connection.java new file mode 100644 index 000000000..1a4915ff4 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/Connection.java @@ -0,0 +1,28 @@ +package com.simibubi.create.content.contraptions.solver; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; + +public abstract class Connection { + public final BlockPos from; + public final BlockPos to; + + public Connection(BlockPos from, BlockPos to) { + this.from = from; + this.to = to; + } + + public abstract boolean isCompatible(Connection that); + + public static final class Shaft extends Connection { + + public Shaft(BlockPos pos, Direction face) { + super(pos, pos.relative(face)); + } + + @Override + public boolean isCompatible(Connection that) { + return that instanceof Shaft; + } + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/ConnectionGoal.java b/src/main/java/com/simibubi/create/content/contraptions/solver/ConnectionGoal.java new file mode 100644 index 000000000..aa5f68f26 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/ConnectionGoal.java @@ -0,0 +1,43 @@ +package com.simibubi.create.content.contraptions.solver; + +import net.minecraft.core.BlockPos; + +public abstract class ConnectionGoal implements Goal { + public final Connection connection; + + public ConnectionGoal(Connection connection) { + this.connection = connection; + } + + @Override + public void onAdded(KineticSolver.PropertyMap solver) { + solver.addConnection(connection); + } + + public static class EqualSpeed extends ConnectionGoal { + public EqualSpeed(Connection connection) { + super(connection); + } + + @Override + public BlockPos getPos() { + return connection.from; + } + + @Override + public SolveResult solve(KineticSolver.PropertyMap solver) { + if (solver.isComplete(connection)) { + Value toSpeed = solver.getOrCreateProperty(connection.to, "speed"); + Value fromSpeed = solver.getOrCreateProperty(connection.from, "speed"); + if (toSpeed instanceof Value.Known toValue && fromSpeed instanceof Value.Known fromValue) { + if (toValue.value != fromValue.value) { + return Goal.SolveResult.CONTRADICTION; + } + } else { + solver.setProperty(connection.to, "speed", fromSpeed); + } + } + return Goal.SolveResult.OK; + } + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/GeneratorGoal.java b/src/main/java/com/simibubi/create/content/contraptions/solver/GeneratorGoal.java new file mode 100644 index 000000000..69e5d85c9 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/GeneratorGoal.java @@ -0,0 +1,24 @@ +package com.simibubi.create.content.contraptions.solver; + +import net.minecraft.core.BlockPos; + +public final class GeneratorGoal implements Goal { + public final BlockPos me; + public final float speed; + + public GeneratorGoal(BlockPos me, float speed) { + this.me = me; + this.speed = speed; + } + + @Override + public BlockPos getPos() { + return me; + } + + @Override + public SolveResult solve(KineticSolver.PropertyMap solver) { + solver.setProperty(me, "speed", new Value.Known(speed)); + return Goal.SolveResult.OK; + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/Goal.java b/src/main/java/com/simibubi/create/content/contraptions/solver/Goal.java new file mode 100644 index 000000000..2a4357756 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/Goal.java @@ -0,0 +1,17 @@ +package com.simibubi.create.content.contraptions.solver; + +import net.minecraft.core.BlockPos; + +public interface Goal { + BlockPos getPos(); + + SolveResult solve(KineticSolver.PropertyMap solver); + + default void onAdded(KineticSolver.PropertyMap solver) { + } + + enum SolveResult { + CONTRADICTION, + OK, + } +} 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 new file mode 100644 index 000000000..7f67255fd --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/KineticSolver.java @@ -0,0 +1,111 @@ +package com.simibubi.create.content.contraptions.solver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import com.simibubi.create.foundation.utility.WorldAttached; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; + +public class KineticSolver { + + private static final WorldAttached SOLVERS = new WorldAttached<>($ -> new KineticSolver()); + + public static KineticSolver getSolver(Level level) { + return SOLVERS.get(level); + } + + private final PropertyMap properties = new PropertyMap(); + + private final List goals = new ArrayList<>(); + + public static class PropertyMap { + private final Map> properties = new HashMap<>(); + private final Map> connections = new HashMap<>(); + + public Optional getProperty(BlockPos pos, String property) { + Map map = properties.get(pos); + + if (map != null) { + return Optional.of(map.computeIfAbsent(property, $ -> new Value.Unknown())); + } else { + return Optional.empty(); + } + } + + public Value getOrCreateProperty(BlockPos pos, String property) { + return properties.computeIfAbsent(pos, $ -> new HashMap<>()) + .computeIfAbsent(property, $ -> new Value.Unknown()); + } + + public void setProperty(BlockPos pos, String property, Value value) { + properties.computeIfAbsent(pos, $ -> new HashMap<>()) + .put(property, value); + } + + public boolean isComplete(Connection connection) { + Set connections = this.connections.get(connection.to); + + if (connections == null) return false; + + for (Connection other : connections) { + if (connection.isCompatible(other) && other.to.equals(connection.from)) { + return true; + } + } + + return false; + } + + public void addConnection(Connection connection) { + properties.computeIfAbsent(connection.from, $ -> new HashMap<>()); + connections.computeIfAbsent(connection.from, $ -> new HashSet<>()).add(connection); + } + + public void clear() { + properties.clear(); + } + } + + private boolean needsUpdate; + + public List solve() { + if (!needsUpdate) return Collections.emptyList(); + needsUpdate = false; + + List troublemakers = new ArrayList<>(); + + outer: + while (true) { + properties.clear(); + + for (Goal goal : goals) { + Goal.SolveResult result = goal.solve(properties); + + switch (result) { + case CONTRADICTION -> { + troublemakers.add(goal.getPos()); + continue outer; + } + case OK -> {} + } + } + break; + } + + return troublemakers; + } + + public void addGoal(Goal goal) { + goals.add(goal); + needsUpdate = true; + goal.onAdded(properties); + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/SolverBlock.java b/src/main/java/com/simibubi/create/content/contraptions/solver/SolverBlock.java new file mode 100644 index 000000000..489900ea6 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/SolverBlock.java @@ -0,0 +1,8 @@ +package com.simibubi.create.content.contraptions.solver; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; + +public interface SolverBlock { + void created(KineticSolver solver, Level level, BlockPos pos); +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/solver/Value.java b/src/main/java/com/simibubi/create/content/contraptions/solver/Value.java new file mode 100644 index 000000000..60460c50a --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/solver/Value.java @@ -0,0 +1,21 @@ +package com.simibubi.create.content.contraptions.solver; + +public sealed class Value { + public static final class Unknown extends Value { + private static int nextID = 0; + + public final int id; + + public Unknown() { + this.id = nextID++; + } + } + + public static final class Known extends Value { + public final float value; + + public Known(float value) { + this.value = value; + } + } +} diff --git a/src/main/java/com/simibubi/create/events/CommonEvents.java b/src/main/java/com/simibubi/create/events/CommonEvents.java index b34db0aac..8547d4a05 100644 --- a/src/main/java/com/simibubi/create/events/CommonEvents.java +++ b/src/main/java/com/simibubi/create/events/CommonEvents.java @@ -7,6 +7,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.tra import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.CapabilityMinecartController; import com.simibubi.create.content.contraptions.fluids.recipe.FluidTransferRecipes; import com.simibubi.create.content.contraptions.fluids.recipe.PotionMixingRecipeManager; +import com.simibubi.create.content.contraptions.solver.KineticSolver; import com.simibubi.create.content.contraptions.wrench.WrenchItem; import com.simibubi.create.content.curiosities.toolbox.ToolboxHandler; import com.simibubi.create.content.curiosities.weapons.PotatoProjectileTypeManager; @@ -110,6 +111,8 @@ public class CommonEvents { CapabilityMinecartController.tick(world); CouplingPhysics.tick(world); LinkedControllerServerHandler.tick(world); + + KineticSolver.getSolver(world).solve(); } @SubscribeEvent