Continue work on solver

This commit is contained in:
Jozufozu 2021-12-23 17:52:01 -08:00
parent 760bffe343
commit 69d33525f6
13 changed files with 294 additions and 98 deletions

View file

@ -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<BlockPos, Connection> connectionsFrom = new HashMap<>();
private final Map<BlockPos, Connection> connectionsTo = new HashMap<>();
private final List<Goal> 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);
}
}
}
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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<CreativeMotorTileEntity> {
public class CreativeMotorBlock extends DirectionalKineticBlock implements ITE<CreativeMotorTileEntity>, SolverBlock {
public CreativeMotorBlock(Properties properties) {
super(properties);
@ -54,7 +58,7 @@ public class CreativeMotorBlock extends DirectionalKineticBlock implements ITE<C
public boolean hideStressImpact() {
return true;
}
@Override
public boolean isPathfindable(BlockState state, BlockGetter reader, BlockPos pos, PathComputationType type) {
return false;
@ -69,5 +73,9 @@ public class CreativeMotorBlock extends DirectionalKineticBlock implements ITE<C
public BlockEntityType<? extends CreativeMotorTileEntity> getTileEntityType() {
return AllTileEntities.MOTOR.get();
}
@Override
public void created(KineticSolver solver, Level level, BlockPos pos) {
solver.addGoal(new GeneratorGoal(pos, 16));
}
}

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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,
}
}

View file

@ -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<KineticSolver> SOLVERS = new WorldAttached<>($ -> new KineticSolver());
public static KineticSolver getSolver(Level level) {
return SOLVERS.get(level);
}
private final PropertyMap properties = new PropertyMap();
private final List<Goal> goals = new ArrayList<>();
public static class PropertyMap {
private final Map<BlockPos, Map<String, Value>> properties = new HashMap<>();
private final Map<BlockPos, Set<Connection>> connections = new HashMap<>();
public Optional<Value> getProperty(BlockPos pos, String property) {
Map<String, Value> 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<Connection> 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<BlockPos> solve() {
if (!needsUpdate) return Collections.emptyList();
needsUpdate = false;
List<BlockPos> 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);
}
}

View file

@ -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);
}

View file

@ -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;
}
}
}

View file

@ -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