Getting signals across

- Fixed signaling edge groups not being cleared when a signal migrates graphs
- Graphs without any signals now create a fallback group
- Fixed removed signals not clearing signal groups of previously affected edges
- Signaling sections now use a fixed set of colours
- Colours of signaling sections now try to be distinct from neighbouring sections
- Colours of signal sections now get synched to the client
- Edges now keep track of any intersections with bezier turns that cannot be part of the underlying graph structure
- Signaling sections now consider all other intersecting sections when checking whether they are occupied
- Holding a signal block now highlights nearby tracks with their respective section colour
- Graphs now keep track of a boundary box
- Track edges now carry back-references to their nodes
- Graph debugger moved from keypress to f3
This commit is contained in:
simibubi 2022-04-25 17:53:45 +02:00
parent 423bb407f7
commit 3848221712
30 changed files with 1198 additions and 508 deletions

View file

@ -57,7 +57,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
}
public BezierConnection secondary() {
return new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), false, hasGirder);
return new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), !primary, hasGirder);
}
public BezierConnection(CompoundTag compound, BlockPos localTo) {

View file

@ -13,23 +13,20 @@ import java.util.UUID;
import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableObject;
import org.lwjgl.glfw.GLFW;
import com.simibubi.create.AllKeys;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.KineticDebugger;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.entity.TrainPacket;
import com.simibubi.create.content.logistics.trains.management.display.GlobalTrainDisplayData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.foundation.networking.AllPackets;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
@ -56,7 +53,14 @@ public class GlobalRailwayManager {
loadTrackData(serverPlayer.getServer());
trackNetworks.values()
.forEach(g -> sync.sendFullGraphTo(g, serverPlayer));
sync.sendEdgeGroups(signalEdgeGroups.keySet(), serverPlayer);
ArrayList<SignalEdgeGroup> asList = new ArrayList<>(signalEdgeGroups.values());
sync.sendEdgeGroups(asList.stream()
.map(g -> g.id)
.toList(),
asList.stream()
.map(g -> g.color)
.toList(),
serverPlayer);
for (Train train : trains.values())
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> serverPlayer),
new TrainPacket(train, true));
@ -119,19 +123,32 @@ public class GlobalRailwayManager {
return trackNetworks.computeIfAbsent(graphID, uid -> new TrackGraph(graphID));
}
public void putGraphWithDefaultGroup(TrackGraph graph) {
SignalEdgeGroup group = new SignalEdgeGroup(graph.id);
signalEdgeGroups.put(graph.id, group);
sync.edgeGroupCreated(graph.id, group.color);
putGraph(graph);
}
public void putGraph(TrackGraph graph) {
trackNetworks.put(graph.id, graph);
markTracksDirty();
}
public void removeGraph(TrackGraph railGraph) {
trackNetworks.remove(railGraph.id);
public void removeGraphAndGroup(TrackGraph graph) {
signalEdgeGroups.remove(graph.id);
sync.edgeGroupRemoved(graph.id);
removeGraph(graph);
}
public void removeGraph(TrackGraph graph) {
trackNetworks.remove(graph.id);
markTracksDirty();
}
public void updateSplitGraph(TrackGraph graph) {
Set<TrackGraph> disconnected = graph.findDisconnectedGraphs(null);
disconnected.forEach(this::putGraph);
disconnected.forEach(this::putGraphWithDefaultGroup);
if (!disconnected.isEmpty()) {
sync.graphSplit(graph, disconnected);
markTracksDirty();
@ -159,10 +176,7 @@ public class GlobalRailwayManager {
}
public void tick(Level level) {
ResourceLocation location2 = DimensionType.OVERWORLD_LOCATION.location();
ResourceLocation location = level.dimension()
.location();
if (!location.equals(location2))
if (level.dimension() != Level.OVERWORLD)
return;
for (SignalEdgeGroup group : signalEdgeGroups.values()) {
@ -170,8 +184,10 @@ public class GlobalRailwayManager {
group.reserved = null;
}
for (TrackGraph graph : trackNetworks.values())
for (TrackGraph graph : trackNetworks.values()) {
graph.tickPoints(true);
graph.resolveIntersectingEdgeGroups(level);
}
tickTrains(level);
@ -182,12 +198,12 @@ public class GlobalRailwayManager {
if (GlobalTrainDisplayData.updateTick)
GlobalTrainDisplayData.refresh();
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_K))
// trackNetworks.values()
// .forEach(TrackGraph::debugViewReserved);
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H) && AllKeys.altDown())
// for (TrackGraph trackGraph : trackNetworks.values())
// TrackGraphVisualizer.debugViewSignalData(trackGraph);
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && AllKeys.altDown())
// trackNetworks.values()
// .forEach(TrackGraph::debugViewNodes);
// for (TrackGraph trackGraph : trackNetworks.values())
// TrackGraphVisualizer.debugViewNodes(trackGraph);
}
private void tickTrains(Level level) {
@ -218,13 +234,16 @@ public class GlobalRailwayManager {
}
}
public void tickSignalOverlay() {
if (!KineticDebugger.isActive())
for (TrackGraph trackGraph : trackNetworks.values())
TrackGraphVisualizer.visualiseSignalEdgeGroups(trackGraph);
}
public void clientTick() {
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H) && !AllKeys.altDown())
trackNetworks.values()
.forEach(TrackGraph::debugViewSignalData);
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && !AllKeys.altDown())
trackNetworks.values()
.forEach(TrackGraph::debugViewNodes);
if (KineticDebugger.isActive())
for (TrackGraph trackGraph : trackNetworks.values())
TrackGraphVisualizer.debugViewGraph(trackGraph);
}
public GlobalRailwayManager sided(LevelAccessor level) {

View file

@ -6,6 +6,8 @@ import java.util.UUID;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.foundation.utility.NBTHelper;
@ -37,6 +39,7 @@ public class RailwaySavedData extends SavedData {
sd.signalEdgeGroups = new HashMap<>();
sd.trains = new HashMap<>();
Create.LOGGER.info("Loading Railway Information...");
NBTHelper.iterateCompoundList(nbt.getList("RailGraphs", Tag.TAG_COMPOUND), c -> {
TrackGraph graph = TrackGraph.read(c);
sd.trackNetworks.put(graph.id, graph);
@ -49,13 +52,29 @@ public class RailwaySavedData extends SavedData {
SignalEdgeGroup group = SignalEdgeGroup.read(c);
sd.signalEdgeGroups.put(group.id, group);
});
for (TrackGraph graph : sd.trackNetworks.values()) {
for (SignalBoundary signal : graph.getPoints(EdgePointType.SIGNAL)) {
UUID groupId = signal.groups.getFirst();
UUID otherGroupId = signal.groups.getSecond();
if (groupId == null || otherGroupId == null)
continue;
SignalEdgeGroup group = sd.signalEdgeGroups.get(groupId);
SignalEdgeGroup otherGroup = sd.signalEdgeGroups.get(otherGroupId);
if (group == null || otherGroup == null)
continue;
group.putAdjacent(otherGroupId);
otherGroup.putAdjacent(groupId);
}
}
return sd;
}
public Map<UUID, TrackGraph> getTrackNetworks() {
return trackNetworks;
}
public Map<UUID, Train> getTrains() {
return trains;
}

View file

@ -1,22 +1,33 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public class TrackEdge {
public TrackNode node1;
public TrackNode node2;
BezierConnection turn;
EdgeData edgeData;
public TrackEdge(BezierConnection turn) {
public TrackEdge(TrackNode node1, TrackNode node2, BezierConnection turn) {
this.edgeData = new EdgeData(this);
this.node1 = node1;
this.node2 = node2;
this.turn = turn;
this.edgeData = new EdgeData();
}
public boolean isTurn() {
@ -31,29 +42,112 @@ public class TrackEdge {
return turn;
}
public Vec3 getDirection(TrackNode node1, TrackNode node2, boolean fromFirst) {
return getPosition(node1, node2, fromFirst ? 0.25f : 1)
.subtract(getPosition(node1, node2, fromFirst ? 0 : 0.75f))
public Vec3 getDirection(boolean fromFirst) {
return getPosition(fromFirst ? 0.25f : 1).subtract(getPosition(fromFirst ? 0 : 0.75f))
.normalize();
}
public double getLength(TrackNode node1, TrackNode node2) {
public double getLength() {
return isTurn() ? turn.getLength()
: node1.location.getLocation()
.distanceTo(node2.location.getLocation());
}
public double incrementT(TrackNode node1, TrackNode node2, double currentT, double distance) {
boolean tooFar = Math.abs(distance) > 5;
distance = distance / getLength(node1, node2);
public double incrementT(double currentT, double distance) {
boolean tooFar = Math.abs(distance) > 5;
distance = distance / getLength();
return !tooFar && isTurn() ? turn.incrementT(currentT, distance) : currentT + distance;
}
public Vec3 getPosition(TrackNode node1, TrackNode node2, double t) {
public Vec3 getPosition(double t) {
return isTurn() ? turn.getPosition(Mth.clamp(t, 0, 1))
: VecHelper.lerp((float) t, node1.location.getLocation(), node2.location.getLocation());
}
public Collection<double[]> getIntersection(TrackNode node1, TrackNode node2, TrackEdge other, TrackNode other1,
TrackNode other2) {
Vec3 v1 = node1.location.getLocation();
Vec3 v2 = node2.location.getLocation();
Vec3 w1 = other1.location.getLocation();
Vec3 w2 = other2.location.getLocation();
if (v1.y != v2.y || v1.y != w1.y || v1.y != w2.y)
return Collections.emptyList();
if (!isTurn()) {
if (!other.isTurn())
return ImmutableList.of(VecHelper.intersectRanged(v1, w1, v2, w2, Axis.Y));
return other.getIntersection(other1, other2, this, node1, node2)
.stream()
.map(a -> new double[] { a[1], a[0] })
.toList();
}
AABB bb = turn.getBounds();
if (!other.isTurn()) {
if (!bb.intersects(w1, w2))
return Collections.emptyList();
Vec3 seg1 = v1;
Vec3 seg2 = null;
double t = 0;
Collection<double[]> intersections = new ArrayList<>();
for (int i = 0; i < turn.getSegmentCount(); i++) {
double tOffset = t;
t += .5;
seg2 = getPosition(t / getLength());
double[] intersection = VecHelper.intersectRanged(seg1, w1, seg2, w2, Axis.Y);
seg1 = seg2;
if (intersection == null)
continue;
intersection[0] += tOffset;
intersections.add(intersection);
}
return intersections;
}
if (!bb.intersects(other.turn.getBounds()))
return Collections.emptyList();
Vec3 seg1 = v1;
Vec3 seg2 = null;
double t = 0;
Collection<double[]> intersections = new ArrayList<>();
for (int i = 0; i < turn.getSegmentCount(); i++) {
double tOffset = t;
t += .5;
seg2 = getPosition(t / getLength());
Vec3 otherSeg1 = w1;
Vec3 otherSeg2 = null;
double u = 0;
for (int j = 0; j < other.turn.getSegmentCount(); j++) {
double uOffset = u;
u += .5;
otherSeg2 = other.getPosition(u / other.getLength());
double[] intersection = VecHelper.intersectRanged(seg1, otherSeg1, seg2, otherSeg2, Axis.Y);
otherSeg1 = otherSeg2;
if (intersection == null)
continue;
intersection[0] += tOffset;
intersection[1] += uOffset;
intersections.add(intersection);
}
seg1 = seg2;
}
return intersections;
}
public Vec3 getNormal(TrackNode node1, TrackNode node2, double t) {
return isTurn() ? turn.getNormal(Mth.clamp(t, 0, 1)) : node1.getNormal();
}
@ -65,7 +159,7 @@ public class TrackEdge {
}
public static TrackEdge read(FriendlyByteBuf buffer) {
return new TrackEdge(buffer.readBoolean() ? new BezierConnection(buffer) : null);
return new TrackEdge(null, null, buffer.readBoolean() ? new BezierConnection(buffer) : null);
}
public CompoundTag write() {
@ -76,8 +170,8 @@ public class TrackEdge {
public static TrackEdge read(CompoundTag tag, TrackGraph graph) {
TrackEdge trackEdge =
new TrackEdge(tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null);
trackEdge.edgeData = EdgeData.read(tag.getCompound("Signals"), graph);
new TrackEdge(null, null, tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null);
trackEdge.edgeData = EdgeData.read(tag.getCompound("Signals"), trackEdge, graph);
return trackEdge;
}

View file

@ -16,34 +16,28 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import org.lwjgl.glfw.GLFW;
import com.simibubi.create.AllKeys;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointManager;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointStorage;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackEdgeIntersection;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.world.entity.Entity;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.Vec3;
@ -58,6 +52,9 @@ public class TrackGraph {
Map<Integer, TrackNode> nodesById;
Map<TrackNode, Map<TrackNode, TrackEdge>> connectionsByNode;
EdgePointStorage edgePoints;
Map<ResourceKey<Level>, TrackGraphBounds> bounds;
List<TrackEdge> deferredIntersectionUpdates;
public TrackGraph() {
this(UUID.randomUUID());
@ -67,8 +64,10 @@ public class TrackGraph {
setId(graphID);
nodes = new HashMap<>();
nodesById = new HashMap<>();
bounds = new HashMap<>();
connectionsByNode = new IdentityHashMap<>();
edgePoints = new EdgePointStorage();
deferredIntersectionUpdates = new ArrayList<>();
}
//
@ -104,6 +103,16 @@ public class TrackGraph {
//
public TrackGraphBounds getBounds(Level level) {
return bounds.computeIfAbsent(level.dimension(), dim -> new TrackGraphBounds(this, dim));
}
public void invalidateBounds() {
bounds.clear();
}
//
public Set<TrackNodeLocation> getNodes() {
return nodes.keySet();
}
@ -125,6 +134,7 @@ public class TrackGraph {
return false;
TrackNode newNode = nodes.get(location);
Create.RAILWAYS.sync.nodeAdded(this, newNode);
invalidateBounds();
markDirty();
return true;
}
@ -165,17 +175,30 @@ public class TrackGraph {
}
nodesById.remove(removed.netId);
invalidateBounds();
if (!connectionsByNode.containsKey(removed))
return true;
Map<TrackNode, TrackEdge> connections = connectionsByNode.remove(removed);
for (TrackEdge trackEdge : connections.values())
for (TrackEdgePoint point : trackEdge.getEdgeData()
.getPoints()) {
for (Entry<TrackNode, TrackEdge> entry : connections.entrySet()) {
TrackEdge trackEdge = entry.getValue();
EdgeData edgeData = trackEdge.getEdgeData();
for (TrackEdgePoint point : edgeData.getPoints()) {
if (level != null)
point.invalidate(level);
edgePoints.remove(point.getType(), point.getId());
}
if (level != null) {
TrackNode otherNode = entry.getKey();
for (TrackEdgeIntersection intersection : edgeData.getIntersections()) {
Couple<TrackNodeLocation> target = intersection.target;
TrackGraph graph = Create.RAILWAYS.getGraph(level, target.getFirst());
if (graph != null)
graph.removeIntersection(intersection, removed, otherNode);
}
}
}
for (TrackNode railNode : connections.keySet())
if (connectionsByNode.containsKey(railNode))
@ -185,6 +208,29 @@ public class TrackGraph {
return true;
}
private void removeIntersection(TrackEdgeIntersection intersection, TrackNode targetNode1, TrackNode targetNode2) {
TrackNode node1 = locateNode(intersection.target.getFirst());
TrackNode node2 = locateNode(intersection.target.getSecond());
if (node1 == null || node2 == null)
return;
Map<TrackNode, TrackEdge> from1 = getConnectionsFrom(node1);
if (from1 != null) {
TrackEdge edge = from1.get(node2);
if (edge != null)
edge.getEdgeData()
.removeIntersection(this, intersection.id);
}
Map<TrackNode, TrackEdge> from2 = getConnectionsFrom(node2);
if (from2 != null) {
TrackEdge edge = from2.get(node1);
if (edge != null)
edge.getEdgeData()
.removeIntersection(this, intersection.id);
}
}
public static int nextNodeId() {
return netIdGenerator.incrementAndGet();
}
@ -209,6 +255,7 @@ public class TrackGraph {
edgePoints.transferAll(toOther, toOther.edgePoints);
nodes.clear();
connectionsByNode.clear();
toOther.invalidateBounds();
Map<UUID, Train> trains = Create.RAILWAYS.trains;
for (Iterator<UUID> iterator = trains.keySet()
@ -268,6 +315,7 @@ public class TrackGraph {
public void transfer(TrackNode node, TrackGraph target) {
target.addNode(node);
target.invalidateBounds();
TrackNodeLocation nodeLoc = node.getLocation();
Map<TrackNode, TrackEdge> connections = getConnectionsFrom(node);
@ -298,6 +346,7 @@ public class TrackGraph {
nodes.remove(nodeLoc);
nodesById.remove(node.getNetId());
connectionsByNode.remove(node);
invalidateBounds();
}
public boolean isEmpty() {
@ -312,16 +361,66 @@ public class TrackGraph {
return getConnectionsFrom(nodes.getFirst()).get(nodes.getSecond());
}
public void connectNodes(TrackNodeLocation location, TrackNodeLocation location2, TrackEdge edge) {
public void connectNodes(LevelAccessor reader, TrackNodeLocation location, TrackNodeLocation location2,
@Nullable BezierConnection turn) {
TrackNode node1 = nodes.get(location);
TrackNode node2 = nodes.get(location2);
TrackEdge edge2 = new TrackEdge(edge.turn != null ? edge.turn.secondary() : null);
boolean bezier = turn != null;
TrackEdge edge = new TrackEdge(node1, node2, turn);
TrackEdge edge2 = new TrackEdge(node2, node1, bezier ? turn.secondary() : null);
if (reader instanceof Level level) {
for (TrackGraph graph : Create.RAILWAYS.trackNetworks.values()) {
if (graph != this
&& !graph.getBounds(level).box.intersects(location.getLocation(), location2.getLocation()))
continue;
for (TrackNode otherNode1 : graph.nodes.values()) {
Map<TrackNode, TrackEdge> connections = graph.connectionsByNode.get(otherNode1);
if (connections == null)
continue;
for (Entry<TrackNode, TrackEdge> entry : connections.entrySet()) {
TrackNode otherNode2 = entry.getKey();
TrackEdge otherEdge = entry.getValue();
if (graph == this)
if (otherNode1 == node1 || otherNode2 == node1 || otherNode1 == node2
|| otherNode2 == node2)
continue;
if (edge == otherEdge)
continue;
if (!bezier && !otherEdge.isTurn())
continue;
if (otherEdge.isTurn() && otherEdge.turn.isPrimary())
continue;
Collection<double[]> intersections =
edge.getIntersection(node1, node2, otherEdge, otherNode1, otherNode2);
UUID id = UUID.randomUUID();
for (double[] intersection : intersections) {
double s = intersection[0];
double t = intersection[1];
edge.edgeData.addIntersection(this, id, s, otherNode1, otherNode2, t);
edge2.edgeData.addIntersection(this, id, edge.getLength() - s, otherNode1, otherNode2, t);
otherEdge.edgeData.addIntersection(graph, id, t, node1, node2, s);
TrackEdge otherEdge2 = graph.getConnection(Couple.create(otherNode2, otherNode1));
if (otherEdge2 != null)
otherEdge2.edgeData.addIntersection(graph, id, otherEdge.getLength() - t, node1, node2,
s);
}
}
}
}
}
putConnection(node1, node2, edge);
putConnection(node2, node1, edge2);
Create.RAILWAYS.sync.edgeAdded(this, node1, node2, edge);
Create.RAILWAYS.sync.edgeAdded(this, node2, node1, edge2);
markDirty();
}
@ -343,6 +442,47 @@ public class TrackGraph {
return connections.put(node2, edge) == null;
}
public void deferIntersectionUpdate(TrackEdge edge) {
deferredIntersectionUpdates.add(edge);
}
public void resolveIntersectingEdgeGroups(Level level) {
for (TrackEdge edge : deferredIntersectionUpdates) {
if (!connectionsByNode.containsKey(edge.node1) || edge != connectionsByNode.get(edge.node1)
.get(edge.node2))
continue;
EdgeData edgeData = edge.getEdgeData();
for (TrackEdgeIntersection intersection : edgeData.getIntersections()) {
UUID groupId = edgeData.getGroupAtPosition(this, intersection.location);
Couple<TrackNodeLocation> target = intersection.target;
TrackGraph graph = Create.RAILWAYS.getGraph(level, target.getFirst());
if (graph == null)
continue;
TrackNode node1 = graph.locateNode(target.getFirst());
TrackNode node2 = graph.locateNode(target.getSecond());
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node1);
if (connectionsFrom == null)
continue;
TrackEdge otherEdge = connectionsFrom.get(node2);
if (otherEdge == null)
continue;
UUID otherGroupId = otherEdge.getEdgeData()
.getGroupAtPosition(graph, intersection.targetLocation);
SignalEdgeGroup group = Create.RAILWAYS.signalEdgeGroups.get(groupId);
SignalEdgeGroup otherGroup = Create.RAILWAYS.signalEdgeGroups.get(otherGroupId);
if (group == null || otherGroup == null)
continue;
intersection.groupId = groupId;
group.putIntersection(intersection.id, otherGroupId);
otherGroup.putIntersection(intersection.id, groupId);
}
}
deferredIntersectionUpdates.clear();
}
public void markDirty() {
Create.RAILWAYS.markTracksDirty();
}
@ -418,6 +558,8 @@ public class TrackGraph {
NBTHelper.iterateCompoundList(nodeTag.getList("Connections", Tag.TAG_COMPOUND), c -> {
TrackNode node2 = indexTracker.get(c.getInt("To"));
TrackEdge edge = TrackEdge.read(c.getCompound("EdgeData"), graph);
edge.node1 = node1;
edge.node2 = node2;
graph.putConnection(node1, node2, edge);
});
}
@ -425,300 +567,4 @@ public class TrackGraph {
return graph;
}
public void debugViewReserved() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null)
return;
Set<UUID> reserved = new HashSet<>();
Set<UUID> occupied = new HashSet<>();
for (Train train : Create.RAILWAYS.trains.values()) {
reserved.addAll(train.reservedSignalBlocks);
occupied.addAll(train.occupiedSignalBlocks.keySet());
}
reserved.removeAll(occupied);
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 100)
continue;
Map<TrackNode, TrackEdge> map = connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
continue;
Vec3 yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
TrackEdge edge = entry.getValue();
EdgeData signalData = edge.getEdgeData();
UUID singleGroup = signalData.singleSignalGroup;
SignalEdgeGroup signalEdgeGroup =
singleGroup == null ? null : Create.RAILWAYS.sided(null).signalEdgeGroups.get(singleGroup);
if (!edge.isTurn()) {
Vec3 p1 = edge.getPosition(node, other, 0);
Vec3 p2 = edge.getPosition(node, other, 1);
if (signalData.hasPoints()) {
double prev = 0;
double length = edge.getLength(node, other);
SignalBoundary prevBoundary = null;
SignalEdgeGroup group = null;
for (TrackEdgePoint trackEdgePoint : signalData.getPoints()) {
if (!(trackEdgePoint instanceof SignalBoundary boundary))
continue;
prevBoundary = boundary;
UUID groupId = boundary.getGroup(node);
group = Create.RAILWAYS.sided(null).signalEdgeGroups.get(groupId);
double start = prev + (prev == 0 ? 0 : 1 / 16f / length);
prev = (boundary.getLocationOn(node, other, edge) / length) - 1 / 16f / length;
if (group != null
&& (group.reserved != null || occupied.contains(groupId) || reserved.contains(groupId)))
CreateClient.OUTLINER
.showLine(Pair.of(boundary, edge), edge.getPosition(node, other, start)
.add(yOffset),
edge.getPosition(node, other, prev)
.add(yOffset))
.colored(occupied.contains(groupId) ? 0xF68989
: group.reserved != null ? 0xC5D8A4 : 0xF6E7D8)
.lineWidth(1 / 16f);
}
if (prevBoundary != null) {
UUID groupId = prevBoundary.getGroup(other);
SignalEdgeGroup lastGroup = Create.RAILWAYS.sided(null).signalEdgeGroups.get(groupId);
if (lastGroup != null && ((lastGroup.reserved != null || occupied.contains(groupId)
|| reserved.contains(groupId))))
CreateClient.OUTLINER
.showLine(edge, edge.getPosition(node, other, prev + 1 / 16f / length)
.add(yOffset), p2.add(yOffset))
.colored(occupied.contains(groupId) ? 0xF68989
: lastGroup.reserved != null ? 0xC5D8A4 : 0xF6E7D8)
.lineWidth(1 / 16f);
continue;
}
}
if (signalEdgeGroup == null || !(signalEdgeGroup.reserved != null || occupied.contains(singleGroup)
|| reserved.contains(singleGroup)))
continue;
CreateClient.OUTLINER.showLine(edge, p1.add(yOffset), p2.add(yOffset))
.colored(occupied.contains(singleGroup) ? 0xF68989
: signalEdgeGroup.reserved != null ? 0xC5D8A4 : 0xF6E7D8)
.lineWidth(1 / 16f);
continue;
}
if (signalEdgeGroup == null || !(signalEdgeGroup.reserved != null || occupied.contains(singleGroup)
|| reserved.contains(singleGroup)))
continue;
int color =
occupied.contains(singleGroup) ? 0xF68989 : signalEdgeGroup.reserved != null ? 0xC5D8A4 : 0xF6E7D8;
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(node, other, i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(color)
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
public void debugViewSignalData() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null)
return;
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 50)
continue;
Map<TrackNode, TrackEdge> map = connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
continue;
Vec3 yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
TrackEdge edge = entry.getValue();
EdgeData signalData = edge.getEdgeData();
UUID singleGroup = signalData.singleSignalGroup;
SignalEdgeGroup signalEdgeGroup =
singleGroup == null ? null : Create.RAILWAYS.sided(null).signalEdgeGroups.get(singleGroup);
if (!edge.isTurn()) {
Vec3 p1 = edge.getPosition(node, other, 0);
Vec3 p2 = edge.getPosition(node, other, 1);
if (signalData.hasPoints()) {
double prev = 0;
double length = edge.getLength(node, other);
SignalBoundary prevBoundary = null;
SignalEdgeGroup group = null;
for (TrackEdgePoint trackEdgePoint : signalData.getPoints()) {
if (trackEdgePoint instanceof GlobalStation) {
Vec3 v1 = edge
.getPosition(node, other,
(trackEdgePoint.getLocationOn(node, other, edge) / length))
.add(yOffset);
Vec3 v2 = v1.add(node.normal.scale(3 / 16f));
CreateClient.OUTLINER.showLine(trackEdgePoint.id, v1, v2)
.colored(Color.mixColors(Color.WHITE, color, 1))
.lineWidth(1 / 8f);
continue;
}
if (!(trackEdgePoint instanceof SignalBoundary boundary))
continue;
prevBoundary = boundary;
group = Create.RAILWAYS.sided(null).signalEdgeGroups.get(boundary.getGroup(node));
if (group != null)
CreateClient.OUTLINER
.showLine(Pair.of(boundary, edge),
edge.getPosition(node, other, prev + (prev == 0 ? 0 : 1 / 16f / length))
.add(yOffset),
edge.getPosition(node, other,
(prev = boundary.getLocationOn(node, other, edge) / length)
- 1 / 16f / length)
.add(yOffset))
.colored(group.color.getRGB())
.lineWidth(1 / 16f);
}
if (prevBoundary != null) {
group = Create.RAILWAYS.sided(null).signalEdgeGroups.get(prevBoundary.getGroup(other));
if (group != null)
CreateClient.OUTLINER
.showLine(edge, edge.getPosition(node, other, prev + 1 / 16f / length)
.add(yOffset), p2.add(yOffset))
.colored(group.color.getRGB())
.lineWidth(1 / 16f);
continue;
}
}
if (signalEdgeGroup == null)
continue;
CreateClient.OUTLINER.showLine(edge, p1.add(yOffset), p2.add(yOffset))
.colored(signalEdgeGroup.color.getRGB())
.lineWidth(1 / 16f);
continue;
}
if (signalEdgeGroup == null)
continue;
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(node, other, i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(signalEdgeGroup.color.getRGB())
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
public void debugViewNodes() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null)
return;
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 50)
continue;
Vec3 yOffset = new Vec3(0, 3 / 16f, 0);
Vec3 v1 = location.add(yOffset);
Vec3 v2 = v1.add(node.normal.scale(3 / 16f));
CreateClient.OUTLINER.showLine(Integer.valueOf(node.netId), v1, v2)
.colored(Color.mixColors(Color.WHITE, color, 1))
.lineWidth(1 / 8f);
Map<TrackNode, TrackEdge> map = connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
continue;
yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
TrackEdge edge = entry.getValue();
if (!edge.isTurn()) {
CreateClient.OUTLINER.showLine(edge, edge.getPosition(node, other, 0)
.add(yOffset),
edge.getPosition(node, other, 1)
.add(yOffset))
.colored(color)
.lineWidth(1 / 16f);
continue;
}
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(node, other, i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(color)
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
}

View file

@ -0,0 +1,40 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public class TrackGraphBounds {
public AABB box;
public List<BezierConnection> beziers;
// TODO: filter nodes by dimensional coordinate
public TrackGraphBounds(TrackGraph graph, ResourceKey<Level> dimension) {
beziers = new ArrayList<>();
box = null;
for (TrackNode node : graph.nodes.values()) {
include(node);
Map<TrackNode, TrackEdge> connections = graph.getConnectionsFrom(node);
for (TrackEdge edge : connections.values())
if (edge.turn != null && edge.turn.isPrimary())
beziers.add(edge.turn);
}
if (box != null)
box = box.inflate(2);
}
private void include(TrackNode node) {
Vec3 v = node.location.getLocation();
AABB aabb = new AABB(v, v);
box = box == null ? aabb : box.minmax(aabb);
}
}

View file

@ -24,7 +24,7 @@ public class TrackGraphHelper {
public static GraphLocation getGraphLocationAt(Level level, BlockPos pos, AxisDirection targetDirection,
Vec3 targetAxis) {
BlockState trackBlockState = level.getBlockState(pos);
if (!(trackBlockState.getBlock() instanceof ITrackBlock track))
if (!(trackBlockState.getBlock()instanceof ITrackBlock track))
return null;
Vec3 axis = targetAxis.scale(targetDirection.getStep());
@ -44,7 +44,7 @@ public class TrackGraphHelper {
for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) {
TrackNode backNode = entry.getKey();
Vec3 direction = entry.getValue()
.getDirection(node, backNode, true);
.getDirection(true);
if (direction.scale(length)
.distanceToSqr(axis.scale(-1)) > 1 / 4096f)
continue;
@ -133,9 +133,9 @@ public class TrackGraphHelper {
BezierTrackPointLocation targetBezier) {
BlockState state = level.getBlockState(pos);
if (!(state.getBlock() instanceof ITrackBlock track))
if (!(state.getBlock()instanceof ITrackBlock track))
return null;
if (!(level.getBlockEntity(pos) instanceof TrackTileEntity trackTE))
if (!(level.getBlockEntity(pos)instanceof TrackTileEntity trackTE))
return null;
BezierConnection bc = trackTE.getConnections()
.get(targetBezier.curveTarget());
@ -163,7 +163,7 @@ public class TrackGraphHelper {
graphLocation.position = (targetBezier.segment() + 1) / 2f;
if (targetDirection == AxisDirection.POSITIVE) {
graphLocation.edge = graphLocation.edge.swap();
graphLocation.position = edge.getLength(node, targetNode) - graphLocation.position;
graphLocation.position = edge.getLength() - graphLocation.position;
}
return graphLocation;

View file

@ -1,7 +1,7 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@ -11,6 +11,7 @@ import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.EdgeGroupColor;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroupPacket;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.foundation.networking.AllPackets;
@ -87,16 +88,18 @@ public class TrackGraphSync {
//
public void sendEdgeGroups(Collection<UUID> ids, ServerPlayer player) {
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SignalEdgeGroupPacket(ids, true));
public void sendEdgeGroups(List<UUID> ids, List<EdgeGroupColor> colors, ServerPlayer player) {
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player),
new SignalEdgeGroupPacket(ids, colors, true));
}
public void edgeGroupCreated(UUID id) {
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new SignalEdgeGroupPacket(ImmutableList.of(id), true));
public void edgeGroupCreated(UUID id, EdgeGroupColor color) {
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new SignalEdgeGroupPacket(id, color));
}
public void edgeGroupRemoved(UUID id) {
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new SignalEdgeGroupPacket(ImmutableList.of(id), false));
AllPackets.channel.send(PacketDistributor.ALL.noArg(),
new SignalEdgeGroupPacket(ImmutableList.of(id), Collections.emptyList(), false));
}
//

View file

@ -169,8 +169,14 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
for (Pair<Couple<Integer>, TrackEdge> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst()
.map(graph::getNode);
if (nodes.getFirst() != null && nodes.getSecond() != null)
graph.putConnection(nodes.getFirst(), nodes.getSecond(), pair.getSecond());
TrackNode node1 = nodes.getFirst();
TrackNode node2 = nodes.getSecond();
if (node1 != null && node2 != null) {
TrackEdge edge = pair.getSecond();
edge.node1 = node1;
edge.node2 = node2;
graph.putConnection(node1, node2, edge);
}
}
for (TrackEdgePoint edgePoint : addedEdgePoints)
@ -203,13 +209,13 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
if (edge == null)
continue;
EdgeData edgeData = new EdgeData();
EdgeData edgeData = new EdgeData(edge);
if (groupType == NULL_GROUP)
edgeData.singleSignalGroup = null;
edgeData.setSingleSignalGroup(null, null);
else if (groupType == PASSIVE_GROUP)
edgeData.singleSignalGroup = EdgeData.passiveGroup;
edgeData.setSingleSignalGroup(null, EdgeData.passiveGroup);
else
edgeData.singleSignalGroup = idList.get(0);
edgeData.setSingleSignalGroup(null, idList.get(0));
List<TrackEdgePoint> points = edgeData.getPoints();
edge.edgeData = edgeData;
@ -232,9 +238,9 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
List<UUID> list = new ArrayList<>();
EdgeData edgeData = edge.getEdgeData();
int groupType = edgeData.hasSignalBoundaries() ? NULL_GROUP
: EdgeData.passiveGroup.equals(edgeData.singleSignalGroup) ? PASSIVE_GROUP : GROUP;
: EdgeData.passiveGroup.equals(edgeData.getSingleSignalGroup()) ? PASSIVE_GROUP : GROUP;
if (groupType == GROUP)
list.add(edgeData.singleSignalGroup);
list.add(edgeData.getSingleSignalGroup());
for (TrackEdgePoint point : edgeData.getPoints())
list.add(point.getId());
updatedEdgeData.put(key, Pair.of(groupType, list));

View file

@ -0,0 +1,283 @@
package com.simibubi.create.content.logistics.trains;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import org.lwjgl.glfw.GLFW;
import com.simibubi.create.AllKeys;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackEdgeIntersection;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.outliner.Outliner;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public class TrackGraphVisualizer {
public static void visualiseSignalEdgeGroups(TrackGraph graph) {
Minecraft mc = Minecraft.getInstance();
Entity cameraEntity = mc.cameraEntity;
if (cameraEntity == null)
return;
AABB box = graph.getBounds(mc.level).box;
if (box == null || !box.intersects(cameraEntity.getBoundingBox()
.inflate(50)))
return;
Vec3 camera = cameraEntity.getEyePosition();
Outliner outliner = CreateClient.OUTLINER;
boolean ctrl = AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL);
Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.sided(null).signalEdgeGroups;
float width = 1 / 8f;
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : graph.nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 50)
continue;
Map<TrackNode, TrackEdge> map = graph.connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
TrackEdge edge = entry.getValue();
EdgeData signalData = edge.getEdgeData();
// temporary
if (other.hashCode() > hashCode == ctrl)
for (TrackEdgeIntersection intersection : signalData.getIntersections()) {
Vec3 v1 = edge.getPosition(intersection.location / edge.getLength());
Vec3 v2 = v1.add(node.normal.scale(8 / 16f));
outliner.showLine(intersection, v1, v2)
.colored(Color.mixColors(Color.WHITE, graph.color, 1))
.lineWidth(width);
} //
if (other.hashCode() > hashCode && !ctrl)
continue;
Vec3 yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 5) / 64f, 0);
Vec3 startPoint = edge.getPosition(0);
Vec3 endPoint = edge.getPosition(1);
if (!edge.isTurn()) {
// Straight edge with signal boundaries
if (signalData.hasSignalBoundaries()) {
double prev = 0;
double length = edge.getLength();
SignalBoundary prevBoundary = null;
SignalEdgeGroup group = null;
for (TrackEdgePoint trackEdgePoint : signalData.getPoints()) {
if (!(trackEdgePoint instanceof SignalBoundary boundary))
continue;
prevBoundary = boundary;
group = allGroups.get(boundary.getGroup(node));
if (group != null)
outliner.showLine(Pair.of(boundary, edge),
edge.getPosition(prev + (prev == 0 ? 0 : 1 / 16f / length))
.add(yOffset),
edge.getPosition((prev = boundary.getLocationOn(edge) / length) - 1 / 16f / length)
.add(yOffset))
.colored(group.color.get())
.lineWidth(width);
}
if (prevBoundary != null) {
group = allGroups.get(prevBoundary.getGroup(other));
if (group != null)
outliner.showLine(edge, edge.getPosition(prev + 1 / 16f / length)
.add(yOffset), endPoint.add(yOffset))
.colored(group.color.get())
.lineWidth(width);
continue;
}
}
// Straight edge, no signal boundaries
UUID singleGroup = signalData.getEffectiveEdgeGroupId(graph);
SignalEdgeGroup singleEdgeGroup = singleGroup == null ? null : allGroups.get(singleGroup);
if (singleEdgeGroup == null)
continue;
outliner.showLine(edge, startPoint.add(yOffset), endPoint.add(yOffset))
.colored(singleEdgeGroup.color.get())
.lineWidth(width);
} else {
// Bezier edge with signal boundaries
if (signalData.hasSignalBoundaries()) {
Iterator<TrackEdgePoint> points = signalData.getPoints()
.iterator();
SignalBoundary currentBoundary = null;
double currentBoundaryPosition = 0;
while (points.hasNext()) {
TrackEdgePoint next = points.next();
if (!(next instanceof SignalBoundary signal))
continue;
currentBoundary = signal;
currentBoundaryPosition = signal.getLocationOn(edge);
break;
}
if (currentBoundary == null)
continue;
UUID initialGroupId = currentBoundary.getGroup(node);
if (initialGroupId == null)
continue;
SignalEdgeGroup initialGroup = allGroups.get(initialGroupId);
if (initialGroup == null)
continue;
Color currentColour = initialGroup.color.get();
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
double f = i * 1f / turn.getSegmentCount();
double position = f * turn.getLength();
Vec3 current = edge.getPosition(f);
if (previous != null) {
if (currentBoundary != null && position > currentBoundaryPosition) {
current = edge.getPosition((currentBoundaryPosition - width) / turn.getLength());
outliner
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(currentColour)
.lineWidth(width);
current = edge.getPosition((currentBoundaryPosition + width) / turn.getLength());
previous = current;
UUID newId = currentBoundary.getGroup(other);
if (newId != null && allGroups.containsKey(newId))
currentColour = allGroups.get(newId).color.get();
currentBoundary = null;
while (points.hasNext()) {
TrackEdgePoint next = points.next();
if (!(next instanceof SignalBoundary signal))
continue;
currentBoundary = signal;
currentBoundaryPosition = signal.getLocationOn(edge);
break;
}
}
outliner.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(currentColour)
.lineWidth(width);
}
previous = current;
}
}
// Bezier edge, no signal boundaries
UUID singleGroup = signalData.getEffectiveEdgeGroupId(graph);
SignalEdgeGroup singleEdgeGroup = singleGroup == null ? null : allGroups.get(singleGroup);
if (singleEdgeGroup == null)
continue;
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(i * 1f / turn.getSegmentCount());
if (previous != null)
outliner.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(singleEdgeGroup.color.get())
.lineWidth(width);
previous = current;
}
}
}
}
}
public static void debugViewGraph(TrackGraph graph) {
Minecraft mc = Minecraft.getInstance();
Entity cameraEntity = mc.cameraEntity;
if (cameraEntity == null)
return;
AABB box = graph.getBounds(mc.level).box;
if (box == null || !box.intersects(cameraEntity.getBoundingBox()
.inflate(50)))
return;
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : graph.nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 50)
continue;
Vec3 yOffset = new Vec3(0, 3 / 16f, 0);
Vec3 v1 = location.add(yOffset);
Vec3 v2 = v1.add(node.normal.scale(3 / 16f));
CreateClient.OUTLINER.showLine(Integer.valueOf(node.netId), v1, v2)
.colored(Color.mixColors(Color.WHITE, graph.color, 1))
.lineWidth(1 / 8f);
Map<TrackNode, TrackEdge> map = graph.connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
continue;
yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
TrackEdge edge = entry.getValue();
if (!edge.isTurn()) {
CreateClient.OUTLINER.showLine(edge, edge.getPosition(0)
.add(yOffset),
edge.getPosition(1)
.add(yOffset))
.colored(graph.color)
.lineWidth(1 / 16f);
continue;
}
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(graph.color)
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
}

View file

@ -32,7 +32,7 @@ public class TrackPropagator {
}
public static void onRailRemoved(LevelAccessor reader, BlockPos pos, BlockState state) {
if (!(state.getBlock()instanceof ITrackBlock track))
if (!(state.getBlock() instanceof ITrackBlock track))
return;
Collection<DiscoveredLocation> ends = track.getConnected(reader, pos, state, false, null);
@ -51,7 +51,7 @@ public class TrackPropagator {
sync.nodeRemoved(foundGraph, removedNode);
if (!foundGraph.isEmpty())
continue;
manager.removeGraph(foundGraph);
manager.removeGraphAndGroup(foundGraph);
sync.graphRemoved(foundGraph);
}
}
@ -79,7 +79,7 @@ public class TrackPropagator {
}
public static TrackGraph onRailAdded(LevelAccessor reader, BlockPos pos, BlockState state) {
if (!(state.getBlock()instanceof ITrackBlock track))
if (!(state.getBlock() instanceof ITrackBlock track))
return null;
// 1. Remove all immediately reachable node locations
@ -124,7 +124,7 @@ public class TrackPropagator {
TrackGraph railGraph = iterator.next();
if (!railGraph.isEmpty() || connectedGraphs.size() == 1)
continue;
manager.removeGraph(railGraph);
manager.removeGraphAndGroup(railGraph);
sync.graphRemoved(railGraph);
iterator.remove();
}
@ -136,7 +136,7 @@ public class TrackPropagator {
graph = other;
else {
other.transferAll(graph);
manager.removeGraph(other);
manager.removeGraphAndGroup(other);
sync.graphRemoved(other);
}
} else if (connectedGraphs.size() == 1) {
@ -144,7 +144,7 @@ public class TrackPropagator {
.findFirst()
.get();
} else
manager.putGraph(graph = new TrackGraph());
manager.putGraphWithDefaultGroup(graph = new TrackGraph());
DiscoveredLocation startNode = null;
@ -191,7 +191,7 @@ public class TrackPropagator {
if (isValidGraphNodeLocation(entry.currentNode, ends, first) && entry.currentNode != startNode) {
boolean nodeIsNew = graph.createNodeIfAbsent(entry.currentNode);
graph.connectNodes(parentNode, entry.currentNode, new TrackEdge(entry.currentNode.getTurn()));
graph.connectNodes(reader, parentNode, entry.currentNode, entry.currentNode.getTurn());
addedNodes.add(graph.locateNode(entry.currentNode));
parentNode = entry.currentNode;
if (!nodeIsNew)

View file

@ -239,8 +239,8 @@ public class CarriageSyncData {
TravellingPoint toApproach = pointsToApproach[index];
point.travel(graph, partial * f,
point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)), point.ignoreEdgePoints(),
point.ignoreTurns());
point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)),
point.ignoreEdgePoints(), point.ignoreTurns());
// could not pathfind to server location
if (!success.booleanValue()) {
@ -284,8 +284,7 @@ public class CarriageSyncData {
TrackEdge targetEdge = graph.getConnectionsFrom(targetNode1)
.get(targetNode2);
double distanceToNode2 =
forward ? initialEdge.getLength(initialNode1, initialNode2) - current.position : current.position;
double distanceToNode2 = forward ? initialEdge.getLength() - current.position : current.position;
frontier.add(Pair.of(distanceToNode2, Pair.of(Couple.create(initialNode1, initialNode2), initialEdge)));
@ -294,15 +293,12 @@ public class CarriageSyncData {
double distance = poll.getFirst();
Pair<Couple<TrackNode>, TrackEdge> currentEntry = poll.getSecond();
TrackNode node1 = currentEntry.getFirst()
.getFirst();
TrackNode node2 = currentEntry.getFirst()
.getSecond();
TrackEdge edge = currentEntry.getSecond();
if (edge == targetEdge)
return (float) (distance
- (forward ? edge.getLength(node1, node2) - target.position : target.position));
return (float) (distance - (forward ? edge.getLength() - target.position : target.position));
if (distance > maxDistance)
continue;
@ -310,10 +306,9 @@ public class CarriageSyncData {
List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>();
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node2);
for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) {
TrackNode newNode = entry.getKey();
TrackEdge newEdge = entry.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false);
Vec3 newDirection = newEdge.getDirection(node2, newNode, true);
Vec3 currentDirection = edge.getDirection(false);
Vec3 newDirection = newEdge.getDirection(true);
if (currentDirection.dot(newDirection) < 3 / 4f)
continue;
if (!visited.add(entry.getValue()))
@ -328,8 +323,7 @@ public class CarriageSyncData {
TrackNode newNode = entry.getKey();
TrackEdge newEdge = entry.getValue();
reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, edge));
frontier.add(Pair.of(newEdge.getLength(node2, newNode) + distance,
Pair.of(Couple.create(node2, newNode), newEdge)));
frontier.add(Pair.of(newEdge.getLength() + distance, Pair.of(Couple.create(node2, newNode), newEdge)));
}
}

View file

@ -449,7 +449,7 @@ public class Navigation {
backTrack = reachedVia.get(edgeReached);
}
double position = edge.getLength(node1, node2) - destination.getLocationOn(node1, node2, edge);
double position = edge.getLength() - destination.getLocationOn(edge);
double distanceToDestination = distance - position;
results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath));
return true;
@ -510,12 +510,7 @@ public class Navigation {
return false;
TrackEdge edge = currentEntry.getSecond();
TrackNode node1 = currentEntry.getFirst()
.getFirst();
TrackNode node2 = currentEntry.getFirst()
.getSecond();
double position = edge.getLength(node1, node2) - globalStation.getLocationOn(node1, node2, edge);
double position = edge.getLength() - globalStation.getLocationOn(edge);
if (distance - position < minDistance)
return false;
result.setValue(globalStation);
@ -571,8 +566,7 @@ public class Navigation {
TrackNode initialNode2 = forward ? startingPoint.node2 : startingPoint.node1;
TrackEdge initialEdge = graph.getConnectionsFrom(initialNode1)
.get(initialNode2);
double distanceToNode2 = forward ? initialEdge.getLength(initialNode1, initialNode2) - startingPoint.position
: startingPoint.position;
double distanceToNode2 = forward ? initialEdge.getLength() - startingPoint.position : startingPoint.position;
frontier.add(new FrontierEntry(distanceToNode2, 0, initialNode1, initialNode2, initialEdge));
@ -597,8 +591,7 @@ public class Navigation {
EdgeData signalData = edge.getEdgeData();
if (signalData.hasPoints()) {
for (TrackEdgePoint point : signalData.getPoints()) {
if (node1 == initialNode1
&& point.getLocationOn(node1, node2, edge) < edge.getLength(node1, node2) - distanceToNode2)
if (node1 == initialNode1 && point.getLocationOn(edge) < edge.getLength() - distanceToNode2)
continue;
if (costRelevant && distance + penalty > maxCost)
continue Search;
@ -624,10 +617,9 @@ public class Navigation {
List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>();
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node2);
for (Entry<TrackNode, TrackEdge> connection : connectionsFrom.entrySet()) {
TrackNode newNode = connection.getKey();
TrackEdge newEdge = connection.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false);
Vec3 newDirection = newEdge.getDirection(node2, newNode, true);
Vec3 currentDirection = edge.getDirection(false);
Vec3 newDirection = newEdge.getDirection(true);
if (currentDirection.dot(newDirection) < 3 / 4f)
continue;
validTargets.add(connection);
@ -639,7 +631,7 @@ public class Navigation {
for (Entry<TrackNode, TrackEdge> target : validTargets) {
TrackNode newNode = target.getKey();
TrackEdge newEdge = target.getValue();
double newDistance = newEdge.getLength(node2, newNode) + distance;
double newDistance = newEdge.getLength() + distance;
int newPenalty = penalty;
reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2)));
frontier.add(new FrontierEntry(newDistance, newPenalty, node2, newNode, newEdge));
@ -704,10 +696,10 @@ public class Navigation {
destination = graph != null && tag.contains("Destination")
? graph.getPoint(EdgePointType.STATION, tag.getUUID("Destination"))
: null;
if (destination == null)
return;
distanceToDestination = tag.getDouble("DistanceToDestination");
distanceStartedAt = tag.getDouble("DistanceStartedAt");
destinationBehindTrain = tag.getBoolean("BehindTrain");

View file

@ -129,7 +129,7 @@ public class Train {
addToSignalGroups(occupiedSignalBlocks.keySet());
return;
}
if (updateSignalBlocks) {
updateSignalBlocks = false;
collectInitiallyOccupiedSignalBlocks();
@ -714,15 +714,15 @@ public class Train {
MutableObject<UUID> prevGroup = new MutableObject<>(null);
if (signalData.hasSignalBoundaries()) {
SignalBoundary nextBoundary = signalData.next(EdgePointType.SIGNAL, node1, node2, edge, position);
SignalBoundary nextBoundary = signalData.next(EdgePointType.SIGNAL, position);
if (nextBoundary == null) {
double d = 0;
SignalBoundary prev = null;
SignalBoundary current = signalData.next(EdgePointType.SIGNAL, node1, node2, edge, 0);
SignalBoundary current = signalData.next(EdgePointType.SIGNAL, 0);
while (current != null) {
prev = current;
d = current.getLocationOn(node1, node2, edge);
current = signalData.next(EdgePointType.SIGNAL, node1, node2, edge, d);
d = current.getLocationOn(edge);
current = signalData.next(EdgePointType.SIGNAL, d);
}
if (prev != null) {
UUID group = prev.getGroup(node2);
@ -740,9 +740,12 @@ public class Train {
}
}
} else if (signalData.singleSignalGroup != null && allGroups.containsKey(signalData.singleSignalGroup)) {
occupy(signalData.singleSignalGroup, null);
prevGroup.setValue(signalData.singleSignalGroup);
} else {
UUID groupId = signalData.getEffectiveEdgeGroupId(graph);
if (allGroups.containsKey(groupId)) {
occupy(groupId, null);
prevGroup.setValue(groupId);
}
}
forEachTravellingPointBackwards((tp, d) -> {
@ -764,9 +767,9 @@ public class Train {
});
}
public boolean shouldCarriageSyncThisTick(long gameTicks, int updateInterval) {
return (gameTicks + tickOffset) % updateInterval == 0;
return (gameTicks + tickOffset) % updateInterval == 0;
}
public Couple<Couple<TrackNode>> getEndpointEdges() {

View file

@ -27,8 +27,8 @@ public class TrainMigration {
public TrainMigration() {}
public TrainMigration(TravellingPoint point) {
double t = point.position / point.edge.getLength(point.node1, point.node2);
fallback = point.edge.getPosition(point.node1, point.node2, t);
double t = point.position / point.edge.getLength();
fallback = point.edge.getPosition(t);
curve = point.edge.isTurn();
positionOnOldEdge = point.position;
locations = Couple.create(point.node1.getLocation(), point.node2.getLocation());
@ -71,7 +71,7 @@ public class TrainMigration {
continue;
TrackNode newNode2 = entry.getKey();
float radius = 1 / 64f;
Vec3 direction = edge.getDirection(newNode1, newNode2, true);
Vec3 direction = edge.getDirection(true);
if (!Mth.equal(direction.dot(prevDirection), 1))
continue;
Vec3 intersectSphere = VecHelper.intersectSphere(nodeVec, direction, fallback, radius);
@ -80,7 +80,7 @@ public class TrainMigration {
if (!Mth.equal(direction.dot(intersectSphere.subtract(nodeVec)
.normalize()), 1))
continue;
double edgeLength = edge.getLength(newNode1, newNode2);
double edgeLength = edge.getLength();
double position = intersectSphere.distanceTo(nodeVec) - radius;
if (Double.isNaN(position))
continue;

View file

@ -153,9 +153,9 @@ public class TravellingPoint {
Entry<TrackNode, TrackEdge> best = null;
for (Entry<TrackNode, TrackEdge> entry : validTargets) {
Vec3 trajectory = edge.getDirection(node1, node2, false);
Vec3 trajectory = edge.getDirection(false);
Vec3 entryTrajectory = entry.getValue()
.getDirection(node2, entry.getKey(), true);
.getDirection(true);
Vec3 normal = trajectory.cross(upNormal);
double dot = normal.dot(entryTrajectory);
double diff = Math.abs(direction.targetDot - dot);
@ -178,14 +178,14 @@ public class TravellingPoint {
public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector,
IEdgePointListener signalListener, ITurnListener turnListener) {
blocked = false;
double edgeLength = edge.getLength(node1, node2);
double edgeLength = edge.getLength();
if (distance == 0)
return 0;
double prevPos = position;
double traveled = distance;
double currentT = position / edgeLength;
double incrementT = edge.incrementT(node1, node2, currentT, distance);
double incrementT = edge.incrementT(currentT, distance);
position = incrementT * edgeLength;
// FIXME: using incrementT like this becomes inaccurate at medium-long distances
@ -193,7 +193,7 @@ public class TravellingPoint {
// 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<>();
@ -221,8 +221,8 @@ public class TravellingPoint {
continue;
TrackEdge newEdge = entry.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false);
Vec3 newDirection = newEdge.getDirection(node2, newNode, true);
Vec3 currentDirection = edge.getDirection(false);
Vec3 newDirection = newEdge.getDirection(true);
if (currentDirection.dot(newDirection) < 3 / 4f)
continue;
@ -258,7 +258,7 @@ public class TravellingPoint {
}
prevPos = 0;
edgeLength = edge.getLength(node1, node2);
edgeLength = edge.getLength();
}
} else {
@ -274,8 +274,8 @@ public class TravellingPoint {
TrackEdge newEdge = graph.getConnectionsFrom(newNode)
.get(node1);
Vec3 currentDirection = edge.getDirection(node1, node2, true);
Vec3 newDirection = newEdge.getDirection(newNode, node1, false);
Vec3 currentDirection = edge.getDirection(true);
Vec3 newDirection = newEdge.getDirection(false);
if (currentDirection.dot(newDirection) < 3 / 4f)
continue;
@ -297,7 +297,7 @@ public class TravellingPoint {
edge = graph.getConnectionsFrom(node1)
.get(node2);
collectedDistance += edgeLength;
edgeLength = edge.getLength(node1, node2);
edgeLength = edge.getLength();
position += edgeLength;
blockedLocation =
@ -326,11 +326,11 @@ public class TravellingPoint {
double to = forward ? position : prevPos;
List<TrackEdgePoint> edgePoints = edgeData.getPoints();
double length = edge.getLength(node1, node2);
double length = edge.getLength();
for (int i = 0; i < edgePoints.size(); i++) {
int index = forward ? i : edgePoints.size() - i - 1;
TrackEdgePoint nextBoundary = edgePoints.get(index);
double locationOn = nextBoundary.getLocationOn(node1, node2, edge);
double locationOn = nextBoundary.getLocationOn(edge);
double distance = forward ? locationOn : length - locationOn;
if (forward ? (locationOn < from || locationOn >= to) : (locationOn <= from || locationOn > to))
continue;
@ -346,14 +346,14 @@ public class TravellingPoint {
TrackNode n = node1;
node1 = node2;
node2 = n;
position = edge.getLength(node1, node2) - position;
position = edge.getLength() - position;
edge = graph.getConnectionsFrom(node1)
.get(node2);
}
public Vec3 getPosition() {
double t = position / edge.getLength(node1, node2);
return edge.getPosition(node1, node2, t)
double t = position / edge.getLength();
return edge.getPosition(t)
.add(edge.getNormal(node1, node2, t)
.scale(1));
}

View file

@ -1,15 +1,23 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag;
@ -21,11 +29,15 @@ public class EdgeData {
public static final UUID passiveGroup = UUID.fromString("00000000-0000-0000-0000-000000000000");
public UUID singleSignalGroup;
private UUID singleSignalGroup;
private List<TrackEdgePoint> points;
private List<TrackEdgeIntersection> intersections;
private TrackEdge edge;
public EdgeData() {
public EdgeData(TrackEdge edge) {
this.edge = edge;
points = new ArrayList<>();
intersections = new ArrayList<>();
singleSignalGroup = passiveGroup;
}
@ -33,56 +45,134 @@ public class EdgeData {
return singleSignalGroup == null;
}
public UUID getSingleSignalGroup() {
return singleSignalGroup;
}
public void setSingleSignalGroup(@Nullable TrackGraph graph, UUID singleSignalGroup) {
if (graph != null && !Objects.equal(singleSignalGroup, this.singleSignalGroup))
refreshIntersectingSignalGroups(graph);
this.singleSignalGroup = singleSignalGroup;
}
public void refreshIntersectingSignalGroups(TrackGraph graph) {
Map<UUID, SignalEdgeGroup> groups = Create.RAILWAYS.signalEdgeGroups;
for (TrackEdgeIntersection intersection : intersections) {
if (intersection.groupId == null)
continue;
SignalEdgeGroup group = groups.get(intersection.groupId);
if (group != null)
group.removeIntersection(intersection.id);
}
if (hasIntersections())
graph.deferIntersectionUpdate(edge);
}
public boolean hasPoints() {
return !points.isEmpty();
}
public boolean hasIntersections() {
return !intersections.isEmpty();
}
public List<TrackEdgeIntersection> getIntersections() {
return intersections;
}
public void addIntersection(TrackGraph graph, UUID id, double position, TrackNode target1, TrackNode target2,
double targetPosition) {
TrackNodeLocation loc1 = target1.getLocation();
TrackNodeLocation loc2 = target2.getLocation();
for (TrackEdgeIntersection existing : intersections)
if (existing.isNear(position) && existing.targets(loc1, loc2))
return;
TrackEdgeIntersection intersection = new TrackEdgeIntersection();
intersection.id = id;
intersection.location = position;
intersection.target = Couple.create(loc1, loc2);
intersection.targetLocation = targetPosition;
intersections.add(intersection);
graph.deferIntersectionUpdate(edge);
}
public void removeIntersection(TrackGraph graph, UUID id) {
refreshIntersectingSignalGroups(graph);
for (Iterator<TrackEdgeIntersection> iterator = intersections.iterator(); iterator.hasNext();) {
TrackEdgeIntersection existing = iterator.next();
if (existing.id.equals(id))
iterator.remove();
}
}
public UUID getGroupAtPosition(TrackGraph graph, double position) {
if (!hasSignalBoundaries())
return getEffectiveEdgeGroupId(graph);
SignalBoundary firstSignal = next(EdgePointType.SIGNAL, 0);
UUID currentGroup = firstSignal.getGroup(edge.node1);
for (TrackEdgePoint trackEdgePoint : getPoints()) {
if (!(trackEdgePoint instanceof SignalBoundary sb))
continue;
if (sb.getLocationOn(edge) >= position)
return currentGroup;
currentGroup = sb.getGroup(edge.node2);
}
return currentGroup;
}
public List<TrackEdgePoint> getPoints() {
return points;
}
public void removePoint(TrackNode node1, TrackNode node2, TrackEdge edge, TrackEdgePoint point) {
points.remove(point);
if (point.getType() == EdgePointType.SIGNAL)
singleSignalGroup = next(point.getType(), node1, node2, edge, 0) == null ? passiveGroup : null;
public UUID getEffectiveEdgeGroupId(TrackGraph graph) {
return singleSignalGroup == null ? null : singleSignalGroup.equals(passiveGroup) ? graph.id : singleSignalGroup;
}
public <T extends TrackEdgePoint> void addPoint(TrackNode node1, TrackNode node2, TrackEdge edge,
TrackEdgePoint point) {
public void removePoint(TrackGraph graph, TrackEdgePoint point) {
points.remove(point);
if (point.getType() == EdgePointType.SIGNAL) {
boolean noSignalsRemaining = next(point.getType(), 0) == null;
setSingleSignalGroup(graph, noSignalsRemaining ? passiveGroup : null);
}
}
public <T extends TrackEdgePoint> void addPoint(TrackGraph graph, TrackEdgePoint point) {
if (point.getType() == EdgePointType.SIGNAL)
singleSignalGroup = null;
double locationOn = point.getLocationOn(node1, node2, edge);
setSingleSignalGroup(graph, null);
double locationOn = point.getLocationOn(edge);
int i = 0;
for (; i < points.size(); i++)
if (points.get(i)
.getLocationOn(node1, node2, edge) > locationOn)
.getLocationOn(edge) > locationOn)
break;
points.add(i, point);
}
@Nullable
@SuppressWarnings("unchecked")
public <T extends TrackEdgePoint> T next(EdgePointType<T> type, TrackNode node1, TrackNode node2, TrackEdge edge,
double minPosition) {
public <T extends TrackEdgePoint> T next(EdgePointType<T> type, double minPosition) {
for (TrackEdgePoint point : points)
if (point.getType() == type && point.getLocationOn(node1, node2, edge) > minPosition)
if (point.getType() == type && point.getLocationOn(edge) > minPosition)
return (T) point;
return null;
}
@Nullable
public TrackEdgePoint next(TrackNode node1, TrackNode node2, TrackEdge edge, double minPosition) {
public TrackEdgePoint next(double minPosition) {
for (TrackEdgePoint point : points)
if (point.getLocationOn(node1, node2, edge) > minPosition)
if (point.getLocationOn(edge) > minPosition)
return point;
return null;
}
@Nullable
public <T extends TrackEdgePoint> T get(EdgePointType<T> type, TrackNode node1, TrackNode node2, TrackEdge edge,
double exactPosition) {
T next = next(type, node1, node2, edge, exactPosition - .5f);
if (next != null && Mth.equal(next.getLocationOn(node1, node2, edge), exactPosition))
public <T extends TrackEdgePoint> T get(EdgePointType<T> type, double exactPosition) {
T next = next(type, exactPosition - .5f);
if (next != null && Mth.equal(next.getLocationOn(edge), exactPosition))
return next;
return null;
}
@ -103,11 +193,13 @@ public class EdgeData {
.toString());
return tag;
}));
if (hasIntersections())
nbt.put("Intersections", NBTHelper.writeCompoundList(intersections, TrackEdgeIntersection::write));
return nbt;
}
public static EdgeData read(CompoundTag nbt, TrackGraph graph) {
EdgeData data = new EdgeData();
public static EdgeData read(CompoundTag nbt, TrackEdge edge, TrackGraph graph) {
EdgeData data = new EdgeData(edge);
if (nbt.contains("SignalGroup"))
data.singleSignalGroup = nbt.getUUID("SignalGroup");
else if (!nbt.contains("PassiveGroup"))
@ -123,6 +215,9 @@ public class EdgeData {
if (point != null)
data.points.add(point);
});
if (nbt.contains("Intersections"))
data.intersections =
NBTHelper.readCompoundList(nbt.getList("Intersections", Tag.TAG_COMPOUND), TrackEdgeIntersection::read);
return data;
}

View file

@ -22,7 +22,7 @@ public class EdgePointManager {
TrackNode node2 = startNodes.get(!front);
TrackEdge startEdge = startEdges.get(front);
startEdge.getEdgeData()
.addPoint(node1, node2, startEdge, point);
.addPoint(graph, point);
Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge);
}
}
@ -37,7 +37,7 @@ public class EdgePointManager {
if (trackEdge == null)
return;
trackEdge.getEdgeData()
.removePoint(l1, l2, trackEdge, point);
.removePoint(graph, point);
Create.RAILWAYS.sync.edgeDataChanged(graph, l1, l2, trackEdge);
}, startNodes.swap());
}

View file

@ -0,0 +1,57 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint;
import java.util.UUID;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
import com.simibubi.create.foundation.utility.Couple;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
public class TrackEdgeIntersection {
public double location;
public Couple<TrackNodeLocation> target;
public double targetLocation;
public UUID groupId;
public UUID id;
public TrackEdgeIntersection() {
id = UUID.randomUUID();
}
public boolean isNear(double location) {
return Math.abs(location - this.location) < 1 / 32f;
}
public boolean targets(TrackNodeLocation target1, TrackNodeLocation target2) {
return target1.equals(target.getFirst()) && target2.equals(target.getSecond())
|| target1.equals(target.getSecond()) && target2.equals(target.getFirst());
}
public CompoundTag write() {
CompoundTag nbt = new CompoundTag();
nbt.putUUID("Id", id);
if (groupId != null)
nbt.putUUID("GroupId", groupId);
nbt.putDouble("Location", location);
nbt.putDouble("TargetLocation", targetLocation);
nbt.put("TargetEdge", target.serializeEach(loc -> NbtUtils.writeBlockPos(new BlockPos(loc))));
return nbt;
}
public static TrackEdgeIntersection read(CompoundTag nbt) {
TrackEdgeIntersection intersection = new TrackEdgeIntersection();
intersection.id = nbt.getUUID("Id");
if (nbt.contains("GroupId"))
intersection.groupId = nbt.getUUID("GroupId");
intersection.location = nbt.getDouble("Location");
intersection.targetLocation = nbt.getDouble("TargetLocation");
intersection.target = Couple.deserializeEach(nbt.getList("TargetEdge", Tag.TAG_COMPOUND),
tag -> TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(tag)));
return intersection;
}
}

View file

@ -149,13 +149,13 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
if (edge == null)
return null;
double length = edge.getLength(node1, node2);
double length = edge.getLength();
CompoundTag data = migrationData;
migrationData = null;
{
orthogonal = targetBezier == null;
Vec3 direction = edge.getDirection(node1, node2, true);
Vec3 direction = edge.getDirection(true);
int nonZeroComponents = 0;
for (Axis axis : Iterate.axes)
nonZeroComponents += direction.get(axis) != 0 ? 1 : 0;
@ -165,7 +165,7 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
EdgeData signalData = edge.getEdgeData();
if (signalData.hasPoints()) {
for (EdgePointType<?> otherType : EdgePointType.TYPES.values()) {
TrackEdgePoint otherPoint = signalData.get(otherType, node1, node2, edge, loc.position);
TrackEdgePoint otherPoint = signalData.get(otherType, loc.position);
if (otherPoint == null)
continue;
if (otherType != edgePointType) {

View file

@ -202,7 +202,7 @@ public class TrackTargetingBlockItem extends BlockItem {
double edgePosition = location.position;
for (TrackEdgePoint edgePoint : edgeData.getPoints()) {
double otherEdgePosition = edgePoint.getLocationOn(nodes.getFirst(), nodes.getSecond(), edge);
double otherEdgePosition = edgePoint.getLocationOn(edge);
double distance = Math.abs(edgePosition - otherEdgePosition);
if (distance > .75)
continue;

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.logistics.trains.management.edgePoint;
import com.google.common.base.Objects;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.GraphLocation;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour.RenderedTrackOverlayType;
@ -47,9 +48,13 @@ public class TrackTargetingClient {
BezierTrackPointLocation hoveredBezier = null;
ItemStack stack = player.getMainHandItem();
if (stack.getItem() instanceof TrackTargetingBlockItem ttbi)
if (stack.getItem()instanceof TrackTargetingBlockItem ttbi)
type = ttbi.getType(stack);
if (type == EdgePointType.SIGNAL)
Create.RAILWAYS.sided(null)
.tickSignalOverlay();
boolean alreadySelected = stack.hasTag() && stack.getTag()
.contains("SelectedPos");
@ -78,7 +83,7 @@ public class TrackTargetingClient {
BlockHitResult blockHitResult = (BlockHitResult) hitResult;
BlockPos pos = blockHitResult.getBlockPos();
BlockState blockState = mc.level.getBlockState(pos);
if (blockState.getBlock() instanceof ITrackBlock track) {
if (blockState.getBlock()instanceof ITrackBlock track) {
direction = track.getNearestTrackAxis(mc.level, pos, blockState, lookAngle)
.getSecond() == AxisDirection.POSITIVE;
hovered = pos;

View file

@ -0,0 +1,52 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint.signal;
import com.simibubi.create.foundation.utility.Color;
public enum EdgeGroupColor {
YELLOW(0xEBC255),
GREEN(0x51C054),
BLUE(0x5391E1),
ORANGE(0xE36E36),
LAVENDER(0xCB92BA),
RED(0xA43538),
CYAN(0x6EDAD9),
BROWN(0xA17C58),
WHITE(0xE5E1DC)
;
private Color color;
private int mask;
private EdgeGroupColor(int color) {
this.color = new Color(color);
mask = 1 << ordinal();
}
public int strikeFrom(int mask) {
if (this == WHITE)
return mask;
return mask | this.mask;
}
public Color get() {
return color;
}
public static EdgeGroupColor getDefault() {
return values()[0];
}
public static EdgeGroupColor findNextAvailable(int mask) {
EdgeGroupColor[] values = values();
for (int i = 0; i < values.length; i++) {
if ((mask & 1) == 0)
return values[i];
mask = mask >> 1;
}
return WHITE;
}
}

View file

@ -45,9 +45,32 @@ public class SignalBoundary extends TrackEdgePoint {
cachedStates = Couple.create(() -> SignalState.INVALID);
}
public void setGroup(TrackNode side, UUID groupId) {
boolean primary = isPrimary(side);
public void setGroup(boolean primary, UUID groupId) {
UUID previous = groups.get(primary);
groups.set(primary, groupId);
UUID opposite = groups.get(!primary);
Map<UUID, SignalEdgeGroup> signalEdgeGroups = Create.RAILWAYS.signalEdgeGroups;
if (opposite != null && signalEdgeGroups.containsKey(opposite)) {
SignalEdgeGroup oppositeGroup = signalEdgeGroups.get(opposite);
if (previous != null)
oppositeGroup.removeAdjacent(previous);
if (groupId != null)
oppositeGroup.putAdjacent(groupId);
}
if (groupId != null && signalEdgeGroups.containsKey(groupId)) {
SignalEdgeGroup group = signalEdgeGroups.get(groupId);
if (opposite != null)
group.putAdjacent(opposite);
}
}
public void setGroupAndUpdate(TrackNode side, UUID groupId) {
boolean primary = isPrimary(side);
setGroup(primary, groupId);
sidesToUpdate.set(primary, false);
chainedSignals.set(primary, null);
}
@ -60,8 +83,12 @@ public class SignalBoundary extends TrackEdgePoint {
@Override
public void invalidate(LevelAccessor level) {
blockEntities.forEach(s -> s.forEach(pos -> invalidateAt(level, pos)));
groups.forEach(uuid -> {
if (Create.RAILWAYS.signalEdgeGroups.remove(uuid) != null)
Create.RAILWAYS.sync.edgeGroupRemoved(uuid);
});
}
@Override
public boolean canCoexistWith(EdgePointType<?> otherType, boolean front) {
return otherType == getType();

View file

@ -1,51 +1,147 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint.signal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import org.apache.commons.lang3.mutable.MutableInt;
import com.google.common.base.Predicates;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
public class SignalEdgeGroup {
public UUID id;
public Color color;
public EdgeGroupColor color;
public Set<Train> trains;
public SignalBoundary reserved;
public Map<UUID, UUID> intersecting;
public Set<SignalEdgeGroup> intersectingResolved;
public Set<UUID> adjacent;
public SignalEdgeGroup(UUID id) {
this.id = id;
color = Color.rainbowColor(Create.RANDOM.nextInt());
trains = new HashSet<>();
adjacent = new HashSet<>();
intersecting = new HashMap<>();
intersectingResolved = new HashSet<>();
color = EdgeGroupColor.getDefault();
}
public boolean isOccupiedUnless(Train train) {
if (intersectingResolved.isEmpty())
walkIntersecting(intersectingResolved::add);
for (SignalEdgeGroup group : intersectingResolved)
if (group.isThisOccupiedUnless(train))
return true;
return false;
}
private boolean isThisOccupiedUnless(Train train) {
return reserved != null || trains.size() > 1 || !trains.contains(train) && !trains.isEmpty();
}
public boolean isOccupiedUnless(SignalBoundary boundary) {
if (intersectingResolved.isEmpty())
walkIntersecting(intersectingResolved::add);
for (SignalEdgeGroup group : intersectingResolved)
if (group.isThisOccupiedUnless(boundary))
return true;
return false;
}
private boolean isThisOccupiedUnless(SignalBoundary boundary) {
return !trains.isEmpty() || reserved != null && reserved != boundary;
}
public boolean isOccupied() {
return !trains.isEmpty() || reserved != null;
public void putIntersection(UUID intersectionId, UUID targetGroup) {
intersecting.put(intersectionId, targetGroup);
walkIntersecting(g -> g.intersectingResolved.clear());
resolveColor();
}
public void removeIntersection(UUID intersectionId) {
walkIntersecting(g -> g.intersectingResolved.clear());
UUID removed = intersecting.remove(intersectionId);
SignalEdgeGroup other = Create.RAILWAYS.signalEdgeGroups.get(removed);
if (other != null)
other.intersecting.remove(intersectionId);
resolveColor();
}
public void putAdjacent(UUID adjacent) {
this.adjacent.add(adjacent);
}
public void removeAdjacent(UUID adjacent) {
this.adjacent.remove(adjacent);
}
public void resolveColor() {
if (intersectingResolved.isEmpty())
walkIntersecting(intersectingResolved::add);
MutableInt mask = new MutableInt(0);
intersectingResolved.forEach(group -> group.adjacent.stream()
.map(Create.RAILWAYS.signalEdgeGroups::get)
.filter(Objects::nonNull)
.filter(Predicates.not(intersectingResolved::contains))
.forEach(adjacent -> mask.setValue(adjacent.color.strikeFrom(mask.getValue()))));
EdgeGroupColor newColour = EdgeGroupColor.findNextAvailable(mask.getValue());
if (newColour == color)
return;
walkIntersecting(group -> Create.RAILWAYS.sync.edgeGroupCreated(group.id, group.color = newColour));
Create.RAILWAYS.markTracksDirty();
}
private void walkIntersecting(Consumer<SignalEdgeGroup> callback) {
walkIntersectingRec(new HashSet<>(), callback);
}
private void walkIntersectingRec(Set<SignalEdgeGroup> visited, Consumer<SignalEdgeGroup> callback) {
if (!visited.add(this))
return;
callback.accept(this);
for (UUID uuid : intersecting.values()) {
SignalEdgeGroup group = Create.RAILWAYS.signalEdgeGroups.get(uuid);
if (group != null)
group.walkIntersectingRec(visited, callback);
}
}
public static SignalEdgeGroup read(CompoundTag tag) {
SignalEdgeGroup group = new SignalEdgeGroup(tag.getUUID("Id"));
group.color = new Color(tag.getInt("Color"));
group.color = NBTHelper.readEnum(tag, "Color", EdgeGroupColor.class);
NBTHelper.iterateCompoundList(tag.getList("Connected", Tag.TAG_COMPOUND),
nbt -> group.intersecting.put(nbt.getUUID("Key"), nbt.getUUID("Value")));
return group;
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.putUUID("Id", id);
tag.putInt("Color", color.getRGB());
NBTHelper.writeEnum(tag, "Color", color);
tag.put("Connected", NBTHelper.writeCompoundList(intersecting.entrySet(), e -> {
CompoundTag nbt = new CompoundTag();
nbt.putUUID("Key", e.getKey());
nbt.putUUID("Value", e.getValue());
return nbt;
}));
return tag;
}

View file

@ -1,11 +1,12 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint.signal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.networking.SimplePacketBase;
@ -14,20 +15,30 @@ import net.minecraftforge.network.NetworkEvent.Context;
public class SignalEdgeGroupPacket extends SimplePacketBase {
Collection<UUID> ids;
List<UUID> ids;
List<EdgeGroupColor> colors;
boolean add;
public SignalEdgeGroupPacket(Collection<UUID> ids, boolean add) {
public SignalEdgeGroupPacket(UUID id, EdgeGroupColor color) {
this(ImmutableList.of(id), ImmutableList.of(color), true);
}
public SignalEdgeGroupPacket(List<UUID> ids, List<EdgeGroupColor> colors, boolean add) {
this.ids = ids;
this.colors = colors;
this.add = add;
}
public SignalEdgeGroupPacket(FriendlyByteBuf buffer) {
ids = new ArrayList<>();
colors = new ArrayList<>();
add = buffer.readBoolean();
int size = buffer.readVarInt();
for (int i = 0; i < size; i++)
ids.add(buffer.readUUID());
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
colors.add(EdgeGroupColor.values()[buffer.readVarInt()]);
}
@Override
@ -35,6 +46,8 @@ public class SignalEdgeGroupPacket extends SimplePacketBase {
buffer.writeBoolean(add);
buffer.writeVarInt(ids.size());
ids.forEach(buffer::writeUUID);
buffer.writeVarInt(colors.size());
colors.forEach(c -> buffer.writeVarInt(c.ordinal()));
}
@Override
@ -42,11 +55,18 @@ public class SignalEdgeGroupPacket extends SimplePacketBase {
context.get()
.enqueueWork(() -> {
Map<UUID, SignalEdgeGroup> signalEdgeGroups = CreateClient.RAILWAYS.signalEdgeGroups;
int i = 0;
for (UUID id : ids) {
if (add)
signalEdgeGroups.put(id, new SignalEdgeGroup(id));
else
if (!add) {
signalEdgeGroups.remove(id);
continue;
}
SignalEdgeGroup group = new SignalEdgeGroup(id);
signalEdgeGroups.put(id, group);
if (colors.size() > i)
group.color = colors.get(i);
i++;
}
});
context.get()

View file

@ -36,12 +36,21 @@ public class SignalPropagator {
UUID id = signal.groups.get(front);
if (Create.RAILWAYS.signalEdgeGroups.remove(id) != null)
Create.RAILWAYS.sync.edgeGroupRemoved(id);
walkSignals(graph, signal, front, pair -> {
TrackNode node1 = pair.getFirst();
SignalBoundary boundary = pair.getSecond();
boundary.queueUpdate(node1);
return false;
}, Predicates.alwaysFalse(), false);
}, signalData -> {
if (!signalData.hasSignalBoundaries()) {
signalData.setSingleSignalGroup(graph, EdgeData.passiveGroup);
return true;
}
return false;
}, false);
}
}
@ -53,7 +62,15 @@ public class SignalPropagator {
SignalBoundary boundary = pair.getSecond();
boundary.queueUpdate(node1);
return false;
}, Predicates.alwaysFalse(), false);
}, signalData -> {
if (!signalData.hasSignalBoundaries()) {
signalData.setSingleSignalGroup(graph, EdgeData.passiveGroup);
return true;
}
return false;
}, false);
}
public static void propagateSignalGroup(TrackGraph graph, SignalBoundary signal, boolean front) {
@ -63,8 +80,7 @@ public class SignalPropagator {
SignalEdgeGroup group = new SignalEdgeGroup(UUID.randomUUID());
UUID groupId = group.id;
globalGroups.put(groupId, group);
sync.edgeGroupCreated(groupId);
signal.groups.set(front, groupId);
signal.setGroup(front, groupId);
sync.pointAdded(graph, signal);
walkSignals(graph, signal, front, pair -> {
@ -74,18 +90,22 @@ public class SignalPropagator {
if (currentGroup != null)
if (globalGroups.remove(currentGroup) != null)
sync.edgeGroupRemoved(currentGroup);
boundary.setGroup(node1, groupId);
boundary.setGroupAndUpdate(node1, groupId);
sync.pointAdded(graph, boundary);
return true;
}, signalData -> {
if (signalData.singleSignalGroup != null)
if (globalGroups.remove(signalData.singleSignalGroup) != null)
sync.edgeGroupRemoved(signalData.singleSignalGroup);
signalData.singleSignalGroup = groupId;
UUID singleSignalGroup = signalData.getSingleSignalGroup();
if (singleSignalGroup != null)
if (globalGroups.remove(singleSignalGroup) != null)
sync.edgeGroupRemoved(singleSignalGroup);
signalData.setSingleSignalGroup(graph, groupId);
return true;
}, false);
group.resolveColor();
sync.edgeGroupCreated(groupId, group.color);
}
public static Map<UUID, Boolean> collectChainedSignals(TrackGraph graph, SignalBoundary signal, boolean front) {
@ -117,14 +137,18 @@ public class SignalPropagator {
if (!forCollection) {
notifyTrains(graph, startEdge, oppositeEdge);
startEdge.getEdgeData()
.refreshIntersectingSignalGroups(graph);
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));
.next(EdgePointType.SIGNAL, signal.getLocationOn(startEdge));
if (immediateBoundary != null) {
boundaryCallback.test(Pair.of(node1, immediateBoundary));
if (boundaryCallback.test(Pair.of(node1, immediateBoundary)))
startEdge.getEdgeData()
.refreshIntersectingSignalGroups(graph);
return;
}
@ -159,8 +183,8 @@ public class SignalPropagator {
if (forCollection) {
Vec3 currentDirection = graph.getConnectionsFrom(prevNode)
.get(currentNode)
.getDirection(prevNode, currentNode, false);
Vec3 newDirection = edge.getDirection(currentNode, nextNode, true);
.getDirection(false);
Vec3 newDirection = edge.getDirection(true);
if (currentDirection.dot(newDirection) < 3 / 4f)
continue;
}
@ -175,20 +199,21 @@ public class SignalPropagator {
// no boundary- update group of edge
if (!signalData.hasSignalBoundaries()) {
if (!nonBoundaryCallback.test(signalData))
continue;
notifyTrains(graph, currentEdge);
Create.RAILWAYS.sync.edgeDataChanged(graph, currentNode, nextNode, edge, oppositeEdge);
if (nonBoundaryCallback.test(signalData)) {
notifyTrains(graph, currentEdge);
Create.RAILWAYS.sync.edgeDataChanged(graph, currentNode, nextNode, edge, oppositeEdge);
}
continue;
}
// other/own boundary found
SignalBoundary nextBoundary =
signalData.next(EdgePointType.SIGNAL, currentNode, nextNode, currentEdge, 0);
SignalBoundary nextBoundary = signalData.next(EdgePointType.SIGNAL, 0);
if (nextBoundary == null)
continue;
if (boundaryCallback.test(Pair.of(currentNode, nextBoundary))) {
notifyTrains(graph, edge, oppositeEdge);
currentEdge.getEdgeData()
.refreshIntersectingSignalGroups(graph);
Create.RAILWAYS.sync.edgeDataChanged(graph, currentNode, nextNode, edge, oppositeEdge);
}
continue EdgeWalk;

View file

@ -71,8 +71,8 @@ public abstract class TrackEdgePoint {
this.position = position;
}
public double getLocationOn(TrackNode node1, TrackNode node2, TrackEdge edge) {
return isPrimary(node1) ? edge.getLength(node1, node2) - position : position;
public double getLocationOn(TrackEdge edge) {
return isPrimary(edge.node1) ? edge.getLength() - position : position;
}
public boolean canNavigateVia(TrackNode side) {

View file

@ -316,7 +316,7 @@ public class StationTileEntity extends SmartTileEntity {
}
BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos));
if (potentialBogeyState.getBlock() instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) {
if (potentialBogeyState.getBlock()instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) {
bogeyTypes[bogeyIndex] = bogey;
bogeyLocations[bogeyIndex] = i;
bogeyIndex++;
@ -449,7 +449,7 @@ public class StationTileEntity extends SmartTileEntity {
TrackNode otherNode = entry.getKey();
if (edge.isTurn())
continue;
Vec3 edgeDirection = edge.getDirection(node, otherNode, true);
Vec3 edgeDirection = edge.getDirection(true);
if (Mth.equal(edgeDirection.normalize()
.dot(directionVec), -1d))
secondNode = otherNode;
@ -638,7 +638,7 @@ public class StationTileEntity extends SmartTileEntity {
return true;
BlockState target = edgePoint.getTrackBlockState();
if (!(target.getBlock() instanceof ITrackBlock def))
if (!(target.getBlock()instanceof ITrackBlock def))
return false;
Vec3 axis = null;

View file

@ -302,6 +302,20 @@ public class VecHelper {
.add(p2.scale(3 * t * t));
}
@Nullable
public static double[] intersectRanged(Vec3 p1, Vec3 q1, Vec3 p2, Vec3 q2, Axis plane) {
Vec3 pDiff = p2.subtract(p1);
Vec3 qDiff = q2.subtract(q1);
double[] intersect = intersect(p1, q1, pDiff.normalize(), qDiff.normalize(), plane);
if (intersect == null)
return null;
if (intersect[0] < 0 || intersect[1] < 0)
return null;
if (intersect[0] > pDiff.length() || intersect[1] > qDiff.length())
return null;
return intersect;
}
@Nullable
public static double[] intersect(Vec3 p1, Vec3 p2, Vec3 r, Vec3 s, Axis plane) {
if (plane == Axis.X) {
@ -310,14 +324,14 @@ public class VecHelper {
r = new Vec3(r.y, 0, r.z);
s = new Vec3(s.y, 0, s.z);
}
if (plane == Axis.Z) {
p1 = new Vec3(p1.x, 0, p1.y);
p2 = new Vec3(p2.x, 0, p2.y);
r = new Vec3(r.x, 0, r.y);
s = new Vec3(s.x, 0, s.y);
}
Vec3 qminusp = p2.subtract(p1);
double rcs = r.x * s.z - r.z * s.x;
if (Mth.equal(rcs, 0))