From 67ebc8a2c6427ba59a5f15f52359b2ba328b010a Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Tue, 24 Oct 2023 19:12:15 +0200 Subject: [PATCH 01/16] Rewrite to remove unneeded pathfind call --- .../entity/CarriageContraptionEntity.java | 2 +- .../content/trains/entity/Navigation.java | 39 +++++++------------ .../create/content/trains/entity/Train.java | 10 +++-- .../content/trains/graph/DiscoveredPath.java | 22 +++++++++++ .../trains/schedule/ScheduleRuntime.java | 37 +++++++++++------- .../trains/station/StationBlockEntity.java | 8 ++-- 6 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java diff --git a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java index eeb5d679e..dc750775d 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java @@ -640,7 +640,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { if (lookAhead != null) { if (spaceDown) { carriage.train.manualTick = true; - nav.startNavigation(lookAhead, -1, false); + nav.startNavigation(nav.findPathTo(lookAhead, -1)); carriage.train.manualTick = false; navDistanceTotal = nav.distanceToDestination; return true; diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index b8fdd98d4..31253d612 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -15,6 +15,9 @@ import java.util.UUID; import javax.annotation.Nullable; +import com.mojang.logging.LogUtils; +import com.simibubi.create.content.trains.graph.DiscoveredPath; + import org.apache.commons.lang3.mutable.MutableDouble; import org.apache.commons.lang3.mutable.MutableObject; @@ -380,15 +383,11 @@ public class Navigation { train.reservedSignalBlocks.clear(); } - public double startNavigation(GlobalStation destination, double maxCost, boolean simulate) { - DiscoveredPath pathTo = findPathTo(destination, maxCost); + public double startNavigation(DiscoveredPath pathTo) { boolean noneFound = pathTo == null; double distance = noneFound ? -1 : Math.abs(pathTo.distance); double cost = noneFound ? -1 : pathTo.cost; - if (simulate) - return cost; - distanceToDestination = distance; if (noneFound) { @@ -407,10 +406,10 @@ public class Navigation { train.reservedSignalBlocks.clear(); train.navigation.waitingForSignal = null; - if (this.destination == null && !simulate) + if (this.destination == null) distanceStartedAt = distance; - if (this.destination == destination) + if (this.destination == pathTo.destination) return 0; if (!train.runtime.paused) { @@ -435,16 +434,16 @@ public class Navigation { train.status.foundConductor(); } - this.destination = destination; + this.destination = pathTo.destination; return cost; } @Nullable - private DiscoveredPath findPathTo(GlobalStation destination, double maxCost) { + public DiscoveredPath findPathTo(GlobalStation destination, double maxCost) { TrackGraph graph = train.graph; if (graph == null) return null; - + LogUtils.getLogger().info("finding path"); Couple results = Couple.create(null, null); for (boolean forward : Iterate.trueAndFalse) { @@ -486,7 +485,7 @@ public class Navigation { double position = edge.getLength() - destination.getLocationOn(edge); double distanceToDestination = distance - position; - results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath)); + results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath, destination)); return true; }); } @@ -508,18 +507,6 @@ public class Navigation { return frontBetter ? front : back; } - public class DiscoveredPath { - List> path; - double distance; - double cost; - - public DiscoveredPath(double distance, double cost, List> path) { - this.distance = distance; - this.cost = cost; - this.path = path; - } - } - public GlobalStation findNearestApproachable(boolean forward) { TrackGraph graph = train.graph; if (graph == null) @@ -776,9 +763,9 @@ public class Navigation { c -> currentPath.add(Couple .deserializeEach(c.getList("Nodes", Tag.TAG_COMPOUND), c2 -> TrackNodeLocation.read(c2, dimensions)) .map(graph::locateNode))); - + removeBrokenPathEntries(); - + waitingForSignal = tag.contains("BlockingSignal") ? Pair.of(tag.getUUID("BlockingSignal"), tag.getBoolean("BlockingSignalSide")) : null; @@ -793,7 +780,7 @@ public class Navigation { * Trains might load or save with null entries in their path, this method avoids * that anomaly from causing NPEs. The underlying issue has not been found. */ - + boolean nullEntriesPresent = false; for (Iterator> iterator = currentPath.iterator(); iterator.hasNext();) { diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Train.java b/src/main/java/com/simibubi/create/content/trains/entity/Train.java index 612c8fd7c..48acf59e2 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Train.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Train.java @@ -17,6 +17,8 @@ import java.util.function.Consumer; import javax.annotation.Nullable; +import com.simibubi.create.content.trains.graph.DiscoveredPath; + import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableObject; @@ -541,12 +543,12 @@ public class Train { GlobalStation destination = navigation.destination; if (!navigatingManually && fullRefresh) { - GlobalStation preferredDestination = runtime.startCurrentInstruction(); - if (preferredDestination != null) - destination = preferredDestination; + DiscoveredPath preferredPath = runtime.startCurrentInstruction(); + if (preferredPath != null) + destination = preferredPath.destination; } - navigation.startNavigation(destination, navigatingManually ? -1 : Double.MAX_VALUE, false); + navigation.startNavigation(navigation.findPathTo(destination, navigatingManually ? -1 : Double.MAX_VALUE)); } private void tickDerailedSlowdown() { diff --git a/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java b/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java new file mode 100644 index 000000000..9b2be0aaa --- /dev/null +++ b/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java @@ -0,0 +1,22 @@ +package com.simibubi.create.content.trains.graph; + +import com.simibubi.create.content.trains.station.GlobalStation; +import com.simibubi.create.foundation.utility.Couple; + +import org.openjdk.nashorn.internal.objects.Global; + +import java.util.List; + +public class DiscoveredPath { + public List> path; + public GlobalStation destination; + public double distance; + public double cost; + + public DiscoveredPath(double distance, double cost, List> path, GlobalStation destination) { + this.distance = distance; + this.cost = cost; + this.path = path; + this.destination = destination; + } +} diff --git a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java index 5147edf96..c78056d7a 100644 --- a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java @@ -8,7 +8,9 @@ import java.util.Objects; import com.simibubi.create.AllItems; import com.simibubi.create.content.trains.display.GlobalTrainDisplayData.TrainDeparturePrediction; import com.simibubi.create.content.trains.entity.Carriage; +import com.simibubi.create.content.trains.entity.Navigation; import com.simibubi.create.content.trains.entity.Train; +import com.simibubi.create.content.trains.graph.DiscoveredPath; import com.simibubi.create.content.trains.graph.EdgePointType; import com.simibubi.create.content.trains.schedule.condition.ScheduleWaitCondition; import com.simibubi.create.content.trains.schedule.condition.ScheduledDelay; @@ -122,17 +124,17 @@ public class ScheduleRuntime { return; } - GlobalStation nextStation = startCurrentInstruction(); - if (nextStation == null) + DiscoveredPath nextPath = startCurrentInstruction(); + if (nextPath == null) return; train.status.successfulNavigation(); - if (nextStation == train.getCurrentStation()) { + if (nextPath.destination == train.getCurrentStation()) { state = State.IN_TRANSIT; destinationReached(); return; } - if (train.navigation.startNavigation(nextStation, Double.MAX_VALUE, false) != TBD) { + if (train.navigation.startNavigation(nextPath) != TBD) { state = State.IN_TRANSIT; ticksInTransit = 0; } @@ -167,13 +169,13 @@ public class ScheduleRuntime { carriage.storage.tickIdleCargoTracker(); } - public GlobalStation startCurrentInstruction() { + public DiscoveredPath startCurrentInstruction() { ScheduleEntry entry = schedule.entries.get(currentEntry); ScheduleInstruction instruction = entry.instruction; if (instruction instanceof DestinationInstruction destination) { String regex = destination.getFilterForRegex(); - GlobalStation best = null; + DiscoveredPath best = null; double bestCost = Double.MAX_VALUE; boolean anyMatch = false; @@ -188,12 +190,19 @@ public class ScheduleRuntime { continue; anyMatch = true; boolean matchesCurrent = train.currentStation != null && train.currentStation.equals(globalStation.id); - double cost = matchesCurrent ? 0 : train.navigation.startNavigation(globalStation, bestCost, true); + double cost; + DiscoveredPath path = train.navigation.findPathTo(globalStation, bestCost); + if (matchesCurrent) { + cost = 0; + } else { + cost = path == null ? -1 : path.cost; + } + if (cost < 0) continue; if (cost > bestCost) continue; - best = globalStation; + best = path; bestCost = cost; } @@ -332,9 +341,9 @@ public class ScheduleRuntime { return accumulatedTime; if (predictionTicks.size() <= currentEntry) return accumulatedTime; - + int departureTime = estimateStayDuration(index); - + if (accumulatedTime < 0) { predictions.add(createPrediction(index, filter.getFilter(), currentTitle, accumulatedTime)); return Math.min(accumulatedTime, departureTime); @@ -348,12 +357,12 @@ public class ScheduleRuntime { predictions.add(createPrediction(index, filter.getFilter(), currentTitle, accumulatedTime)); - if (accumulatedTime != TBD) + if (accumulatedTime != TBD) accumulatedTime += departureTime; - + if (departureTime == INVALID) accumulatedTime = INVALID; - + return accumulatedTime; } @@ -381,7 +390,7 @@ public class ScheduleRuntime { private TrainDeparturePrediction createPrediction(int index, String destination, String currentTitle, int time) { if (time == INVALID) return null; - + int size = schedule.entries.size(); if (index >= size) { if (!schedule.cyclic) diff --git a/src/main/java/com/simibubi/create/content/trains/station/StationBlockEntity.java b/src/main/java/com/simibubi/create/content/trains/station/StationBlockEntity.java index 44657b81f..596873cd1 100644 --- a/src/main/java/com/simibubi/create/content/trains/station/StationBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/trains/station/StationBlockEntity.java @@ -13,6 +13,8 @@ import java.util.function.Consumer; import javax.annotation.Nullable; +import com.simibubi.create.content.trains.graph.DiscoveredPath; + import org.jetbrains.annotations.NotNull; import com.simibubi.create.AllBlocks; @@ -286,7 +288,7 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab BlockPos up = new BlockPos(track.getUpNormal(level, pos, state)); BlockPos down = new BlockPos(track.getUpNormal(level, pos, state).scale(-1)); int bogeyOffset = pos.distManhattan(edgePoint.getGlobalPosition()) - 1; - + if (!isValidBogeyOffset(bogeyOffset)) { for (boolean upsideDown : Iterate.falseAndTrue) { for (int i = -1; i <= 1; i++) { @@ -374,8 +376,8 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab if (train.navigation.destination != station) continue; - GlobalStation preferredDestination = train.runtime.startCurrentInstruction(); - train.navigation.startNavigation(preferredDestination != null ? preferredDestination : station, Double.MAX_VALUE, false); + DiscoveredPath preferredPath = train.runtime.startCurrentInstruction(); + train.navigation.startNavigation(preferredPath != null ? preferredPath : train.navigation.findPathTo(station, Double.MAX_VALUE)); } } From 57fbe3cb483d995637410f837284a63556e4c24a Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Tue, 24 Oct 2023 22:04:29 +0200 Subject: [PATCH 02/16] Optimize navigation refresh --- .../simibubi/create/content/trains/entity/Navigation.java | 2 +- .../com/simibubi/create/content/trains/entity/Train.java | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index 31253d612..4638b308f 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -443,7 +443,7 @@ public class Navigation { TrackGraph graph = train.graph; if (graph == null) return null; - LogUtils.getLogger().info("finding path"); + Couple results = Couple.create(null, null); for (boolean forward : Iterate.trueAndFalse) { diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Train.java b/src/main/java/com/simibubi/create/content/trains/entity/Train.java index 48acf59e2..8a760bf25 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Train.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Train.java @@ -541,14 +541,12 @@ public class Train { if (!reservedSignalBlocks.isEmpty()) return; - GlobalStation destination = navigation.destination; if (!navigatingManually && fullRefresh) { DiscoveredPath preferredPath = runtime.startCurrentInstruction(); - if (preferredPath != null) - destination = preferredPath.destination; + if (preferredPath != null){ + navigation.startNavigation(preferredPath); + } } - - navigation.startNavigation(navigation.findPathTo(destination, navigatingManually ? -1 : Double.MAX_VALUE)); } private void tickDerailedSlowdown() { From 29358d2dee18386a8b8deae68aedac10588997a8 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Wed, 25 Oct 2023 21:07:42 +0200 Subject: [PATCH 03/16] A* priority heuristic --- .../content/trains/entity/Navigation.java | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index 4638b308f..8e7cb7c1b 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -18,6 +18,8 @@ import javax.annotation.Nullable; import com.mojang.logging.LogUtils; import com.simibubi.create.content.trains.graph.DiscoveredPath; +import com.simibubi.create.foundation.utility.VecHelper; + import org.apache.commons.lang3.mutable.MutableDouble; import org.apache.commons.lang3.mutable.MutableObject; @@ -443,7 +445,7 @@ public class Navigation { TrackGraph graph = train.graph; if (graph == null) return null; - + long startTime = System.nanoTime(); Couple results = Couple.create(null, null); for (boolean forward : Iterate.trueAndFalse) { @@ -459,7 +461,7 @@ public class Navigation { : graph.getConnectionsFrom(initialPoint.node2) .get(initialPoint.node1); - search(Double.MAX_VALUE, maxCost, forward, (distance, cost, reachedVia, currentEntry, globalStation) -> { + search(Double.MAX_VALUE, maxCost, forward, new Vec3(destination.getBlockEntityPos().getX(), destination.getBlockEntityPos().getY(), destination.getBlockEntityPos().getZ()), (distance, cost, reachedVia, currentEntry, globalStation) -> { if (globalStation != destination) return false; @@ -498,6 +500,10 @@ public class Navigation { boolean canDriveForward = train.hasForwardConductor() || train.runtime.paused; boolean canDriveBackward = train.doubleEnded && train.hasBackwardConductor() || train.runtime.paused; + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + LogUtils.getLogger().info("Time taken: " + duration + " nanoseconds"); + if (backEmpty || !canDriveBackward) return canDriveForward ? front : null; if (frontEmpty || !canDriveForward) @@ -517,7 +523,7 @@ public class Navigation { double minDistance = .75f * (train.speed * train.speed) / (2 * acceleration); double maxDistance = Math.max(32, 1.5f * (train.speed * train.speed) / (2 * acceleration)); - search(maxDistance, forward, (distance, cost, reachedVia, currentEntry, globalStation) -> { + search(maxDistance, forward, null, (distance, cost, reachedVia, currentEntry, globalStation) -> { if (distance < minDistance) return false; @@ -535,15 +541,15 @@ public class Navigation { return result.getValue(); } - public void search(double maxDistance, boolean forward, StationTest stationTest) { - search(maxDistance, -1, forward, stationTest); + public void search(double maxDistance, boolean forward, Vec3 destinationPos, StationTest stationTest) { + search(maxDistance, -1, forward, destinationPos, stationTest); } - public void search(double maxDistance, double maxCost, boolean forward, StationTest stationTest) { + public void search(double maxDistance, double maxCost, boolean forward, Vec3 destinationPos, StationTest stationTest) { TrackGraph graph = train.graph; if (graph == null) return; - + if(destinationPos != null) LogUtils.getLogger().info(destinationPos.toString()); // Cache the list of track types that the train can travel on Set validTypes = new HashSet<>(); for (int i = 0; i < train.carriages.size(); i++) { @@ -601,9 +607,9 @@ public class Navigation { double distanceToNode2 = forward ? initialEdge.getLength() - startingPoint.position : startingPoint.position; - frontier.add(new FrontierEntry(distanceToNode2, 0, initialNode1, initialNode2, initialEdge)); + frontier.add(new FrontierEntry(distanceToNode2, 0, 0, initialNode1, initialNode2, initialEdge)); int signalWeight = Mth.clamp(ticksWaitingForSignal * 2, Train.Penalties.RED_SIGNAL, 200); - + int total = 0; Search: while (!frontier.isEmpty()) { FrontierEntry entry = frontier.poll(); if (!visited.add(entry.edge)) @@ -614,7 +620,7 @@ public class Navigation { if (distance > maxDistance) continue; - + total++; TrackEdge edge = entry.edge; TrackNode node1 = entry.node1; TrackNode node2 = entry.node2; @@ -653,8 +659,11 @@ public class Navigation { if (presentTrain != null && !isOwnStation) penalty += Train.Penalties.STATION_WITH_TRAIN; if (station.canApproachFrom(node2) && stationTest.test(distance, distance + penalty, reachedVia, - Pair.of(Couple.create(node1, node2), edge), station)) + Pair.of(Couple.create(node1, node2), edge), station)){ + // remove brackets after removing this + LogUtils.getLogger().info("Nodes considered: " + total); return; + } if (!isOwnStation) penalty += Train.Penalties.STATION; } @@ -683,9 +692,11 @@ public class Navigation { TrackNode newNode = target.getKey(); TrackEdge newEdge = target.getValue(); double newDistance = newEdge.getLength() + distance; + double straightDist = destinationPos == null ? 0 : newNode.getLocation().getLocation().distanceTo(destinationPos); + LogUtils.getLogger().info("straight: " + straightDist + " | dist: " + newDistance + " | new: " + newNode.getLocation().getLocation().toString() + " | " + (straightDist + newDistance)); int newPenalty = penalty; reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); - frontier.add(new FrontierEntry(newDistance, newPenalty, node2, newNode, newEdge)); + frontier.add(new FrontierEntry(newDistance, newPenalty, straightDist, node2, newNode, newEdge)); } } } @@ -694,13 +705,15 @@ public class Navigation { double distance; int penalty; + double straight; TrackNode node1; TrackNode node2; TrackEdge edge; - public FrontierEntry(double distance, int penalty, TrackNode node1, TrackNode node2, TrackEdge edge) { + public FrontierEntry(double distance, int penalty, double straight, TrackNode node1, TrackNode node2, TrackEdge edge) { this.distance = distance; this.penalty = penalty; + this.straight = straight; this.node1 = node1; this.node2 = node2; this.edge = edge; @@ -708,7 +721,7 @@ public class Navigation { @Override public int compareTo(FrontierEntry o) { - return Double.compare(distance + penalty, o.distance + o.penalty); + return Double.compare(distance + penalty + straight, o.distance + o.penalty + o.straight); } } From bb11cd7b59f22cb76f42c9eb63df09f674829fb9 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Thu, 26 Oct 2023 01:21:48 +0200 Subject: [PATCH 04/16] Fix distance estimator --- .../content/trains/entity/Navigation.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index 8e7cb7c1b..825f6b88d 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -461,7 +461,7 @@ public class Navigation { : graph.getConnectionsFrom(initialPoint.node2) .get(initialPoint.node1); - search(Double.MAX_VALUE, maxCost, forward, new Vec3(destination.getBlockEntityPos().getX(), destination.getBlockEntityPos().getY(), destination.getBlockEntityPos().getZ()), (distance, cost, reachedVia, currentEntry, globalStation) -> { + search(Double.MAX_VALUE, maxCost, forward, destination, (distance, cost, reachedVia, currentEntry, globalStation) -> { if (globalStation != destination) return false; @@ -502,7 +502,6 @@ public class Navigation { long endTime = System.nanoTime(); long duration = (endTime - startTime); - LogUtils.getLogger().info("Time taken: " + duration + " nanoseconds"); if (backEmpty || !canDriveBackward) return canDriveForward ? front : null; @@ -541,15 +540,15 @@ public class Navigation { return result.getValue(); } - public void search(double maxDistance, boolean forward, Vec3 destinationPos, StationTest stationTest) { - search(maxDistance, -1, forward, destinationPos, stationTest); + public void search(double maxDistance, boolean forward, GlobalStation destination, StationTest stationTest) { + search(maxDistance, -1, forward, destination, stationTest); } - public void search(double maxDistance, double maxCost, boolean forward, Vec3 destinationPos, StationTest stationTest) { + public void search(double maxDistance, double maxCost, boolean forward, GlobalStation destination, StationTest stationTest) { TrackGraph graph = train.graph; if (graph == null) return; - if(destinationPos != null) LogUtils.getLogger().info(destinationPos.toString()); + // Cache the list of track types that the train can travel on Set validTypes = new HashSet<>(); for (int i = 0; i < train.carriages.size(); i++) { @@ -624,7 +623,7 @@ public class Navigation { TrackEdge edge = entry.edge; TrackNode node1 = entry.node1; TrackNode node2 = entry.node2; - + //LogUtils.getLogger().info("straight: " + entry.straight + " | dist: " + distance + " | old: " + node1.getLocation().getLocation().toString() + " | new: " + node2.getLocation().getLocation().toString() + " | " + (entry.straight + entry.distance)); if (costRelevant) penalty += penalties.getOrDefault(edge, 0); @@ -659,11 +658,8 @@ public class Navigation { if (presentTrain != null && !isOwnStation) penalty += Train.Penalties.STATION_WITH_TRAIN; if (station.canApproachFrom(node2) && stationTest.test(distance, distance + penalty, reachedVia, - Pair.of(Couple.create(node1, node2), edge), station)){ - // remove brackets after removing this - LogUtils.getLogger().info("Nodes considered: " + total); + Pair.of(Couple.create(node1, node2), edge), station)) return; - } if (!isOwnStation) penalty += Train.Penalties.STATION; } @@ -692,10 +688,14 @@ public class Navigation { TrackNode newNode = target.getKey(); TrackEdge newEdge = target.getValue(); double newDistance = newEdge.getLength() + distance; - double straightDist = destinationPos == null ? 0 : newNode.getLocation().getLocation().distanceTo(destinationPos); - LogUtils.getLogger().info("straight: " + straightDist + " | dist: " + newDistance + " | new: " + newNode.getLocation().getLocation().toString() + " | " + (straightDist + newDistance)); + double straightDist = destination == null ? 0 : newNode.getLocation().getLocation().distanceTo(destination.edgeLocation.getSecond().getLocation()); int newPenalty = penalty; reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); + if (destination != null && straightDist == 0.0 && stationTest.test(newDistance, newDistance + newPenalty, reachedVia, + Pair.of(Couple.create(node2, newNode), newEdge), destination)){ + LogUtils.getLogger().info("Node term: " + total); + return; + } frontier.add(new FrontierEntry(newDistance, newPenalty, straightDist, node2, newNode, newEdge)); } } From b0d1a9e3bbfc95e2a0bf0ec155b8fe208c20e81b Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Thu, 26 Oct 2023 01:30:58 +0200 Subject: [PATCH 05/16] cleanup --- .../content/trains/entity/Navigation.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index 825f6b88d..27d65ee74 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -18,8 +18,6 @@ import javax.annotation.Nullable; import com.mojang.logging.LogUtils; import com.simibubi.create.content.trains.graph.DiscoveredPath; -import com.simibubi.create.foundation.utility.VecHelper; - import org.apache.commons.lang3.mutable.MutableDouble; import org.apache.commons.lang3.mutable.MutableObject; @@ -445,7 +443,7 @@ public class Navigation { TrackGraph graph = train.graph; if (graph == null) return null; - long startTime = System.nanoTime(); + Couple results = Couple.create(null, null); for (boolean forward : Iterate.trueAndFalse) { @@ -500,9 +498,6 @@ public class Navigation { boolean canDriveForward = train.hasForwardConductor() || train.runtime.paused; boolean canDriveBackward = train.doubleEnded && train.hasBackwardConductor() || train.runtime.paused; - long endTime = System.nanoTime(); - long duration = (endTime - startTime); - if (backEmpty || !canDriveBackward) return canDriveForward ? front : null; if (frontEmpty || !canDriveForward) @@ -688,15 +683,15 @@ public class Navigation { TrackNode newNode = target.getKey(); TrackEdge newEdge = target.getValue(); double newDistance = newEdge.getLength() + distance; - double straightDist = destination == null ? 0 : newNode.getLocation().getLocation().distanceTo(destination.edgeLocation.getSecond().getLocation()); - int newPenalty = penalty; + double remainingDist = destination == null ? 0 : newNode.getLocation().getLocation().distanceTo(destination.edgeLocation.getSecond().getLocation()); + reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); - if (destination != null && straightDist == 0.0 && stationTest.test(newDistance, newDistance + newPenalty, reachedVia, + if (destination != null && remainingDist == 0.0 && stationTest.test(newDistance, newDistance + penalty, reachedVia, Pair.of(Couple.create(node2, newNode), newEdge), destination)){ LogUtils.getLogger().info("Node term: " + total); return; } - frontier.add(new FrontierEntry(newDistance, newPenalty, straightDist, node2, newNode, newEdge)); + frontier.add(new FrontierEntry(newDistance, penalty, remainingDist, node2, newNode, newEdge)); } } } @@ -705,15 +700,15 @@ public class Navigation { double distance; int penalty; - double straight; + double remaining; TrackNode node1; TrackNode node2; TrackEdge edge; - public FrontierEntry(double distance, int penalty, double straight, TrackNode node1, TrackNode node2, TrackEdge edge) { + public FrontierEntry(double distance, int penalty, double remaining, TrackNode node1, TrackNode node2, TrackEdge edge) { this.distance = distance; this.penalty = penalty; - this.straight = straight; + this.remaining = remaining; this.node1 = node1; this.node2 = node2; this.edge = edge; @@ -721,7 +716,7 @@ public class Navigation { @Override public int compareTo(FrontierEntry o) { - return Double.compare(distance + penalty + straight, o.distance + o.penalty + o.straight); + return Double.compare(distance + penalty + remaining, o.distance + o.penalty + o.remaining); } } From 25978126453be4514411c334d105fcd2b7211cc1 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Thu, 26 Oct 2023 23:53:49 +0200 Subject: [PATCH 06/16] Keep old FrontierEntry constructor --- .../create/content/trains/entity/Navigation.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index 27d65ee74..5b88f4e20 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -601,7 +601,7 @@ public class Navigation { double distanceToNode2 = forward ? initialEdge.getLength() - startingPoint.position : startingPoint.position; - frontier.add(new FrontierEntry(distanceToNode2, 0, 0, initialNode1, initialNode2, initialEdge)); + frontier.add(new FrontierEntry(distanceToNode2, 0, initialNode1, initialNode2, initialEdge)); int signalWeight = Mth.clamp(ticksWaitingForSignal * 2, Train.Penalties.RED_SIGNAL, 200); int total = 0; Search: while (!frontier.isEmpty()) { @@ -705,6 +705,14 @@ public class Navigation { TrackNode node2; TrackEdge edge; + public FrontierEntry(double distance, int penalty, TrackNode node1, TrackNode node2, TrackEdge edge) { + this.distance = distance; + this.penalty = penalty; + this.remaining = 0; + this.node1 = node1; + this.node2 = node2; + this.edge = edge; + } public FrontierEntry(double distance, int penalty, double remaining, TrackNode node1, TrackNode node2, TrackEdge edge) { this.distance = distance; this.penalty = penalty; From aa03346fa5903117b64ccd07b1963f697bbc8d6a Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Fri, 27 Oct 2023 00:11:38 +0200 Subject: [PATCH 07/16] Remove comments --- .../simibubi/create/content/trains/entity/Navigation.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index 5b88f4e20..e658a44d9 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -15,7 +15,6 @@ import java.util.UUID; import javax.annotation.Nullable; -import com.mojang.logging.LogUtils; import com.simibubi.create.content.trains.graph.DiscoveredPath; import org.apache.commons.lang3.mutable.MutableDouble; @@ -618,7 +617,7 @@ public class Navigation { TrackEdge edge = entry.edge; TrackNode node1 = entry.node1; TrackNode node2 = entry.node2; - //LogUtils.getLogger().info("straight: " + entry.straight + " | dist: " + distance + " | old: " + node1.getLocation().getLocation().toString() + " | new: " + node2.getLocation().getLocation().toString() + " | " + (entry.straight + entry.distance)); + if (costRelevant) penalty += penalties.getOrDefault(edge, 0); @@ -687,10 +686,8 @@ public class Navigation { reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); if (destination != null && remainingDist == 0.0 && stationTest.test(newDistance, newDistance + penalty, reachedVia, - Pair.of(Couple.create(node2, newNode), newEdge), destination)){ - LogUtils.getLogger().info("Node term: " + total); + Pair.of(Couple.create(node2, newNode), newEdge), destination)) return; - } frontier.add(new FrontierEntry(newDistance, penalty, remainingDist, node2, newNode, newEdge)); } } From 0014ea6ac6052f34d9e5bc521515b39a8eb4f395 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Fri, 27 Oct 2023 00:17:12 +0200 Subject: [PATCH 08/16] Cache position in search method --- .../simibubi/create/content/trains/entity/Navigation.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index e658a44d9..0a19dd7ff 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -543,6 +543,9 @@ public class Navigation { if (graph == null) return; + // Cache the position of a node on the station edge if provided + Vec3 destinationNodePosition = destination == null ? null : destination.edgeLocation.getSecond().getLocation(); + // Cache the list of track types that the train can travel on Set validTypes = new HashSet<>(); for (int i = 0; i < train.carriages.size(); i++) { @@ -682,7 +685,7 @@ public class Navigation { TrackNode newNode = target.getKey(); TrackEdge newEdge = target.getValue(); double newDistance = newEdge.getLength() + distance; - double remainingDist = destination == null ? 0 : newNode.getLocation().getLocation().distanceTo(destination.edgeLocation.getSecond().getLocation()); + double remainingDist = destination == null ? 0 : newNode.getLocation().getLocation().distanceTo(destinationNodePosition); reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); if (destination != null && remainingDist == 0.0 && stationTest.test(newDistance, newDistance + penalty, reachedVia, From 1b2f3b1a8d87f0b1e23675fb06d7edc29250197e Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Fri, 27 Oct 2023 00:42:42 +0200 Subject: [PATCH 09/16] Remove unused lines --- .../com/simibubi/create/content/trains/entity/Navigation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index 0a19dd7ff..df61cd3f5 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -605,7 +605,7 @@ public class Navigation { frontier.add(new FrontierEntry(distanceToNode2, 0, initialNode1, initialNode2, initialEdge)); int signalWeight = Mth.clamp(ticksWaitingForSignal * 2, Train.Penalties.RED_SIGNAL, 200); - int total = 0; + Search: while (!frontier.isEmpty()) { FrontierEntry entry = frontier.poll(); if (!visited.add(entry.edge)) @@ -616,7 +616,7 @@ public class Navigation { if (distance > maxDistance) continue; - total++; + TrackEdge edge = entry.edge; TrackNode node1 = entry.node1; TrackNode node2 = entry.node2; From 740ec9092f30bd3730eb350aad1c7ab470979966 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Sat, 28 Oct 2023 16:40:03 +0200 Subject: [PATCH 10/16] Use octile distance instead of euclidean --- .../content/trains/entity/Navigation.java | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index df61cd3f5..d5bdbf3d7 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -1,6 +1,7 @@ package com.simibubi.create.content.trains.entity; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; @@ -685,10 +686,36 @@ public class Navigation { TrackNode newNode = target.getKey(); TrackEdge newEdge = target.getValue(); double newDistance = newEdge.getLength() + distance; - double remainingDist = destination == null ? 0 : newNode.getLocation().getLocation().distanceTo(destinationNodePosition); + double remainingDist = 0; + + if (destination != null) { + Vec3 newNodePosition = newNode.getLocation().getLocation(); + double dMin = Math.abs(newNodePosition.x - destinationNodePosition.x); + double dMid = Math.abs(newNodePosition.y - destinationNodePosition.y); + double dMax = Math.abs(newNodePosition.z - destinationNodePosition.z); + + double temp; + if (dMin > dMid) { + temp = dMid; + dMid = dMin; + dMin = temp; + } + if (dMin > dMax) { + temp = dMax; + dMax = dMin; + dMin = temp; + } + if (dMid > dMax) { + temp = dMax; + dMax = dMid; + dMid = temp; + } + // Octile distance from newNode to station node + remainingDist = 0.317837245 * dMin + 0.414213562 * dMid + dMax; + } reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); - if (destination != null && remainingDist == 0.0 && stationTest.test(newDistance, newDistance + penalty, reachedVia, + if (destination != null && Math.round(remainingDist) == 0 && stationTest.test(newDistance, newDistance + penalty, reachedVia, Pair.of(Couple.create(node2, newNode), newEdge), destination)) return; frontier.add(new FrontierEntry(newDistance, penalty, remainingDist, node2, newNode, newEdge)); From 321056bdd02ea82a7123dad2a0e4e788df5af5c4 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Sun, 29 Oct 2023 12:23:05 +0100 Subject: [PATCH 11/16] Fix penalties not applying on station edge --- .../create/content/trains/entity/Navigation.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index d5bdbf3d7..c2415cb06 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -1,7 +1,6 @@ package com.simibubi.create.content.trains.entity; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; @@ -693,7 +692,7 @@ public class Navigation { double dMin = Math.abs(newNodePosition.x - destinationNodePosition.x); double dMid = Math.abs(newNodePosition.y - destinationNodePosition.y); double dMax = Math.abs(newNodePosition.z - destinationNodePosition.z); - + // Sort distance vector in ascending order double temp; if (dMin > dMid) { temp = dMid; @@ -714,10 +713,10 @@ public class Navigation { remainingDist = 0.317837245 * dMin + 0.414213562 * dMid + dMax; } + if (destination != null && Math.round(remainingDist) == 0) + remainingDist = -999999; // Ensure edges containing the station node get checked first + reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); - if (destination != null && Math.round(remainingDist) == 0 && stationTest.test(newDistance, newDistance + penalty, reachedVia, - Pair.of(Couple.create(node2, newNode), newEdge), destination)) - return; frontier.add(new FrontierEntry(newDistance, penalty, remainingDist, node2, newNode, newEdge)); } } From d6781acbf6f44923b369051d00a0fef12cf24c44 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Mon, 30 Oct 2023 01:16:16 +0100 Subject: [PATCH 12/16] Multiple stations 1 call --- .../entity/CarriageContraptionEntity.java | 3 + .../content/trains/entity/Navigation.java | 287 +++++++++++------- .../content/trains/graph/DiscoveredPath.java | 2 - .../trains/schedule/ScheduleRuntime.java | 21 +- 4 files changed, 189 insertions(+), 124 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java index dc750775d..fdf358405 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java @@ -1,6 +1,7 @@ package com.simibubi.create.content.trains.entity; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -54,6 +55,8 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.network.PacketDistributor; +import org.openjdk.nashorn.internal.objects.Global; + public class CarriageContraptionEntity extends OrientedContraptionEntity { private static final EntityDataAccessor CARRIAGE_DATA = diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index c2415cb06..76fd7e6f8 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -439,6 +439,13 @@ public class Navigation { @Nullable public DiscoveredPath findPathTo(GlobalStation destination, double maxCost) { + ArrayList destinations = new ArrayList<>(); + destinations.add(destination); + return findPathTo(destinations, maxCost); + } + + @Nullable + public DiscoveredPath findPathTo(ArrayList destinations, double maxCost) { TrackGraph graph = train.graph; if (graph == null) return null; @@ -458,34 +465,36 @@ public class Navigation { : graph.getConnectionsFrom(initialPoint.node2) .get(initialPoint.node1); - search(Double.MAX_VALUE, maxCost, forward, destination, (distance, cost, reachedVia, currentEntry, globalStation) -> { - if (globalStation != destination) - return false; + search(Double.MAX_VALUE, maxCost, forward, destinations, (distance, cost, reachedVia, currentEntry, globalStation) -> { + for (GlobalStation destination : destinations){ + if (globalStation == destination) { + TrackEdge edge = currentEntry.getSecond(); + TrackNode node1 = currentEntry.getFirst() + .getFirst(); + TrackNode node2 = currentEntry.getFirst() + .getSecond(); - TrackEdge edge = currentEntry.getSecond(); - TrackNode node1 = currentEntry.getFirst() - .getFirst(); - TrackNode node2 = currentEntry.getFirst() - .getSecond(); + List> currentPath = new ArrayList<>(); + Pair> backTrack = reachedVia.get(edge); + Couple toReach = Couple.create(node1, node2); + TrackEdge edgeReached = edge; + while (backTrack != null) { + if (edgeReached == initialEdge) + break; + if (backTrack.getFirst()) + currentPath.add(0, toReach); + toReach = backTrack.getSecond(); + edgeReached = graph.getConnection(toReach); + backTrack = reachedVia.get(edgeReached); + } - List> currentPath = new ArrayList<>(); - Pair> backTrack = reachedVia.get(edge); - Couple toReach = Couple.create(node1, node2); - TrackEdge edgeReached = edge; - while (backTrack != null) { - if (edgeReached == initialEdge) - break; - if (backTrack.getFirst()) - currentPath.add(0, toReach); - toReach = backTrack.getSecond(); - edgeReached = graph.getConnection(toReach); - backTrack = reachedVia.get(edgeReached); + double position = edge.getLength() - destination.getLocationOn(edge); + double distanceToDestination = distance - position; + results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath, destination)); + return true; + } } - - double position = edge.getLength() - destination.getLocationOn(edge); - double distanceToDestination = distance - position; - results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath, destination)); - return true; + return false; }); } @@ -534,18 +543,15 @@ public class Navigation { return result.getValue(); } - public void search(double maxDistance, boolean forward, GlobalStation destination, StationTest stationTest) { - search(maxDistance, -1, forward, destination, stationTest); + public void search(double maxDistance, boolean forward, ArrayList destinations, StationTest stationTest) { + search(maxDistance, -1, forward, destinations, stationTest); } - public void search(double maxDistance, double maxCost, boolean forward, GlobalStation destination, StationTest stationTest) { + public void search(double maxDistance, double maxCost, boolean forward, ArrayList destinations, StationTest stationTest) { TrackGraph graph = train.graph; if (graph == null) return; - // Cache the position of a node on the station edge if provided - Vec3 destinationNodePosition = destination == null ? null : destination.edgeLocation.getSecond().getLocation(); - // Cache the list of track types that the train can travel on Set validTypes = new HashSet<>(); for (int i = 0; i < train.carriages.size(); i++) { @@ -603,10 +609,57 @@ public class Navigation { double distanceToNode2 = forward ? initialEdge.getLength() - startingPoint.position : startingPoint.position; - frontier.add(new FrontierEntry(distanceToNode2, 0, initialNode1, initialNode2, initialEdge)); int signalWeight = Mth.clamp(ticksWaitingForSignal * 2, Train.Penalties.RED_SIGNAL, 200); - Search: while (!frontier.isEmpty()) { + int initialPenalty = 0; + if (costRelevant) + initialPenalty += penalties.getOrDefault(initialEdge, 0); + + EdgeData initialSignalData = initialEdge.getEdgeData(); + if (initialSignalData.hasPoints()) { + for (TrackEdgePoint point : initialSignalData.getPoints()) { + if (point.getLocationOn(initialEdge) < initialEdge.getLength() - distanceToNode2) + continue; + if (costRelevant && distanceToNode2 + initialPenalty > maxCost) + return; + if (!point.canNavigateVia(initialNode2)) + return; + if (point instanceof SignalBoundary signal) { + if (signal.isForcedRed(initialNode2)) { + initialPenalty += Train.Penalties.REDSTONE_RED_SIGNAL; + continue; + } + UUID group = signal.getGroup(initialNode2); + if (group == null) + continue; + SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(group); + if (signalEdgeGroup == null) + continue; + if (signalEdgeGroup.isOccupiedUnless(signal)) { + initialPenalty += signalWeight; + signalWeight /= 2; + } + } + if (point instanceof GlobalStation station) { + Train presentTrain = station.getPresentTrain(); + boolean isOwnStation = presentTrain == train; + if (presentTrain != null && !isOwnStation) + initialPenalty += Train.Penalties.STATION_WITH_TRAIN; + if (station.canApproachFrom(initialNode2) && stationTest.test(distanceToNode2, distanceToNode2 + initialPenalty, reachedVia, + Pair.of(Couple.create(initialNode1, initialNode2), initialEdge), station)) + return; + if (!isOwnStation) + initialPenalty += Train.Penalties.STATION; + } + } + } + + if (costRelevant && distanceToNode2 + initialPenalty > maxCost) + return; + + frontier.add(new FrontierEntry(distanceToNode2, initialPenalty, false, initialNode1, initialNode2, initialEdge)); + + while (!frontier.isEmpty()) { FrontierEntry entry = frontier.poll(); if (!visited.add(entry.edge)) continue; @@ -621,51 +674,19 @@ public class Navigation { TrackNode node1 = entry.node1; TrackNode node2 = entry.node2; - if (costRelevant) - penalty += penalties.getOrDefault(edge, 0); - - EdgeData signalData = edge.getEdgeData(); - if (signalData.hasPoints()) { - for (TrackEdgePoint point : signalData.getPoints()) { - if (node1 == initialNode1 && point.getLocationOn(edge) < edge.getLength() - distanceToNode2) - continue; - if (costRelevant && distance + penalty > maxCost) - continue Search; - if (!point.canNavigateVia(node2)) - continue Search; - if (point instanceof SignalBoundary signal) { - if (signal.isForcedRed(node2)) { - penalty += Train.Penalties.REDSTONE_RED_SIGNAL; - continue; + if (entry.hasDestination) { + EdgeData signalData = edge.getEdgeData(); + if (signalData.hasPoints()) { + for (TrackEdgePoint point : signalData.getPoints()) { + if (point instanceof GlobalStation station) { + if (station.canApproachFrom(node2) && stationTest.test(distance, penalty, reachedVia, + Pair.of(Couple.create(node1, node2), edge), station)) + return; } - UUID group = signal.getGroup(node2); - if (group == null) - continue; - SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(group); - if (signalEdgeGroup == null) - continue; - if (signalEdgeGroup.isOccupiedUnless(signal)) { - penalty += signalWeight; - signalWeight /= 2; - } - } - if (point instanceof GlobalStation station) { - Train presentTrain = station.getPresentTrain(); - boolean isOwnStation = presentTrain == train; - if (presentTrain != null && !isOwnStation) - penalty += Train.Penalties.STATION_WITH_TRAIN; - if (station.canApproachFrom(node2) && stationTest.test(distance, distance + penalty, reachedVia, - Pair.of(Couple.create(node1, node2), edge), station)) - return; - if (!isOwnStation) - penalty += Train.Penalties.STATION; } } } - if (costRelevant && distance + penalty > maxCost) - continue; - List> validTargets = new ArrayList<>(); Map connectionsFrom = graph.getConnectionsFrom(node2); for (Entry connection : connectionsFrom.entrySet()) { @@ -679,45 +700,100 @@ public class Navigation { if (validTargets.isEmpty()) continue; - for (Entry target : validTargets) { + Search: for (Entry target : validTargets) { if (!validTypes.contains(target.getValue().getTrackMaterial().trackType)) continue; TrackNode newNode = target.getKey(); TrackEdge newEdge = target.getValue(); - double newDistance = newEdge.getLength() + distance; - double remainingDist = 0; + int newPenalty = penalty; + double edgeLength = newEdge.getLength(); + double newDistance = distance + edgeLength; - if (destination != null) { - Vec3 newNodePosition = newNode.getLocation().getLocation(); - double dMin = Math.abs(newNodePosition.x - destinationNodePosition.x); - double dMid = Math.abs(newNodePosition.y - destinationNodePosition.y); - double dMax = Math.abs(newNodePosition.z - destinationNodePosition.z); - // Sort distance vector in ascending order - double temp; - if (dMin > dMid) { - temp = dMid; - dMid = dMin; - dMin = temp; + if (costRelevant) + newPenalty += penalties.getOrDefault(newEdge, 0); + + boolean hasDestination = false; + EdgeData signalData = newEdge.getEdgeData(); + if (signalData.hasPoints()) { + for (TrackEdgePoint point : signalData.getPoints()) { + if (node2 == initialNode1 && point.getLocationOn(newEdge) < edgeLength - distanceToNode2) + continue; + if (costRelevant && newDistance + newPenalty > maxCost) + continue Search; + if (!point.canNavigateVia(newNode)) + continue Search; + if (point instanceof SignalBoundary signal) { + if (signal.isForcedRed(newNode)) { + newPenalty += Train.Penalties.REDSTONE_RED_SIGNAL; + continue; + } + UUID group = signal.getGroup(newNode); + if (group == null) + continue; + SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(group); + if (signalEdgeGroup == null) + continue; + if (signalEdgeGroup.isOccupiedUnless(signal)) { + newPenalty += signalWeight; + signalWeight /= 2; + } + } + if (point instanceof GlobalStation station) { + Train presentTrain = station.getPresentTrain(); + boolean isOwnStation = presentTrain == train; + if (presentTrain != null && !isOwnStation) + newPenalty += Train.Penalties.STATION_WITH_TRAIN; + if (station.canApproachFrom(newNode) && stationTest.test(newDistance, newDistance + newPenalty, reachedVia, + Pair.of(Couple.create(node2, newNode), newEdge), station)) { + hasDestination = true; + continue; + } + if (!isOwnStation) + newPenalty += Train.Penalties.STATION; + } } - if (dMin > dMax) { - temp = dMax; - dMax = dMin; - dMin = temp; - } - if (dMid > dMax) { - temp = dMax; - dMax = dMid; - dMid = temp; - } - // Octile distance from newNode to station node - remainingDist = 0.317837245 * dMin + 0.414213562 * dMid + dMax; } - if (destination != null && Math.round(remainingDist) == 0) - remainingDist = -999999; // Ensure edges containing the station node get checked first + if (costRelevant && newDistance + newPenalty > maxCost) + continue; + + double remainingDist = 0; + + if (destinations != null && !destinations.isEmpty()) { + remainingDist = Double.MAX_VALUE; + Vec3 newNodePosition = newNode.getLocation().getLocation(); + for (GlobalStation destination : destinations) { + TrackNodeLocation destinationNode = destination.edgeLocation.getFirst(); + double dMin = Math.abs(newNodePosition.x - destinationNode.getLocation().x); + double dMid = Math.abs(newNodePosition.y - destinationNode.getLocation().y); + double dMax = Math.abs(newNodePosition.z - destinationNode.getLocation().z); + // Sort distance vector in ascending order + double temp; + if (dMin > dMid) { + temp = dMid; + dMid = dMin; + dMin = temp; + } + if (dMin > dMax) { + temp = dMax; + dMax = dMin; + dMin = temp; + } + if (dMid > dMax) { + temp = dMax; + dMax = dMid; + dMid = temp; + } + // Octile distance from newNode to station node + double currentRemaining = 0.317837245 * dMin + 0.414213562 * dMid + dMax + destination.position; + if (node2.getLocation().equals(destinationNode)) + currentRemaining -= newEdge.getLength() * 2; // Correct the distance estimator for station edge + remainingDist = Math.min(remainingDist, currentRemaining); + } + } reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); - frontier.add(new FrontierEntry(newDistance, penalty, remainingDist, node2, newNode, newEdge)); + frontier.add(new FrontierEntry(newDistance, newPenalty, remainingDist, hasDestination, node2, newNode, newEdge)); } } } @@ -727,22 +803,25 @@ public class Navigation { double distance; int penalty; double remaining; + boolean hasDestination; TrackNode node1; TrackNode node2; TrackEdge edge; - public FrontierEntry(double distance, int penalty, TrackNode node1, TrackNode node2, TrackEdge edge) { + public FrontierEntry(double distance, int penalty, boolean hasDestination, TrackNode node1, TrackNode node2, TrackEdge edge) { this.distance = distance; this.penalty = penalty; this.remaining = 0; + this.hasDestination = hasDestination; this.node1 = node1; this.node2 = node2; this.edge = edge; } - public FrontierEntry(double distance, int penalty, double remaining, TrackNode node1, TrackNode node2, TrackEdge edge) { + public FrontierEntry(double distance, int penalty, double remaining, boolean hasDestination, TrackNode node1, TrackNode node2, TrackEdge edge) { this.distance = distance; this.penalty = penalty; this.remaining = remaining; + this.hasDestination = hasDestination; this.node1 = node1; this.node2 = node2; this.edge = edge; diff --git a/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java b/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java index 9b2be0aaa..398b261e1 100644 --- a/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java +++ b/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java @@ -3,8 +3,6 @@ package com.simibubi.create.content.trains.graph; import com.simibubi.create.content.trains.station.GlobalStation; import com.simibubi.create.foundation.utility.Couple; -import org.openjdk.nashorn.internal.objects.Global; - import java.util.List; public class DiscoveredPath { diff --git a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java index c78056d7a..53fd20b38 100644 --- a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java @@ -175,9 +175,8 @@ public class ScheduleRuntime { if (instruction instanceof DestinationInstruction destination) { String regex = destination.getFilterForRegex(); - DiscoveredPath best = null; - double bestCost = Double.MAX_VALUE; boolean anyMatch = false; + ArrayList validStations = new ArrayList<>(); if (!train.hasForwardConductor() && !train.hasBackwardConductor()) { train.status.missingConductor(); @@ -189,23 +188,9 @@ public class ScheduleRuntime { if (!globalStation.name.matches(regex)) continue; anyMatch = true; - boolean matchesCurrent = train.currentStation != null && train.currentStation.equals(globalStation.id); - double cost; - DiscoveredPath path = train.navigation.findPathTo(globalStation, bestCost); - if (matchesCurrent) { - cost = 0; - } else { - cost = path == null ? -1 : path.cost; - } - - if (cost < 0) - continue; - if (cost > bestCost) - continue; - best = path; - bestCost = cost; + validStations.add(globalStation); } - + DiscoveredPath best = train.navigation.findPathTo(validStations, Double.MAX_VALUE); if (best == null) { if (anyMatch) train.status.failedNavigation(); From 2565fa76055dfb5934e5d7202545edd42c5ec349 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Mon, 30 Oct 2023 14:14:11 +0100 Subject: [PATCH 13/16] Fix #4756 --- .../java/com/simibubi/create/content/trains/entity/Train.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Train.java b/src/main/java/com/simibubi/create/content/trains/entity/Train.java index 8a760bf25..ee73a2d89 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Train.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Train.java @@ -1040,7 +1040,7 @@ public class Train { } public static class Penalties { - static final int STATION = 200, STATION_WITH_TRAIN = 300; + static final int STATION = 50, STATION_WITH_TRAIN = 300; static final int MANUAL_TRAIN = 200, IDLE_TRAIN = 700, ARRIVING_TRAIN = 50, WAITING_TRAIN = 50, ANY_TRAIN = 25, RED_SIGNAL = 25, REDSTONE_RED_SIGNAL = 400; } From aff05bf84ab780f397da188f5060b8195741060f Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Mon, 30 Oct 2023 18:38:29 +0100 Subject: [PATCH 14/16] cleanup --- .../trains/entity/CarriageContraptionEntity.java | 3 --- .../create/content/trains/entity/Navigation.java | 10 ++++++---- .../content/trains/schedule/ScheduleRuntime.java | 10 +++++----- .../content/trains/station/StationBlockEntity.java | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java index fdf358405..dc750775d 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java @@ -1,7 +1,6 @@ package com.simibubi.create.content.trains.entity; import java.lang.ref.WeakReference; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -55,8 +54,6 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.network.PacketDistributor; -import org.openjdk.nashorn.internal.objects.Global; - public class CarriageContraptionEntity extends OrientedContraptionEntity { private static final EntityDataAccessor CARRIAGE_DATA = diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index 76fd7e6f8..798103a0c 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -611,6 +611,7 @@ public class Navigation { int signalWeight = Mth.clamp(ticksWaitingForSignal * 2, Train.Penalties.RED_SIGNAL, 200); + // Apply penalties to initial edge int initialPenalty = 0; if (costRelevant) initialPenalty += penalties.getOrDefault(initialEdge, 0); @@ -712,6 +713,7 @@ public class Navigation { if (costRelevant) newPenalty += penalties.getOrDefault(newEdge, 0); + // Apply penalty to next connected edge boolean hasDestination = false; EdgeData signalData = newEdge.getEdgeData(); if (signalData.hasPoints()) { @@ -758,7 +760,7 @@ public class Navigation { continue; double remainingDist = 0; - + // Calculate remaining distance estimator for next connected edge if (destinations != null && !destinations.isEmpty()) { remainingDist = Double.MAX_VALUE; Vec3 newNodePosition = newNode.getLocation().getLocation(); @@ -884,9 +886,9 @@ public class Navigation { c -> currentPath.add(Couple .deserializeEach(c.getList("Nodes", Tag.TAG_COMPOUND), c2 -> TrackNodeLocation.read(c2, dimensions)) .map(graph::locateNode))); - + removeBrokenPathEntries(); - + waitingForSignal = tag.contains("BlockingSignal") ? Pair.of(tag.getUUID("BlockingSignal"), tag.getBoolean("BlockingSignalSide")) : null; @@ -901,7 +903,7 @@ public class Navigation { * Trains might load or save with null entries in their path, this method avoids * that anomaly from causing NPEs. The underlying issue has not been found. */ - + boolean nullEntriesPresent = false; for (Iterator> iterator = currentPath.iterator(); iterator.hasNext();) { diff --git a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java index 53fd20b38..58dcbee64 100644 --- a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java @@ -326,9 +326,9 @@ public class ScheduleRuntime { return accumulatedTime; if (predictionTicks.size() <= currentEntry) return accumulatedTime; - + int departureTime = estimateStayDuration(index); - + if (accumulatedTime < 0) { predictions.add(createPrediction(index, filter.getFilter(), currentTitle, accumulatedTime)); return Math.min(accumulatedTime, departureTime); @@ -344,10 +344,10 @@ public class ScheduleRuntime { if (accumulatedTime != TBD) accumulatedTime += departureTime; - + if (departureTime == INVALID) accumulatedTime = INVALID; - + return accumulatedTime; } @@ -375,7 +375,7 @@ public class ScheduleRuntime { private TrainDeparturePrediction createPrediction(int index, String destination, String currentTitle, int time) { if (time == INVALID) return null; - + int size = schedule.entries.size(); if (index >= size) { if (!schedule.cyclic) diff --git a/src/main/java/com/simibubi/create/content/trains/station/StationBlockEntity.java b/src/main/java/com/simibubi/create/content/trains/station/StationBlockEntity.java index 596873cd1..86318d1a1 100644 --- a/src/main/java/com/simibubi/create/content/trains/station/StationBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/trains/station/StationBlockEntity.java @@ -288,7 +288,7 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab BlockPos up = new BlockPos(track.getUpNormal(level, pos, state)); BlockPos down = new BlockPos(track.getUpNormal(level, pos, state).scale(-1)); int bogeyOffset = pos.distManhattan(edgePoint.getGlobalPosition()) - 1; - + if (!isValidBogeyOffset(bogeyOffset)) { for (boolean upsideDown : Iterate.falseAndTrue) { for (int i = -1; i <= 1; i++) { From 30a90a62037767fc26bb92df00c26fcbbfeeabc5 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Mon, 30 Oct 2023 19:50:58 +0100 Subject: [PATCH 15/16] Fix crash on invalid schedule regex (e.g. "station \\E1\\Q") --- .../trains/schedule/ScheduleRuntime.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java index 58dcbee64..85521e83a 100644 --- a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.regex.PatternSyntaxException; import com.simibubi.create.AllItems; import com.simibubi.create.content.trains.display.GlobalTrainDisplayData.TrainDeparturePrediction; @@ -183,13 +184,16 @@ public class ScheduleRuntime { cooldown = INTERVAL; return null; } - - for (GlobalStation globalStation : train.graph.getPoints(EdgePointType.STATION)) { - if (!globalStation.name.matches(regex)) - continue; - anyMatch = true; - validStations.add(globalStation); - } + + try { + for (GlobalStation globalStation : train.graph.getPoints(EdgePointType.STATION)) { + if (!globalStation.name.matches(regex)) + continue; + anyMatch = true; + validStations.add(globalStation); + } + } catch (PatternSyntaxException ignored) {} + DiscoveredPath best = train.navigation.findPathTo(validStations, Double.MAX_VALUE); if (best == null) { if (anyMatch) From 3c3ce93fa1dfee985ab581625bde173a9e2096f4 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Wed, 1 Nov 2023 13:56:53 +0100 Subject: [PATCH 16/16] final cleanup --- .../simibubi/create/content/trains/entity/Navigation.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index 798103a0c..a27a52b7c 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -658,7 +658,7 @@ public class Navigation { if (costRelevant && distanceToNode2 + initialPenalty > maxCost) return; - frontier.add(new FrontierEntry(distanceToNode2, initialPenalty, false, initialNode1, initialNode2, initialEdge)); + frontier.add(new FrontierEntry(distanceToNode2, initialPenalty, initialNode1, initialNode2, initialEdge)); while (!frontier.isEmpty()) { FrontierEntry entry = frontier.poll(); @@ -787,7 +787,7 @@ public class Navigation { dMid = temp; } // Octile distance from newNode to station node - double currentRemaining = 0.317837245 * dMin + 0.414213562 * dMid + dMax + destination.position; + double currentRemaining = 0.317837245195782 * dMin + 0.414213562373095 * dMid + dMax + destination.position; if (node2.getLocation().equals(destinationNode)) currentRemaining -= newEdge.getLength() * 2; // Correct the distance estimator for station edge remainingDist = Math.min(remainingDist, currentRemaining); @@ -810,11 +810,11 @@ public class Navigation { TrackNode node2; TrackEdge edge; - public FrontierEntry(double distance, int penalty, boolean hasDestination, TrackNode node1, TrackNode node2, TrackEdge edge) { + public FrontierEntry(double distance, int penalty, TrackNode node1, TrackNode node2, TrackEdge edge) { this.distance = distance; this.penalty = penalty; this.remaining = 0; - this.hasDestination = hasDestination; + this.hasDestination = false; this.node1 = node1; this.node2 = node2; this.edge = edge;