mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-01-11 23:07:13 +01:00
New approach
This commit is contained in:
parent
69d33525f6
commit
8f5a885bb0
17 changed files with 456 additions and 216 deletions
|
@ -346,16 +346,24 @@ public class KineticTileEntity extends SmartTileEntity
|
|||
|
||||
public void attachKinetics() {
|
||||
updateSpeed = false;
|
||||
KineticSolver solver = KineticSolver.getSolver(level);
|
||||
|
||||
KineticSolver solver = KineticSolver.getSolver(level);
|
||||
BlockState state = getBlockState();
|
||||
if (state.getBlock() instanceof SolverBlock sb) {
|
||||
solver.removeAllRules(worldPosition);
|
||||
sb.created(solver, level, worldPosition);
|
||||
}
|
||||
|
||||
RotationPropagator.handleAdded(level, worldPosition, this);
|
||||
}
|
||||
|
||||
public void detachKinetics() {
|
||||
KineticSolver solver = KineticSolver.getSolver(level);
|
||||
BlockState state = getBlockState();
|
||||
if (state.getBlock() instanceof SolverBlock) {
|
||||
solver.removeAllRules(worldPosition);
|
||||
}
|
||||
|
||||
RotationPropagator.handleRemoved(level, worldPosition, this);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ 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.ConstantSpeedRule;
|
||||
import com.simibubi.create.content.contraptions.solver.HalfShaftConnectionRule;
|
||||
import com.simibubi.create.content.contraptions.solver.KineticSolver;
|
||||
import com.simibubi.create.content.contraptions.solver.SolverBlock;
|
||||
import com.simibubi.create.foundation.block.ITE;
|
||||
|
@ -76,6 +77,11 @@ public class CreativeMotorBlock extends DirectionalKineticBlock implements ITE<C
|
|||
|
||||
@Override
|
||||
public void created(KineticSolver solver, Level level, BlockPos pos) {
|
||||
solver.addGoal(new GeneratorGoal(pos, 16));
|
||||
BlockState state = level.getBlockState(pos);
|
||||
Direction to = state.getValue(FACING);
|
||||
int speed = getTileEntityOptional(level, pos).map(te -> te.generatedSpeed.getValue()).orElse(0);
|
||||
|
||||
solver.addRule(pos, new HalfShaftConnectionRule(to));
|
||||
solver.addRule(pos, new ConstantSpeedRule(speed));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ import com.simibubi.create.AllShapes;
|
|||
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.ShaftConnectionRule;
|
||||
import com.simibubi.create.content.contraptions.solver.ShaftEqualSpeedRule;
|
||||
import com.simibubi.create.content.contraptions.solver.SolverBlock;
|
||||
import com.simibubi.create.foundation.advancement.AllTriggers;
|
||||
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
|
||||
|
@ -90,16 +90,13 @@ public class ShaftBlock extends AbstractShaftBlock implements SolverBlock {
|
|||
@Override
|
||||
public void created(KineticSolver solver, Level level, BlockPos pos) {
|
||||
BlockState state = level.getBlockState(pos);
|
||||
|
||||
Direction.Axis axis = state.getValue(AXIS);
|
||||
|
||||
Direction positive = Direction.fromAxisAndDirection(axis, Direction.AxisDirection.POSITIVE);
|
||||
Direction negative = positive.getOpposite();
|
||||
|
||||
Connection.Shaft c1 = new Connection.Shaft(pos, positive);
|
||||
Connection.Shaft c2 = new Connection.Shaft(pos, positive.getOpposite());
|
||||
|
||||
solver.addGoal(new ConnectionGoal.EqualSpeed(c1));
|
||||
solver.addGoal(new ConnectionGoal.EqualSpeed(c2));
|
||||
solver.addRule(pos, new ShaftConnectionRule(axis));
|
||||
solver.addRule(pos, new ShaftEqualSpeedRule(positive));
|
||||
solver.addRule(pos, new ShaftEqualSpeedRule(negative));
|
||||
}
|
||||
|
||||
@MethodsReturnNonnullByDefault
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import net.minecraft.core.Vec3i;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class AllPropertyTypes {
|
||||
public static final Property.Type<Float> SPEED = new Property.Type<>();
|
||||
public static final Property.Type<Set<Vec3i>> SHAFT_CONNECTIONS = new Property.Type<>();
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class ConstantSpeedRule implements RewriteRule.Descriptor<Float> {
|
||||
private final float speed;
|
||||
|
||||
public ConstantSpeedRule(float speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property.Type<Float> getWrittenProperty() {
|
||||
return AllPropertyTypes.SPEED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Float> getRewrittenValue(RewriteRule.PropertyReader reader) {
|
||||
return Optional.of(speed);
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
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,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Vec3i;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class HalfShaftConnectionRule implements RewriteRule.Descriptor<Set<Vec3i>> {
|
||||
private final Set<Vec3i> connections;
|
||||
|
||||
public HalfShaftConnectionRule(Direction dir) {
|
||||
connections = Set.of(dir.getNormal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property.Type<Set<Vec3i>> getWrittenProperty() {
|
||||
return AllPropertyTypes.SHAFT_CONNECTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Set<Vec3i>> getRewrittenValue(RewriteRule.PropertyReader reader) {
|
||||
return Optional.of(connections);
|
||||
}
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
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;
|
||||
|
@ -23,89 +21,70 @@ public class KineticSolver {
|
|||
}
|
||||
|
||||
private final PropertyMap properties = new PropertyMap();
|
||||
private final Map<BlockPos, Set<RewriteRule.Tracker<?>>> rules = new HashMap<>();
|
||||
private final HashSet<RewriteRule.Tracker<?>> allRules = new HashSet<>();
|
||||
|
||||
private final List<Goal> goals = new ArrayList<>();
|
||||
private Set<RewriteRule.Tracker<?>> rulesFrontier = new HashSet<>();
|
||||
|
||||
public static class PropertyMap {
|
||||
private final Map<BlockPos, Map<String, Value>> properties = new HashMap<>();
|
||||
private final Map<BlockPos, Set<Connection>> connections = new HashMap<>();
|
||||
public <T> RewriteRule<T> addRule(BlockPos pos, RewriteRule.Descriptor<T> ruleDesc) {
|
||||
RewriteRule<T> rule = new RewriteRule<>(ruleDesc);
|
||||
RewriteRule.Tracker<?> tracker = new RewriteRule.Tracker<>(rule, pos, properties::trackReader);
|
||||
rules.computeIfAbsent(pos, $ -> new HashSet<>()).add(tracker);
|
||||
allRules.add(tracker);
|
||||
rulesFrontier.add(tracker);
|
||||
return rule;
|
||||
}
|
||||
|
||||
public Optional<Value> getProperty(BlockPos pos, String property) {
|
||||
Map<String, Value> map = properties.get(pos);
|
||||
public void removeRule(BlockPos pos, RewriteRule<?> rule) {
|
||||
Set<RewriteRule.Tracker<?>> trackers = rules.get(pos);
|
||||
if (trackers == null) return;
|
||||
trackers.stream()
|
||||
.filter(t -> t.rule == rule)
|
||||
.findAny()
|
||||
.ifPresent(tracker -> {
|
||||
allRules.remove(tracker);
|
||||
trackers.remove(tracker);
|
||||
if (trackers.isEmpty()) {
|
||||
rules.remove(pos);
|
||||
}
|
||||
properties.untrackReader(tracker);
|
||||
rulesFrontier.addAll(properties.unwrite(tracker.writes));
|
||||
});
|
||||
}
|
||||
|
||||
if (map != null) {
|
||||
return Optional.of(map.computeIfAbsent(property, $ -> new Value.Unknown()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
public void removeAllRules(BlockPos pos) {
|
||||
Set<RewriteRule.Tracker<?>> trackers = rules.remove(pos);
|
||||
if (trackers == null) return;
|
||||
for (RewriteRule.Tracker<?> tracker: trackers) {
|
||||
allRules.remove(tracker);
|
||||
properties.untrackReader(tracker);
|
||||
}
|
||||
|
||||
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();
|
||||
for (RewriteRule.Tracker<?> tracker: trackers) {
|
||||
rulesFrontier.addAll(properties.unwrite(tracker.writes));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needsUpdate;
|
||||
public Set<BlockPos> solve() {
|
||||
Set<BlockPos> contradictions = new HashSet<>();
|
||||
|
||||
public List<BlockPos> solve() {
|
||||
if (!needsUpdate) return Collections.emptyList();
|
||||
needsUpdate = false;
|
||||
while (!rulesFrontier.isEmpty()) {
|
||||
Set<RewriteRule.Tracker<?>> next = new HashSet<>();
|
||||
|
||||
List<BlockPos> troublemakers = new ArrayList<>();
|
||||
for (RewriteRule.Tracker<?> rule : rulesFrontier) {
|
||||
if (!allRules.contains(rule) || !rule.canRewrite()) continue;
|
||||
|
||||
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 -> {}
|
||||
PropertyMap.WriteResult res = rule.rewrite(properties);
|
||||
if (res instanceof PropertyMap.WriteResult.Ok ok) {
|
||||
next.addAll(ok.readyToRewrite);
|
||||
} else if (res instanceof PropertyMap.WriteResult.Contradiction) {
|
||||
removeAllRules(rule.pos);
|
||||
contradictions.add(rule.pos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
rulesFrontier = next;
|
||||
}
|
||||
|
||||
return troublemakers;
|
||||
}
|
||||
|
||||
public void addGoal(Goal goal) {
|
||||
goals.add(goal);
|
||||
needsUpdate = true;
|
||||
goal.onAdded(properties);
|
||||
return contradictions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record Property<T>(BlockPos pos, Type<T> type) {
|
||||
public static class Type<T> { }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Property<?> property = (Property<?>) o;
|
||||
return Objects.equals(pos, property.pos) && Objects.equals(type, property.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(pos, type);
|
||||
}
|
||||
|
||||
public static record Relative<T>(Vec3i offset, Type<T> type) {
|
||||
public Relative(Type<T> type) {
|
||||
this(Vec3i.ZERO, type);
|
||||
}
|
||||
|
||||
public Property<T> toAbsolute(BlockPos pos) {
|
||||
return new Property<>(pos.offset(offset), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Relative<?> relative = (Relative<?>) o;
|
||||
return Objects.equals(offset, relative.offset) && Objects.equals(type, relative.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(offset, type);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PropertyMap {
|
||||
public static sealed class WriteResult {
|
||||
public static final class Ok extends WriteResult {
|
||||
public static final Ok V = new Ok(Set.of());
|
||||
public final Set<RewriteRule.Tracker<?>> readyToRewrite;
|
||||
public Ok(Set<RewriteRule.Tracker<?>> readyToRewrite) { this.readyToRewrite = readyToRewrite; }
|
||||
}
|
||||
|
||||
public static final class Contradiction extends WriteResult {
|
||||
public static final Contradiction V = new Contradiction();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Counter<T> {
|
||||
public final Property<T> key;
|
||||
private T value;
|
||||
private final Set<RewriteRule.Tracker<?>> readers = new HashSet<>();
|
||||
|
||||
public Counter(Property<T> key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return value == null;
|
||||
}
|
||||
|
||||
public boolean canDrop() {
|
||||
return isEmpty() && readers.isEmpty();
|
||||
}
|
||||
|
||||
public Optional<T> read() {
|
||||
return Optional.ofNullable(value);
|
||||
}
|
||||
|
||||
public WriteResult write(@Nonnull T newValue) {
|
||||
WriteResult result = WriteResult.Ok.V;
|
||||
if (isEmpty()) {
|
||||
value = newValue;
|
||||
// notify readers
|
||||
readers.forEach(RewriteRule.Tracker::dependencyRemoved);
|
||||
result = new WriteResult.Ok(readers.stream()
|
||||
.filter(RewriteRule.Tracker::canRewrite)
|
||||
.collect(Collectors.toSet()));
|
||||
} else if (!value.equals(newValue)) {
|
||||
return WriteResult.Contradiction.V;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void unwrite() {
|
||||
if (isEmpty()) throw new IllegalStateException();
|
||||
value = null;
|
||||
// notify readers
|
||||
readers.forEach(RewriteRule.Tracker::dependencyAdded);
|
||||
}
|
||||
|
||||
public boolean trackReader(RewriteRule.Tracker<?> reader) {
|
||||
readers.add(reader);
|
||||
return isEmpty();
|
||||
}
|
||||
|
||||
public void untrackReader(RewriteRule.Tracker<?> reader) {
|
||||
readers.remove(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<Property<?>, Counter<?>> properties = new HashMap<>();
|
||||
|
||||
public <T> Optional<T> read(Property<T> property) {
|
||||
Counter<T> counter = (Counter<T>) properties.get(property);
|
||||
if (counter == null) return Optional.empty();
|
||||
return counter.read();
|
||||
}
|
||||
|
||||
public <T> WriteResult write(Property<T> property, T value) {
|
||||
Counter<T> counter = (Counter<T>) properties.computeIfAbsent(property, $ -> new Counter<>(property));
|
||||
return counter.write(value);
|
||||
}
|
||||
|
||||
public Set<RewriteRule.Tracker<?>> unwrite(Property<?> property) {
|
||||
Counter<?> init = properties.get(property);
|
||||
if (init == null) return Set.of();
|
||||
|
||||
Set<Counter<?>> toVisit = new HashSet<>();
|
||||
toVisit.add(init);
|
||||
Set<Counter<?>> visited = new HashSet<>();
|
||||
|
||||
while (!toVisit.isEmpty()) {
|
||||
Set<Counter<?>> next = new HashSet<>();
|
||||
|
||||
for (Counter<?> c : toVisit) {
|
||||
if (c.isEmpty() || visited.contains(c)) continue;
|
||||
visited.add(c);
|
||||
|
||||
c.unwrite();
|
||||
if (c.canDrop()) {
|
||||
properties.put(c.key, null);
|
||||
}
|
||||
|
||||
c.readers.stream()
|
||||
.map(r -> properties.get(r.writes))
|
||||
.filter(Objects::nonNull)
|
||||
.forEachOrdered(toVisit::add);
|
||||
}
|
||||
|
||||
toVisit = next;
|
||||
}
|
||||
|
||||
return visited.stream().flatMap(c -> c.readers.stream()).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public int trackReader(RewriteRule.Tracker<?> reader) {
|
||||
int dependencies = 0;
|
||||
for (Property<?> p : reader.reads) {
|
||||
dependencies += properties.computeIfAbsent(p, $ -> new Counter<>(p)).trackReader(reader) ? 1 : 0;
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public void untrackReader(RewriteRule.Tracker<?> reader) {
|
||||
for (Property<?> property : reader.reads) {
|
||||
properties.get(property).untrackReader(reader);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RewriteRule<T> {
|
||||
private final Property.Type<T> writes;
|
||||
private final Set<Property.Relative<?>> reads;
|
||||
private final Descriptor<T> descriptor;
|
||||
|
||||
public interface Descriptor<T> {
|
||||
Property.Type<T> getWrittenProperty();
|
||||
|
||||
default Set<Property.Relative<?>> getReadProperties() { return Set.of(); }
|
||||
|
||||
Optional<T> getRewrittenValue(PropertyReader reader);
|
||||
}
|
||||
|
||||
public RewriteRule(Descriptor<T> descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
this.writes = descriptor.getWrittenProperty();
|
||||
this.reads = descriptor.getReadProperties();
|
||||
}
|
||||
|
||||
record PropertyReader(BlockPos pos, PropertyMap map) {
|
||||
public <U> U read(Property.Relative<U> property) {
|
||||
return map.read(property.toAbsolute(pos)).get();
|
||||
}
|
||||
}
|
||||
|
||||
private PropertyMap.WriteResult rewrite(BlockPos pos, PropertyMap properties) {
|
||||
return descriptor
|
||||
.getRewrittenValue(new PropertyReader(pos, properties))
|
||||
.map(v -> properties.write(new Property<>(pos, writes), v))
|
||||
.orElse(PropertyMap.WriteResult.Ok.V);
|
||||
}
|
||||
|
||||
public static class Tracker<T> {
|
||||
public final RewriteRule<T> rule;
|
||||
public final BlockPos pos;
|
||||
public final Property<T> writes;
|
||||
public final Set<Property<?>> reads;
|
||||
private int dependencies;
|
||||
|
||||
public Tracker(RewriteRule<T> rule, BlockPos pos, Function<Tracker<?>, Integer> dependencies) {
|
||||
this.rule = rule;
|
||||
this.pos = pos;
|
||||
this.writes = new Property<>(pos, rule.writes);
|
||||
this.reads = rule.reads.stream().map(p -> p.toAbsolute(pos)).collect(Collectors.toSet());
|
||||
this.dependencies = dependencies.apply(this);
|
||||
}
|
||||
|
||||
public PropertyMap.WriteResult rewrite(PropertyMap properties) {
|
||||
if (!canRewrite()) throw new IllegalStateException();
|
||||
return rule.rewrite(pos, properties);
|
||||
}
|
||||
|
||||
public void dependencyAdded() {
|
||||
dependencies += 1;
|
||||
}
|
||||
|
||||
public void dependencyRemoved() {
|
||||
if (dependencies == 0) throw new IllegalStateException();
|
||||
dependencies -= 1;
|
||||
}
|
||||
|
||||
public boolean canRewrite() {
|
||||
return dependencies == 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Vec3i;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class ShaftConnectionRule implements RewriteRule.Descriptor<Set<Vec3i>> {
|
||||
private final Set<Vec3i> connections;
|
||||
|
||||
public ShaftConnectionRule(Direction.Axis axis) {
|
||||
Direction positive = Direction.fromAxisAndDirection(axis, Direction.AxisDirection.POSITIVE);
|
||||
connections = Set.of(positive.getNormal(), positive.getOpposite().getNormal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property.Type<Set<Vec3i>> getWrittenProperty() {
|
||||
return AllPropertyTypes.SHAFT_CONNECTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Set<Vec3i>> getRewrittenValue(RewriteRule.PropertyReader reader) {
|
||||
return Optional.of(connections);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.simibubi.create.content.contraptions.solver;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Vec3i;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class ShaftEqualSpeedRule implements RewriteRule.Descriptor<Float> {
|
||||
private final Vec3i to, from;
|
||||
private final Property.Relative<Set<Vec3i>> otherConnections;
|
||||
private final Property.Relative<Float> otherSpeed;
|
||||
|
||||
public ShaftEqualSpeedRule(Direction dir) {
|
||||
to = dir.getNormal();
|
||||
from = dir.getOpposite().getNormal();
|
||||
otherConnections = new Property.Relative<>(to, AllPropertyTypes.SHAFT_CONNECTIONS);
|
||||
otherSpeed = new Property.Relative<>(to, AllPropertyTypes.SPEED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property.Type<Float> getWrittenProperty() {
|
||||
return AllPropertyTypes.SPEED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Property.Relative<?>> getReadProperties() {
|
||||
return Set.of(otherConnections, otherSpeed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Float> getRewrittenValue(RewriteRule.PropertyReader reader) {
|
||||
Set<Vec3i> otherConnections = reader.read(this.otherConnections);
|
||||
float otherSpeed = reader.read(this.otherSpeed);
|
||||
|
||||
if (otherConnections.contains(from))
|
||||
return Optional.of(otherSpeed);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue