diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java b/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java index f053ca2a9..2ddf685bd 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java @@ -57,7 +57,7 @@ public class BezierConnection implements Iterable { } 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) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java b/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java index 71c1f257e..cabfe2418 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java @@ -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 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 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) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/RailwaySavedData.java b/src/main/java/com/simibubi/create/content/logistics/trains/RailwaySavedData.java index 2617030a5..ca275e246 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/RailwaySavedData.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/RailwaySavedData.java @@ -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 getTrackNetworks() { return trackNetworks; } - + public Map getTrains() { return trains; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackEdge.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackEdge.java index 311bcc214..d21eca81f 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackEdge.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackEdge.java @@ -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 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 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 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; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java index 60a8fd816..712f78da4 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java @@ -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 nodesById; Map> connectionsByNode; EdgePointStorage edgePoints; + Map, TrackGraphBounds> bounds; + + List 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 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 connections = connectionsByNode.remove(removed); - for (TrackEdge trackEdge : connections.values()) - for (TrackEdgePoint point : trackEdge.getEdgeData() - .getPoints()) { + for (Entry 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 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 from1 = getConnectionsFrom(node1); + if (from1 != null) { + TrackEdge edge = from1.get(node2); + if (edge != null) + edge.getEdgeData() + .removeIntersection(this, intersection.id); + } + + Map 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 trains = Create.RAILWAYS.trains; for (Iterator 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 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 connections = graph.connectionsByNode.get(otherNode1); + if (connections == null) + continue; + for (Entry 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 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 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 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 reserved = new HashSet<>(); - Set 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 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 map = connectionsByNode.get(node); - if (map == null) - continue; - - int hashCode = node.hashCode(); - for (Entry 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 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 map = connectionsByNode.get(node); - if (map == null) - continue; - - int hashCode = node.hashCode(); - for (Entry 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 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 map = connectionsByNode.get(node); - if (map == null) - continue; - - int hashCode = node.hashCode(); - for (Entry 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; - } - } - } - } - } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphBounds.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphBounds.java new file mode 100644 index 000000000..9e167f565 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphBounds.java @@ -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 beziers; + + // TODO: filter nodes by dimensional coordinate + public TrackGraphBounds(TrackGraph graph, ResourceKey dimension) { + beziers = new ArrayList<>(); + box = null; + + for (TrackNode node : graph.nodes.values()) { + include(node); + Map 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); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphHelper.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphHelper.java index e858c0a95..f99444e7f 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphHelper.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphHelper.java @@ -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 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; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSync.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSync.java index b65602bec..860f1c296 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSync.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSync.java @@ -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 ids, ServerPlayer player) { - AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SignalEdgeGroupPacket(ids, true)); + public void sendEdgeGroups(List ids, List 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)); } // diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSyncPacket.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSyncPacket.java index 444eaa564..22a0eccbc 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSyncPacket.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSyncPacket.java @@ -169,8 +169,14 @@ public class TrackGraphSyncPacket extends TrackGraphPacket { for (Pair, TrackEdge> pair : addedEdges) { Couple 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 points = edgeData.getPoints(); edge.edgeData = edgeData; @@ -232,9 +238,9 @@ public class TrackGraphSyncPacket extends TrackGraphPacket { List 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)); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphVisualizer.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphVisualizer.java new file mode 100644 index 000000000..a4ef2605d --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphVisualizer.java @@ -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 allGroups = Create.RAILWAYS.sided(null).signalEdgeGroups; + float width = 1 / 8f; + + for (Entry 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 map = graph.connectionsByNode.get(node); + if (map == null) + continue; + + int hashCode = node.hashCode(); + for (Entry 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 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 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 map = graph.connectionsByNode.get(node); + if (map == null) + continue; + + int hashCode = node.hashCode(); + for (Entry 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; + } + } + } + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackPropagator.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackPropagator.java index 24ab3ee38..0b0d608b9 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackPropagator.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackPropagator.java @@ -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 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) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageSyncData.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageSyncData.java index 4d15a4f04..be3b609a1 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageSyncData.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageSyncData.java @@ -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, 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> validTargets = new ArrayList<>(); Map connectionsFrom = graph.getConnectionsFrom(node2); for (Entry 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))); } } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java index f939879ee..2ada1a287 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java @@ -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> validTargets = new ArrayList<>(); Map connectionsFrom = graph.getConnectionsFrom(node2); for (Entry 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 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"); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java index 2d7942a8c..f3be96ef6 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java @@ -129,7 +129,7 @@ public class Train { addToSignalGroups(occupiedSignalBlocks.keySet()); return; } - + if (updateSignalBlocks) { updateSignalBlocks = false; collectInitiallyOccupiedSignalBlocks(); @@ -714,15 +714,15 @@ public class Train { MutableObject 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> getEndpointEdges() { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java index 56a9bf31a..4def20c9a 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java @@ -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; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java index f47778ca0..5760f8119 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java @@ -153,9 +153,9 @@ public class TravellingPoint { Entry best = null; for (Entry 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> 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 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)); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgeData.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgeData.java index 27973def3..9c45d8c7d 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgeData.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgeData.java @@ -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 points; + private List 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 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 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 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 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 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 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 next(EdgePointType type, TrackNode node1, TrackNode node2, TrackEdge edge, - double minPosition) { + public T next(EdgePointType 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 get(EdgePointType 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 get(EdgePointType 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; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointManager.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointManager.java index 3c0693240..ecca7e8d4 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointManager.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointManager.java @@ -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()); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackEdgeIntersection.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackEdgeIntersection.java new file mode 100644 index 000000000..16a523323 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackEdgeIntersection.java @@ -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 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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java index 15b681658..55c8d62a3 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java @@ -149,13 +149,13 @@ public class TrackTargetingBehaviour 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 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) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java index 2466ee280..97641261e 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java @@ -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; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingClient.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingClient.java index 56c085d0b..0b0293aeb 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingClient.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingClient.java @@ -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; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/EdgeGroupColor.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/EdgeGroupColor.java new file mode 100644 index 000000000..6c0161ec1 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/EdgeGroupColor.java @@ -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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalBoundary.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalBoundary.java index 02b27fa7d..f1aaa1e85 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalBoundary.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalBoundary.java @@ -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 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(); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalEdgeGroup.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalEdgeGroup.java index b1b530017..edbde3214 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalEdgeGroup.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalEdgeGroup.java @@ -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 trains; public SignalBoundary reserved; + public Map intersecting; + public Set intersectingResolved; + public Set 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 callback) { + walkIntersectingRec(new HashSet<>(), callback); + } + + private void walkIntersectingRec(Set visited, Consumer 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; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalEdgeGroupPacket.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalEdgeGroupPacket.java index b948e813d..8918a6e77 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalEdgeGroupPacket.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalEdgeGroupPacket.java @@ -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 ids; + List ids; + List colors; boolean add; - public SignalEdgeGroupPacket(Collection ids, boolean add) { + public SignalEdgeGroupPacket(UUID id, EdgeGroupColor color) { + this(ImmutableList.of(id), ImmutableList.of(color), true); + } + + public SignalEdgeGroupPacket(List ids, List 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 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() diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalPropagator.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalPropagator.java index e6cc4448b..cc17d7fbd 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalPropagator.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalPropagator.java @@ -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 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; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/TrackEdgePoint.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/TrackEdgePoint.java index 99b519972..3e513dd7a 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/TrackEdgePoint.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/TrackEdgePoint.java @@ -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) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java index 0ec3d0045..6bf62cceb 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java @@ -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; diff --git a/src/main/java/com/simibubi/create/foundation/utility/VecHelper.java b/src/main/java/com/simibubi/create/foundation/utility/VecHelper.java index 8260ea8d5..ffb52b2b3 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/VecHelper.java +++ b/src/main/java/com/simibubi/create/foundation/utility/VecHelper.java @@ -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))