Traffic Frights

- Fixed Trains ignoring their signal when nearby graph nodes are changed
- Fixed Trains not being notified of new signal blocks if they share an edge with the added/removed signal
- Fixed Navigation reserving chained signal blocks before verifying that it can be fully traversed
- Fixed Track placement requiring much shallower slopes for diagonal tracks
- Fixed long range navigation choosing detours near the beginning
- Temporarily patched and highlighted a problem with track traversal and long distances
This commit is contained in:
simibubi 2022-03-17 23:14:53 +01:00
parent a9a37b313c
commit 6dd3231b6d
9 changed files with 67 additions and 48 deletions

View file

@ -176,9 +176,9 @@ public class GlobalRailwayManager {
for (TrackGraph graph : trackNetworks.values())
graph.tickPoints(false);
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_K))
// trackNetworks.values()
// .forEach(TrackGraph::debugViewReserved);
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_K))
trackNetworks.values()
.forEach(TrackGraph::debugViewReserved);
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && AllKeys.altDown())
// trackNetworks.values()
// .forEach(TrackGraph::debugViewNodes);

View file

@ -44,8 +44,9 @@ public class TrackEdge {
}
public double incrementT(TrackNode node1, TrackNode node2, double currentT, double distance) {
boolean tooFar = Math.abs(distance) > 5;
distance = distance / getLength(node1, node2);
return isTurn() ? turn.incrementT(currentT, distance) : currentT + distance;
return !tooFar && isTurn() ? turn.incrementT(currentT, distance) : currentT + distance;
}
public Vec3 getPosition(TrackNode node1, TrackNode node2, double t) {

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.logistics.trains.entity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
@ -39,6 +40,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
@ -53,7 +55,7 @@ public class Navigation {
private TravellingPoint signalScout;
public Pair<UUID, Boolean> waitingForSignal;
private Set<UUID> waitingForChainedGroups;
private Map<UUID, SignalBoundary> waitingForChainedGroups;
public double distanceToSignal;
public int ticksWaitingForSignal;
@ -61,7 +63,7 @@ public class Navigation {
this.train = train;
currentPath = new ArrayList<>();
signalScout = new TravellingPoint();
waitingForChainedGroups = new HashSet<>();
waitingForChainedGroups = new HashMap<>();
}
public void tick(Level level) {
@ -101,11 +103,11 @@ public class Navigation {
// Signals
if (train.graph != null) {
if (waitingForSignal != null && currentSignalResolved()) {
SignalBoundary signal = train.graph.getPoint(EdgePointType.SIGNAL, waitingForSignal.getFirst());
if (signal.types.get(waitingForSignal.getSecond()) == SignalType.CROSS_SIGNAL)
train.reservedSignalBlocks.addAll(waitingForChainedGroups);
UUID signalId = waitingForSignal.getFirst();
SignalBoundary signal = train.graph.getPoint(EdgePointType.SIGNAL, signalId);
if (signal != null && signal.types.get(waitingForSignal.getSecond()) == SignalType.CROSS_SIGNAL)
waitingForChainedGroups.clear();
waitingForSignal = null;
waitingForChainedGroups.clear();
}
TravellingPoint leadingPoint = !destinationBehindTrain ? train.carriages.get(0)
@ -126,16 +128,15 @@ public class Navigation {
signalScout.edge = leadingPoint.edge;
signalScout.position = leadingPoint.position;
double brakingDistanceNoFlicker =
Math.max(preDepartureLookAhead, brakingDistance + 3 - (brakingDistance % 3));
double scanDistance = Math.min(distanceToDestination, brakingDistanceNoFlicker);
double brakingDistanceNoFlicker = brakingDistance + 3 - (brakingDistance % 3);
double scanDistance = Mth.clamp(brakingDistanceNoFlicker, preDepartureLookAhead, distanceToDestination);
MutableDouble crossSignalDistanceTracker = new MutableDouble(-1);
MutableObject<Pair<UUID, Boolean>> trackingCrossSignal = new MutableObject<>(null);
waitingForChainedGroups.clear();
// train.reservedSignalBlocks.clear();
signalScout.travel(train.graph, distanceToDestination * speedMod, controlSignalScout(),
// Adding 50 to the distance due to unresolved inaccuracies in TravellingPoint::travel
signalScout.travel(train.graph, (distanceToDestination + 50) * speedMod, controlSignalScout(),
(distance, couple) -> {
// > scanDistance and not following down a cross signal
boolean crossSignalTracked = trackingCrossSignal.getValue() != null;
@ -158,15 +159,11 @@ public class Navigation {
boolean crossSignal = signal.types.get(primary) == SignalType.CROSS_SIGNAL;
boolean occupied = signalEdgeGroup.isOccupiedUnless(train);
if (!occupied && distance < distanceToSignal + .25)
signalEdgeGroup.reserved = signal; // Reserve group for traversal, unless waiting at an
// earlier cross signal
if (!crossSignalTracked) {
if (crossSignal) { // Now entering cross signal path
trackingCrossSignal.setValue(Pair.of(boundary.id, primary));
crossSignalDistanceTracker.setValue(distance);
waitingForChainedGroups.add(entering);
waitingForChainedGroups.put(entering, signal);
}
if (occupied) { // Section is occupied
waitingForSignal = Pair.of(boundary.id, primary);
@ -174,8 +171,14 @@ public class Navigation {
if (!crossSignal)
return true; // Standard entry signal, do not collect any further segments
}
} else {
waitingForChainedGroups.add(entering); // Add group to chain
if (!occupied && !crossSignal && distance < distanceToSignal + .25
&& distance < brakingDistanceNoFlicker)
signalEdgeGroup.reserved = signal; // Reserve group for traversal
return false;
}
if (crossSignalTracked) {
waitingForChainedGroups.put(entering, signal); // Add group to chain
if (occupied) { // Section is occupied, but wait at the cross signal that started the chain
waitingForSignal = trackingCrossSignal.getValue();
distanceToSignal = crossSignalDistanceTracker.doubleValue();
@ -186,8 +189,7 @@ public class Navigation {
if (distance < distanceToSignal + .25) {
// Collect and reset the signal chain because none were blocked
trackingCrossSignal.setValue(null);
train.reservedSignalBlocks.addAll(waitingForChainedGroups);
waitingForChainedGroups.clear();
reserveChain();
return false;
} else
return true; // End of a blocked signal chain
@ -202,20 +204,13 @@ public class Navigation {
curveDistanceTracker.setValue(distance);
});
if (trackingCrossSignal.getValue() != null) {
if (waitingForSignal == null) {
train.reservedSignalBlocks.addAll(waitingForChainedGroups);
waitingForChainedGroups.clear();
}
}
if (trackingCrossSignal.getValue() != null && waitingForSignal == null)
reserveChain();
distanceToNextCurve = curveDistanceTracker.floatValue();
} else {
} else
ticksWaitingForSignal++;
// if chain signal try finding new path/destination every x ticks
}
}
double targetDistance = waitingForSignal != null ? distanceToSignal : distanceToDestination;
@ -265,6 +260,16 @@ public class Navigation {
train.approachTargetSpeed(1);
}
private void reserveChain() {
train.reservedSignalBlocks.addAll(waitingForChainedGroups.keySet());
waitingForChainedGroups.forEach((groupId, boundary) -> {
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(groupId);
if (signalEdgeGroup != null)
signalEdgeGroup.reserved = boundary;
});
waitingForChainedGroups.clear();
}
private boolean currentSignalResolved() {
if (distanceToDestination < .5f)
return true;
@ -274,10 +279,12 @@ public class Navigation {
// Cross Signal
if (signal.types.get(waitingForSignal.getSecond()) == SignalType.CROSS_SIGNAL) {
for (UUID groupId : waitingForChainedGroups) {
for (UUID groupId : waitingForChainedGroups.keySet()) {
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(groupId);
if (signalEdgeGroup == null)
continue;
if (signalEdgeGroup == null) { // Migration, re-initialize chain
waitingForSignal.setFirst(null);
return true;
}
if (signalEdgeGroup.isOccupiedUnless(train))
return false;
}
@ -629,7 +636,7 @@ public class Navigation {
TrackEdge newEdge = target.getValue();
double newDistance = newEdge.getLength(node2, newNode) + distance;
int newPenalty = penalty;
reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2)));
reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2)));
frontier.add(new FrontierEntry(newDistance, newPenalty, node2, newNode, newEdge));
}
}

View file

@ -123,16 +123,17 @@ public class Train {
status.tick(level);
if (graph == null && !migratingPoints.isEmpty())
reattachToTracks(level);
addToSignalGroups(occupiedSignalBlocks.keySet());
if (graph == null)
if (graph == null) {
addToSignalGroups(occupiedSignalBlocks.keySet());
return;
}
if (updateSignalBlocks) {
updateSignalBlocks = false;
collectInitiallyOccupiedSignalBlocks();
}
addToSignalGroups(occupiedSignalBlocks.keySet());
addToSignalGroups(reservedSignalBlocks);
}

View file

@ -187,6 +187,15 @@ public class TravellingPoint {
double currentT = position / edgeLength;
double incrementT = edge.incrementT(node1, node2, currentT, distance);
position = incrementT * edgeLength;
// FIXME: using incrementT like this becomes inaccurate at medium-long distances
// travelling points would travel only 50m instead of 100m due to the low
// incrementT at their starting position (e.g. bezier turn)
// In an ideal scenario the amount added to position would iterate the traversed
// edges for context first
// A workaround was added in TrackEdge::incrementT
List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>();
boolean forward = distance > 0;

View file

@ -42,7 +42,7 @@ public class SignalBoundary extends TrackEdgePoint {
groups = Couple.create(null, null);
sidesToUpdate = Couple.create(true, true);
types = Couple.create(() -> SignalType.ENTRY_SIGNAL);
cachedStates = Couple.create(() -> SignalState.GREEN);
cachedStates = Couple.create(() -> SignalState.INVALID);
}
public void setGroup(TrackNode side, UUID groupId) {

View file

@ -115,15 +115,16 @@ public class SignalPropagator {
if (startEdge == null)
return;
if (!forCollection)
if (!forCollection) {
notifyTrains(graph, startEdge, oppositeEdge);
Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge, oppositeEdge);
}
// Check for signal on the same edge
SignalBoundary immediateBoundary = startEdge.getEdgeData()
.next(EdgePointType.SIGNAL, node1, node2, startEdge, signal.getLocationOn(node1, node2, startEdge));
if (immediateBoundary != null) {
if (boundaryCallback.test(Pair.of(node1, immediateBoundary)))
notifyTrains(graph, startEdge, oppositeEdge);
boundaryCallback.test(Pair.of(node1, immediateBoundary));
return;
}

View file

@ -34,7 +34,7 @@ public class SignalRenderer extends SafeTileEntityRenderer<SignalTileEntity> {
if (signalState.isRedLight(renderTime))
CachedBufferer.partial(AllBlockPartials.SIGNAL_ON, blockState)
.renderInto(ms, buffer.getBuffer(RenderType.solid()));
else if (signalState.isGreenLight(renderTime))
else
CachedBufferer.partial(AllBlockPartials.SIGNAL_OFF, blockState)
.renderInto(ms, buffer.getBuffer(RenderType.solid()));

View file

@ -247,7 +247,7 @@ public class TrackPlacement {
int hDistance = info.end1Extent;
if (axis1.y == 0 || !Mth.equal(absAscend + 1, dist / axis1.length())) {
info.end1Extent = 0;
double minHDistance = Math.max(absAscend < 4 ? absAscend * 4 : absAscend * 3, 6);
double minHDistance = Math.max(absAscend < 4 ? absAscend * 4 : absAscend * 3, 6) / axis1.length();
if (hDistance < minHDistance)
return info.withMessage("too_steep");
if (hDistance > minHDistance) {