From 3362e6a05e40315c406149c4d71aaacfac33f397 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Mon, 28 Feb 2022 22:52:14 +0100 Subject: [PATCH] Signal Station - Refactored Stations and Signals into a more unified and expandable edge point abstraction - Trains no longer depart a station when a red signal is close by - Fixed inconsistent pathfinding when stations, signals and trains are sharing a graph edge --- .../trains/GlobalRailwayManager.java | 25 +- .../content/logistics/trains/TrackGraph.java | 232 +++++++++--------- .../logistics/trains/TrackGraphSync.java | 41 +++- .../logistics/trains/TrackPropagator.java | 41 ++-- .../logistics/trains/entity/Navigation.java | 117 ++++----- .../logistics/trains/entity/Train.java | 11 +- .../trains/entity/TravellingPoint.java | 9 +- .../trains/management/GlobalStation.java | 55 ++--- .../trains/management/ScheduleRuntime.java | 3 +- .../trains/management/StationBlock.java | 6 +- .../trains/management/StationEditPacket.java | 9 +- .../trains/management/StationRenderer.java | 4 +- .../trains/management/StationTileEntity.java | 135 +++------- .../management/TrackTargetingBehaviour.java | 128 +++++++++- .../edgePoint/EdgePointManager.java | 40 +++ .../edgePoint/EdgePointStorage.java | 92 +++++++ .../management/edgePoint/EdgePointType.java | 46 ++++ .../management/schedule/ScheduledDelay.java | 2 +- .../schedule/StationPoweredCondition.java | 2 +- .../schedule/StationUnloadedCondition.java | 2 +- .../trains/management/signal/EdgeData.java | 161 +++++------- .../management/signal/SignalBoundary.java | 65 +++-- .../management/signal/SignalPropagator.java | 36 +-- .../management/signal/SignalRenderer.java | 2 +- .../management/signal/SignalTileEntity.java | 113 ++------- .../signal/SingleTileEdgePoint.java | 55 +++++ .../management/signal/TrackEdgePoint.java | 77 +++++- .../logistics/trains/track/TrackBlock.java | 14 +- .../trains/track/TrackTileEntity.java | 1 + .../command/DumpRailwaysCommand.java | 7 +- .../tileEntity/SmartTileEntity.java | 3 +- .../create/foundation/utility/Couple.java | 9 + 32 files changed, 884 insertions(+), 659 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointManager.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointStorage.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointType.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SingleTileEdgePoint.java 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 c41fc6562..a484fc3ff 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 @@ -1,6 +1,9 @@ package com.simibubi.create.content.logistics.trains; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -118,6 +121,16 @@ public class GlobalRailwayManager { return null; } + public List getGraphs(LevelAccessor level, TrackNodeLocation vertex) { + if (trackNetworks == null) + return Collections.emptyList(); + ArrayList intersecting = new ArrayList<>(); + for (TrackGraph railGraph : trackNetworks.values()) + if (railGraph.locateNode(vertex) != null) + intersecting.add(railGraph); + return intersecting; + } + public void tick(Level level) { ResourceLocation location2 = DimensionType.OVERWORLD_LOCATION.location(); ResourceLocation location = level.dimension() @@ -131,10 +144,8 @@ public class GlobalRailwayManager { } for (TrackGraph graph : trackNetworks.values()) - graph.getSignals() - .forEach(sb -> sb.tick(graph)); - - for (Train train : trains.values()) + graph.tickPoints(); + for (Train train : trains.values()) train.earlyTick(level); for (Train train : trains.values()) train.tick(level); @@ -143,10 +154,14 @@ public class GlobalRailwayManager { trackNetworks.values() .forEach(TrackGraph::debugViewSignalData); } + if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && AllKeys.altDown()) { + trackNetworks.values() + .forEach(TrackGraph::debugViewNodes); + } } public void clientTick() { - if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J)) { + if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && !AllKeys.altDown()) { trackNetworks.values() .forEach(TrackGraph::debugViewNodes); } 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 9efd5891d..2e95d4c45 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 @@ -24,12 +24,14 @@ 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.GlobalStation; +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.signal.EdgeData; import com.simibubi.create.content.logistics.trains.management.signal.SignalBoundary; import com.simibubi.create.content.logistics.trains.management.signal.SignalEdgeGroup; -import com.simibubi.create.content.logistics.trains.management.signal.SignalPropagator; +import com.simibubi.create.content.logistics.trains.management.signal.TrackEdgePoint; 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; @@ -54,67 +56,47 @@ public class TrackGraph { Map nodes; Map nodesById; Map> connectionsByNode; - - private Map stations; - private Map signals; + EdgePointStorage edgePoints; public TrackGraph() { this(UUID.randomUUID()); } public TrackGraph(UUID graphID) { - id = graphID; + setId(graphID); nodes = new HashMap<>(); nodesById = new HashMap<>(); connectionsByNode = new IdentityHashMap<>(); - color = Color.rainbowColor(new Random(graphID.getLeastSignificantBits()).nextInt()); - stations = new HashMap<>(); - signals = new HashMap<>(); + edgePoints = new EdgePointStorage(); } // - @Nullable - public GlobalStation getStation(UUID id) { - return stations.get(id); - } - - @Nullable - public SignalBoundary getSignal(UUID id) { - return signals.get(id); - } - - public Collection getStations() { - return stations.values(); - } - - public Collection getSignals() { - return signals.values(); - } - - public void addStation(GlobalStation station) { - stations.put(station.id, station); - SignalPropagator.onEdgePointAdded(this, station, GlobalStation.class); + public void addPoint(EdgePointType type, T point) { + edgePoints.put(type, point); + EdgePointManager.onEdgePointAdded(this, point, type); markDirty(); } - public void addSignal(SignalBoundary signal) { - signals.put(signal.id, signal); - SignalPropagator.onEdgePointAdded(this, signal, SignalBoundary.class); - markDirty(); + public T getPoint(EdgePointType type, UUID id) { + return edgePoints.get(type, id); } - public void removeStation(UUID id) { - stations.remove(id); - markDirty(); + public Collection getPoints(EdgePointType type) { + return edgePoints.values(type); } - public void removeSignal(UUID id) { - SignalBoundary signal = signals.remove(id); - if (signal == null) - return; - SignalPropagator.onSignalRemoved(this, signal); + public T removePoint(EdgePointType type, UUID id) { + T removed = edgePoints.remove(type, id); + if (removed == null) + return null; + EdgePointManager.onEdgePointRemoved(this, removed, type); markDirty(); + return removed; + } + + public void tickPoints() { + edgePoints.tick(this); } // @@ -135,8 +117,8 @@ public class TrackGraph { return nodesById.get(netId); } - public boolean createNode(DiscoveredLocation location) { - if (!createSpecificNode(location, nextNodeId(), location.normal)) + public boolean createNodeIfAbsent(DiscoveredLocation location) { + if (!addNodeIfAbsent(new TrackNode(location, nextNodeId(), location.normal))) return false; TrackNode newNode = nodes.get(location); Create.RAILWAYS.sync.nodeAdded(this, newNode); @@ -144,11 +126,19 @@ public class TrackGraph { return true; } - public boolean createSpecificNode(TrackNodeLocation location, int netId, Vec3 normal) { - return addNode(new TrackNode(location, netId, normal)); + public void loadNode(TrackNodeLocation location, int netId, Vec3 normal) { + addNode(new TrackNode(location, netId, normal)); } - public boolean addNode(TrackNode node) { + public void addNode(TrackNode node) { + TrackNodeLocation location = node.getLocation(); + if (nodes.containsKey(location)) + removeNode(null, location); + nodes.put(location, node); + nodesById.put(node.getNetId(), node); + } + + public boolean addNodeIfAbsent(TrackNode node) { if (nodes.putIfAbsent(node.getLocation(), node) != null) return false; nodesById.put(node.getNetId(), node); @@ -160,22 +150,6 @@ public class TrackGraph { if (removed == null) return false; - if (level != null) { - for (Iterator iterator = stations.keySet() - .iterator(); iterator.hasNext();) { - UUID uuid = iterator.next(); - GlobalStation globalStation = stations.get(uuid); - Couple loc = globalStation.edgeLocation; - if (loc.getFirst() - .equals(location) - || loc.getSecond() - .equals(location)) { - globalStation.migrate(level); - iterator.remove(); - } - } - } - Map trains = Create.RAILWAYS.trains; for (Iterator iterator = trains.keySet() .iterator(); iterator.hasNext();) { @@ -193,13 +167,18 @@ public class TrackGraph { Map connections = connectionsByNode.remove(removed); for (TrackEdge trackEdge : connections.values()) - for (SignalBoundary boundary : trackEdge.getEdgeData() - .getBoundaries()) - signals.remove(boundary.id); + for (TrackEdgePoint point : trackEdge.getEdgeData() + .getPoints()) { + if (level != null) + point.invalidate(level); + edgePoints.remove(point.getType(), point.getId()); + } + for (TrackNode railNode : connections.keySet()) if (connectionsByNode.containsKey(railNode)) connectionsByNode.get(railNode) .remove(removed); + return true; } @@ -208,20 +187,23 @@ public class TrackGraph { } public void transferAll(TrackGraph toOther) { - toOther.nodes.putAll(nodes); - toOther.nodesById.putAll(nodesById); - toOther.connectionsByNode.putAll(connectionsByNode); - for (GlobalStation globalStation : stations.values()) - toOther.addStation(globalStation); + nodes.forEach((loc, node) -> { + if (toOther.addNodeIfAbsent(node)) + Create.RAILWAYS.sync.nodeAdded(toOther, node); + }); - nodesById.forEach((id, node) -> Create.RAILWAYS.sync.nodeAdded(toOther, node)); - connectionsByNode.forEach( - (node1, map) -> map.forEach((node2, edge) -> Create.RAILWAYS.sync.edgeAdded(toOther, node1, node2, edge))); - markDirty(); + connectionsByNode.forEach((node1, map) -> map.forEach((node2, edge) -> { + TrackNode n1 = toOther.locateNode(node1.location); + TrackNode n2 = toOther.locateNode(node2.location); + if (n1 == null || n2 == null) + return; + toOther.putConnection(n1, n2, edge); + Create.RAILWAYS.sync.edgeAdded(toOther, n1, n2, edge); + })); + + edgePoints.transferAll(toOther.edgePoints); - stations.clear(); nodes.clear(); - nodesById.clear(); connectionsByNode.clear(); Map trains = Create.RAILWAYS.trains; @@ -263,7 +245,7 @@ public class TrackGraph { if (target != null) { transfer(currentNode, target); if (preAssignedIds != null && preAssignedIds.containsKey(currentNode.getNetId())) - target.id = preAssignedIds.get(currentNode.getNetId()); + target.setId(preAssignedIds.get(currentNode.getNetId())); } } @@ -274,31 +256,29 @@ public class TrackGraph { return dicovered; } + public void setId(UUID id) { + this.id = id; + color = Color.rainbowColor(new Random(id.getLeastSignificantBits()).nextInt()); + } + public void transfer(TrackNode node, TrackGraph target) { target.addNode(node); - TrackNodeLocation location1 = node.getLocation(); + TrackNodeLocation nodeLoc = node.getLocation(); Map connections = getConnectionsFrom(node); + Map trains = Create.RAILWAYS.trains; + if (!connections.isEmpty()) { target.connectionsByNode.put(node, connections); - for (TrackNode entry : connections.keySet()) { - for (Iterator iterator = stations.keySet() - .iterator(); iterator.hasNext();) { - UUID uuid = iterator.next(); - GlobalStation globalStation = stations.get(uuid); - Couple loc = globalStation.edgeLocation; - if (loc.getFirst() - .equals(location1) - && loc.getSecond() - .equals(entry.getLocation())) { - target.addStation(globalStation); - iterator.remove(); - } + for (TrackEdge entry : connections.values()) { + EdgeData edgeData = entry.getEdgeData(); + for (TrackEdgePoint trackEdgePoint : edgeData.getPoints()) { + target.edgePoints.put(trackEdgePoint.getType(), trackEdgePoint); + edgePoints.remove(trackEdgePoint.getType(), trackEdgePoint.getId()); } } } - Map trains = Create.RAILWAYS.trains; for (Iterator iterator = trains.keySet() .iterator(); iterator.hasNext();) { UUID uuid = iterator.next(); @@ -310,7 +290,7 @@ public class TrackGraph { train.graph = target; } - nodes.remove(location1); + nodes.remove(nodeLoc); nodesById.remove(node.getNetId()); connectionsByNode.remove(node); } @@ -391,19 +371,14 @@ public class TrackGraph { }); tag.put("Nodes", nodesList); - tag.put("Stations", NBTHelper.writeCompoundList(getStations(), GlobalStation::write)); - tag.put("Signals", NBTHelper.writeCompoundList(getSignals(), SignalBoundary::write)); + tag.put("Points", edgePoints.write()); return tag; } public static TrackGraph read(CompoundTag tag) { TrackGraph graph = new TrackGraph(tag.getUUID("Id")); graph.color = new Color(tag.getInt("Color")); - - NBTHelper.readCompoundList(tag.getList("Signals", Tag.TAG_COMPOUND), SignalBoundary::new) - .forEach(s -> graph.signals.put(s.id, s)); - NBTHelper.readCompoundList(tag.getList("Stations", Tag.TAG_COMPOUND), GlobalStation::new) - .forEach(s -> graph.stations.put(s.id, s)); + graph.edgePoints.read(tag.getCompound("Points")); Map indexTracker = new HashMap<>(); ListTag nodesList = tag.getList("Nodes", Tag.TAG_COMPOUND); @@ -414,7 +389,7 @@ public class TrackGraph { TrackNodeLocation location = TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(nodeTag.getCompound("Location"))); Vec3 normal = VecHelper.readNBT(nodeTag.getList("Normal", Tag.TAG_DOUBLE)); - graph.createSpecificNode(location, nextNodeId(), normal); + graph.loadNode(location, nextNodeId(), normal); indexTracker.put(i, graph.locateNode(location)); i++; } @@ -430,7 +405,6 @@ 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.edgeData.updateDelegates(node1, node2, edge); graph.putConnection(node1, node2, edge); }); } @@ -475,13 +449,30 @@ public class TrackGraph { Vec3 p1 = edge.getPosition(node, other, 0); Vec3 p2 = edge.getPosition(node, other, 1); - if (signalData.hasBoundaries()) { - SignalBoundary boundary = signalData.nextBoundary(node, other, edge, 0); - SignalBoundary prevBoundaryNonNull = boundary; + if (signalData.hasPoints()) { double prev = 0; double length = edge.getLength(node, other); - SignalEdgeGroup group = Create.RAILWAYS.signalEdgeGroups.get(boundary.getGroup(node)); - while (boundary != null) { + 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.signalEdgeGroups.get(boundary.getGroup(node)); + if (group != null) CreateClient.OUTLINER .showLine(Pair.of(boundary, edge), @@ -493,20 +484,19 @@ public class TrackGraph { .add(yOffset)) .colored(group.color.getRGB()) .lineWidth(1 / 16f); - boundary = - signalData.nextBoundary(node, other, edge, boundary.getLocationOn(node, other, edge)); - if (boundary != null) { - group = Create.RAILWAYS.signalEdgeGroups.get(boundary.getGroup(node)); - prevBoundaryNonNull = boundary; - } + + } + + if (prevBoundary != null) { + group = Create.RAILWAYS.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; } - group = Create.RAILWAYS.signalEdgeGroups.get(prevBoundaryNonNull.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) 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 44c0799f9..c71549731 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 @@ -56,8 +56,16 @@ public class TrackGraphSync { public void nodeRemoved(TrackGraph graph, TrackNode node) { flushPacket(graph.id); - if (currentPacket.addedNodes.remove(node.getNetId()) == null) - currentPacket.removedNodes.add(node.getNetId()); + int nodeId = node.getNetId(); + if (currentPacket.addedNodes.remove(nodeId) == null) + currentPacket.removedNodes.add(nodeId); + currentPacket.addedEdges.removeIf(pair -> { + Couple ids = pair.getFirst(); + return ids.getFirst() + .intValue() == nodeId + || ids.getSecond() + .intValue() == nodeId; + }); } public void graphSplit(TrackGraph graph, Set additional) { @@ -80,19 +88,25 @@ public class TrackGraphSync { // public void sendFullGraphTo(TrackGraph graph, ServerPlayer player) { - // TODO ensure packet size limit - RailGraphSyncPacket packet = new RailGraphSyncPacket(graph.id); + int sent = 0; for (TrackNode node : graph.nodes.values()) { - packet.addedNodes.put(node.getNetId(), Pair.of(node.getLocation(), node.getNormal())); + RailGraphSyncPacket currentPacket = packet; + currentPacket.addedNodes.put(node.getNetId(), Pair.of(node.getLocation(), node.getNormal())); if (!graph.connectionsByNode.containsKey(node)) continue; graph.connectionsByNode.get(node) - .forEach((node2, edge) -> packet.addedEdges + .forEach((node2, edge) -> currentPacket.addedEdges .add(Pair.of(Couple.create(node.getNetId(), node2.getNetId()), edge))); - } - AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), packet); + if (sent++ > 1000) { + sent = 0; + AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), packet); + packet = new RailGraphSyncPacket(graph.id); + } + } + if (sent > 0) + AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), packet); } private RailGraphSyncPacket currentPacket; @@ -161,6 +175,7 @@ public class TrackGraphSync { @Override public void write(FriendlyByteBuf buffer) { + buffer.writeUUID(graphId); buffer.writeBoolean(delete); if (delete) @@ -209,11 +224,11 @@ public class TrackGraphSync { railGraph.removeNode(null, node.getLocation()); } - for (Entry> entry : addedNodes.entrySet()) - railGraph.createSpecificNode(entry.getValue() - .getFirst(), entry.getKey(), - entry.getValue() - .getSecond()); + for (Entry> entry : addedNodes.entrySet()) { + Integer nodeId = entry.getKey(); + Pair nodeLocation = entry.getValue(); + railGraph.loadNode(nodeLocation.getFirst(), nodeId, nodeLocation.getSecond()); + } for (Pair, TrackEdge> pair : addedEdges) { Couple nodes = pair.getFirst() 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 8d71497d3..d6cc66915 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 @@ -38,27 +38,24 @@ public class TrackPropagator { Collection ends = track.getConnected(reader, pos, state, false, null); GlobalRailwayManager manager = Create.RAILWAYS; TrackGraphSync sync = manager.sync; - TrackGraph foundGraph = null; // 1. Remove any nodes this rail was part of for (DiscoveredLocation removedLocation : ends) { - if (foundGraph == null) - foundGraph = manager.getGraph(reader, removedLocation); - if (foundGraph == null) - continue; - TrackNode removedNode = foundGraph.locateNode(removedLocation); - if (removedNode != null) { + List intersecting = manager.getGraphs(reader, removedLocation); + for (TrackGraph foundGraph : intersecting) { + TrackNode removedNode = foundGraph.locateNode(removedLocation); + if (removedNode == null) + continue; foundGraph.removeNode(reader, removedLocation); sync.nodeRemoved(foundGraph, removedNode); + if (!foundGraph.isEmpty()) + continue; + manager.removeGraph(foundGraph); + sync.graphRemoved(foundGraph); } } - if (foundGraph != null && foundGraph.isEmpty()) { - manager.removeGraph(foundGraph); - sync.graphRemoved(foundGraph); - } - Set positionsToUpdate = new HashSet<>(); for (DiscoveredLocation removedEnd : ends) positionsToUpdate.addAll(removedEnd.allAdjacent()); @@ -100,8 +97,8 @@ public class TrackPropagator { break; FrontierEntry entry = frontier.remove(0); - TrackGraph graph = manager.getGraph(reader, entry.currentNode); - if (graph != null) { + List intersecting = manager.getGraphs(reader, entry.currentNode); + for (TrackGraph graph : intersecting) { TrackNode node = graph.locateNode(entry.currentNode); graph.removeNode(reader, entry.currentNode); sync.nodeRemoved(graph, node); @@ -109,6 +106,9 @@ public class TrackPropagator { continue; } + if (!intersecting.isEmpty()) + continue; + Collection ends = ITrackBlock.walkConnectedTracks(reader, entry.currentNode, false); if (entry.prevNode != null) ends.remove(entry.prevNode); @@ -117,7 +117,6 @@ public class TrackPropagator { frontier.clear(); visited.clear(); - TrackGraph graph = null; // Remove empty graphs @@ -173,11 +172,7 @@ public class TrackPropagator { frontier.clear(); Set addedNodes = new HashSet<>(); - if (graph.createNode(startNode)) { - TrackNode node = graph.locateNode(startNode); - sync.nodeAdded(graph, node); - } - + graph.createNodeIfAbsent(startNode); frontier.add(new FrontierEntry(startNode, null, startNode)); // 3. Build up the graph via all connected nodes @@ -195,11 +190,7 @@ public class TrackPropagator { ends.remove(entry.prevNode); if (isValidGraphNodeLocation(entry.currentNode, ends, first) && entry.currentNode != startNode) { - boolean nodeIsNew = graph.createNode(entry.currentNode); - if (nodeIsNew) { - TrackNode node = graph.locateNode(entry.currentNode); - sync.nodeAdded(graph, node); - } + boolean nodeIsNew = graph.createNodeIfAbsent(entry.currentNode); graph.connectNodes(parentNode, entry.currentNode, new TrackEdge(entry.currentNode.getTurn())); addedNodes.add(graph.locateNode(entry.currentNode)); parentNode = entry.currentNode; 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 e20bc818e..51343dbde 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 @@ -9,7 +9,6 @@ import java.util.Map.Entry; import java.util.PriorityQueue; import java.util.Set; import java.util.UUID; -import java.util.function.BiPredicate; import org.apache.commons.lang3.mutable.MutableObject; @@ -17,12 +16,13 @@ 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.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.content.logistics.trains.management.GlobalStation; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.signal.EdgeData; import com.simibubi.create.content.logistics.trains.management.signal.SignalBoundary; import com.simibubi.create.content.logistics.trains.management.signal.SignalEdgeGroup; +import com.simibubi.create.content.logistics.trains.management.signal.TrackEdgePoint; import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Iterate; @@ -79,13 +79,14 @@ public class Navigation { double acceleration = AllConfigs.SERVER.trains.getAccelerationMPTT(); double brakingDistance = (train.speed * train.speed) / (2 * acceleration); double speedMod = destinationBehindTrain ? -1 : 1; + double preDepartureLookAhead = train.getCurrentStation() != null ? 4.5 : 0; // Signals if (train.graph != null) { if (waitingForSignal != null && checkBlockingSignal()) waitingForSignal = null; - TravellingPoint leadingPoint = train.speed > 0 ? train.carriages.get(0) + TravellingPoint leadingPoint = !destinationBehindTrain ? train.carriages.get(0) .getLeadingPoint() : train.carriages.get(train.carriages.size() - 1) .getTrailingPoint(); @@ -93,13 +94,19 @@ public class Navigation { if (waitingForSignal == null) distanceToSignal = Double.MAX_VALUE; + if (train.getCurrentStation() != null) { + int i = 0; + i++; + } + if (distanceToSignal > 1 / 16f) { signalScout.node1 = leadingPoint.node1; signalScout.node2 = leadingPoint.node2; signalScout.edge = leadingPoint.edge; signalScout.position = leadingPoint.position; - double brakingDistanceNoFlicker = brakingDistance + 3 - (brakingDistance % 3); + double brakingDistanceNoFlicker = + Math.max(preDepartureLookAhead, brakingDistance + 3 - (brakingDistance % 3)); double scanDistance = Math.min(distanceToDestination - .5f, brakingDistanceNoFlicker); signalScout.travel(train.graph, scanDistance * speedMod, controlSignalScout(), (distance, couple) -> { @@ -133,6 +140,11 @@ public class Navigation { train.arriveAt(destination); destination = null; return; + } else if (train.getCurrentStation() != null) { + // dont leave until green light + if (waitingForSignal != null && distanceToSignal < preDepartureLookAhead) + return; + train.leaveStation(); } train.currentlyBackwards = destinationBehindTrain; @@ -158,7 +170,7 @@ public class Navigation { private boolean checkBlockingSignal() { if (distanceToDestination < .5f) return true; - SignalBoundary signal = train.graph.getSignal(waitingForSignal.getFirst()); + SignalBoundary signal = train.graph.getPoint(EdgePointType.SIGNAL, waitingForSignal.getFirst()); if (signal == null) return true; UUID groupId = signal.groups.get(waitingForSignal.getSecond()); @@ -167,11 +179,8 @@ public class Navigation { SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(groupId); if (signalEdgeGroup == null) return true; - if (!signalEdgeGroup.isOccupiedUnless(train)) { - if (train.currentStation != null) - train.leaveStation(); + if (!signalEdgeGroup.isOccupiedUnless(train)) return true; - } return false; } @@ -272,20 +281,6 @@ public class Navigation { train.status.foundConductor(); } - GlobalStation currentStation = train.getCurrentStation(); - if (currentStation != null) { - SignalBoundary boundary = currentStation.boundary; - if (boundary != null) { - TrackNode node1 = train.graph.locateNode(currentStation.edgeLocation.getFirst()); - UUID group = boundary.getGroup(node1); - if (group != null) { - waitingForSignal = Pair.of(boundary.id, !boundary.isPrimary(node1)); - distanceToSignal = 0; - } - } else - train.leaveStation(); - } - this.destination = destination; return distanceToDestination; } @@ -297,7 +292,6 @@ public class Navigation { if (graph == null) return Pair.of(null, path); - Couple target = destination.edgeLocation; MutableObject>> frontResult = new MutableObject<>(Pair.of(null, path)); MutableObject>> backResult = new MutableObject<>(Pair.of(null, path)); @@ -314,21 +308,16 @@ public class Navigation { : graph.getConnectionsFrom(initialPoint.node2) .get(initialPoint.node1); - search(Double.MAX_VALUE, forward, (reachedVia, poll) -> { + search(Double.MAX_VALUE, forward, (distance, reachedVia, currentEntry, globalStation) -> { + if (globalStation != destination) + return false; - double distance = poll.getFirst(); - Pair, TrackEdge> currentEntry = poll.getSecond(); TrackEdge edge = currentEntry.getSecond(); TrackNode node1 = currentEntry.getFirst() .getFirst(); TrackNode node2 = currentEntry.getFirst() .getSecond(); - TrackNodeLocation loc1 = node1.getLocation(); - TrackNodeLocation loc2 = node2.getLocation(); - if (!loc1.equals(target.getFirst()) || !loc2.equals(target.getSecond())) - return false; - Pair backTrack = reachedVia.get(edge); TrackEdge toReach = edge; while (backTrack != null && toReach != initialEdge) { @@ -338,7 +327,7 @@ public class Navigation { backTrack = reachedVia.get(backTrack.getSecond()); } - double position = edge.getLength(node1, node2) - destination.position; + double position = edge.getLength(node1, node2) - destination.getLocationOn(node1, node2, edge); double distanceToDestination = distance - position; if (forward) @@ -383,40 +372,27 @@ public class Navigation { double minDistance = .75f * (train.speed * train.speed) / (2 * acceleration); double maxDistance = Math.max(32, 1.5f * (train.speed * train.speed) / (2 * acceleration)); - search(maxDistance, forward, (reachedVia, poll) -> { - double distance = poll.getFirst(); + search(maxDistance, forward, (distance, reachedVia, currentEntry, globalStation) -> { if (distance < minDistance) return false; - Pair, TrackEdge> currentEntry = poll.getSecond(); TrackEdge edge = currentEntry.getSecond(); TrackNode node1 = currentEntry.getFirst() .getFirst(); TrackNode node2 = currentEntry.getFirst() .getSecond(); - for (GlobalStation globalStation : edge.getEdgeData() - .getStations()) { - Couple target = globalStation.edgeLocation; - TrackNodeLocation loc1 = node1.getLocation(); - TrackNodeLocation loc2 = node2.getLocation(); - if (!loc1.equals(target.getFirst()) || !loc2.equals(target.getSecond())) - continue; - double position = edge.getLength(node1, node2) - globalStation.position; - if (distance - position < minDistance) - continue; - result.setValue(globalStation); - return true; - } - - return false; + double position = edge.getLength(node1, node2) - globalStation.getLocationOn(node1, node2, edge); + if (distance - position < minDistance) + return false; + result.setValue(globalStation); + return true; }); return result.getValue(); } - public void search(double maxDistance, boolean forward, - BiPredicate>, Pair, TrackEdge>>> condition) { + public void search(double maxDistance, boolean forward, StationTest stationTest) { TrackGraph graph = train.graph; if (graph == null) return; @@ -440,7 +416,7 @@ public class Navigation { frontier.add(Pair.of(distanceToNode2, Pair.of(Couple.create(initialNode1, initialNode2), initialEdge))); - while (!frontier.isEmpty()) { + Search: while (!frontier.isEmpty()) { Pair, TrackEdge>> poll = frontier.poll(); double distance = poll.getFirst(); if (distance > maxDistance) @@ -452,12 +428,24 @@ public class Navigation { .getFirst(); TrackNode node2 = currentEntry.getFirst() .getSecond(); - if (condition.test(reachedVia, poll)) - return; + + 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) + continue; + if (!point.canNavigateVia(node2)) + continue Search; + if (point instanceof GlobalStation station && station.canApproachFrom(node2) + && stationTest.test(distance, reachedVia, currentEntry, station)) + return; + } + } List> validTargets = new ArrayList<>(); - EdgeWalk: for (Entry entry : graph.getConnectionsFrom(node2) - .entrySet()) { + 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); @@ -466,13 +454,6 @@ public class Navigation { continue; if (!visited.add(entry.getValue())) continue; - - EdgeData signalData = newEdge.getEdgeData(); - if (signalData.hasBoundaries()) - for (SignalBoundary boundary : signalData.getBoundaries()) - if (!boundary.canNavigateVia(node2)) - continue EdgeWalk; - validTargets.add(entry); } @@ -489,4 +470,10 @@ public class Navigation { } } + @FunctionalInterface + public interface StationTest { + boolean test(double distance, Map> reachedVia, + Pair, TrackEdge> current, GlobalStation station); + } + } 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 bf54d5d35..6fe5cf0a6 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 @@ -31,6 +31,7 @@ import com.simibubi.create.content.logistics.trains.management.GlobalStation; import com.simibubi.create.content.logistics.trains.management.GraphLocation; import com.simibubi.create.content.logistics.trains.management.ScheduleRuntime; import com.simibubi.create.content.logistics.trains.management.ScheduleRuntime.State; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.signal.EdgeData; import com.simibubi.create.content.logistics.trains.management.signal.SignalBoundary; import com.simibubi.create.content.logistics.trains.management.signal.SignalEdgeGroup; @@ -556,7 +557,7 @@ public class Train { return null; if (graph == null) return null; - return graph.getStation(currentStation); + return graph.getPoint(EdgePointType.STATION, currentStation); } @Nullable @@ -596,16 +597,16 @@ public class Train { Map allGroups = Create.RAILWAYS.signalEdgeGroups; MutableObject prevGroup = new MutableObject<>(null); - if (signalData.hasBoundaries()) { - SignalBoundary nextBoundary = signalData.nextBoundary(node1, node2, edge, position); + if (signalData.hasSignalBoundaries()) { + SignalBoundary nextBoundary = signalData.next(EdgePointType.SIGNAL, node1, node2, edge, position); if (nextBoundary == null) { double d = 0; SignalBoundary prev = null; - SignalBoundary current = signalData.nextBoundary(node1, node2, edge, 0); + SignalBoundary current = signalData.next(EdgePointType.SIGNAL, node1, node2, edge, 0); while (current != null) { prev = current; d = current.getLocationOn(node1, node2, edge); - current = signalData.nextBoundary(node1, node2, edge, d); + current = signalData.next(EdgePointType.SIGNAL, node1, node2, edge, d); } if (prev != null) { UUID group = prev.getGroup(node2); 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 7b9f9b6d4..87789d643 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 @@ -15,8 +15,9 @@ 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.management.GraphLocation; -import com.simibubi.create.content.logistics.trains.management.signal.SignalBoundary; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.signal.EdgeData; +import com.simibubi.create.content.logistics.trains.management.signal.SignalBoundary; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Pair; @@ -258,12 +259,12 @@ public class TravellingPoint { private void edgeTraversedFrom(TrackGraph graph, boolean forward, ISignalBoundaryListener signalListener, double prevPos, double totalDistance) { EdgeData signalsOnEdge = edge.getEdgeData(); - if (!signalsOnEdge.hasBoundaries()) + if (!signalsOnEdge.hasSignalBoundaries()) return; double from = forward ? prevPos : position; double to = forward ? position : prevPos; - SignalBoundary nextBoundary = signalsOnEdge.nextBoundary(node1, node2, edge, from); + SignalBoundary nextBoundary = signalsOnEdge.next(EdgePointType.SIGNAL, node1, node2, edge, from); List discoveredBoundaries = null; while (nextBoundary != null) { @@ -273,7 +274,7 @@ public class TravellingPoint { if (discoveredBoundaries == null) discoveredBoundaries = new ArrayList<>(); discoveredBoundaries.add(nextBoundary); - nextBoundary = signalsOnEdge.nextBoundary(node1, node2, edge, d); + nextBoundary = signalsOnEdge.next(EdgePointType.SIGNAL, node1, node2, edge, d); } if (discoveredBoundaries == null) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/GlobalStation.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/GlobalStation.java index 22d3dac8a..acba8b150 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/GlobalStation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/GlobalStation.java @@ -1,57 +1,40 @@ package com.simibubi.create.content.logistics.trains.management; import java.lang.ref.WeakReference; -import java.util.UUID; import javax.annotation.Nullable; -import com.simibubi.create.content.logistics.trains.TrackNodeLocation; +import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.entity.Train; -import com.simibubi.create.content.logistics.trains.management.signal.SignalBoundary; -import com.simibubi.create.content.logistics.trains.management.signal.TrackEdgePoint; -import com.simibubi.create.foundation.utility.Couple; +import com.simibubi.create.content.logistics.trains.management.signal.SingleTileEdgePoint; -import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.block.entity.BlockEntity; -public class GlobalStation extends TrackEdgePoint { +public class GlobalStation extends SingleTileEdgePoint { public String name; - public BlockPos stationPos; - public WeakReference nearestTrain; - - @Nullable - public SignalBoundary boundary; - public GlobalStation(UUID id, BlockPos stationPos) { - super(id); - this.stationPos = stationPos; + public GlobalStation() { name = "Track Station"; nearestTrain = new WeakReference(null); } - public GlobalStation(CompoundTag nbt) { - super(nbt); + @Override + public void read(CompoundTag nbt, boolean migration) { + super.read(nbt, migration); name = nbt.getString("Name"); - stationPos = NbtUtils.readBlockPos(nbt.getCompound("StationPos")); nearestTrain = new WeakReference(null); } - public void migrate(LevelAccessor level) { - BlockEntity blockEntity = level.getBlockEntity(stationPos); - if (blockEntity instanceof StationTileEntity station) { - station.migrate(this); - return; - } + @Override + public void write(CompoundTag nbt) { + super.write(nbt); + nbt.putString("Name", name); } - - public void setLocation(Couple nodes, double position) { - this.edgeLocation = nodes; - this.position = position; + + public boolean canApproachFrom(TrackNode side) { + return isPrimary(side); } public void reserveFor(Train train) { @@ -97,14 +80,4 @@ public class GlobalStation extends TrackEdgePoint { return this.nearestTrain.get(); } - public CompoundTag write() { - CompoundTag nbt = new CompoundTag(); - nbt.putUUID("Id", id); - nbt.putString("Name", name); - nbt.put("StationPos", NbtUtils.writeBlockPos(stationPos)); - nbt.putDouble("Position", position); - nbt.put("Edge", edgeLocation.serializeEach(loc -> NbtUtils.writeBlockPos(new BlockPos(loc)))); - return nbt; - } - } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/ScheduleRuntime.java index 342482d29..cb56f839c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/ScheduleRuntime.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; 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.schedule.FilteredDestination; import com.simibubi.create.content.logistics.trains.management.schedule.Schedule; import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleDestination; @@ -128,7 +129,7 @@ public class ScheduleRuntime { String regex = filtered.nameFilter.replace("*", ".*"); GlobalStation best = null; double bestCost = Double.MAX_VALUE; - for (GlobalStation globalStation : train.graph.getStations()) { + for (GlobalStation globalStation : train.graph.getPoints(EdgePointType.STATION)) { if (!globalStation.name.matches(regex)) continue; boolean matchesCurrent = train.currentStation != null && train.currentStation.equals(globalStation.id); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationBlock.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationBlock.java index 00bd598c3..f8f201039 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationBlock.java @@ -75,10 +75,10 @@ public class StationBlock extends HorizontalDirectionalBlock implements ITE { int light, int overlay) { BlockPos pos = te.getBlockPos(); - TrackTargetingBehaviour target = te.getTarget(); + TrackTargetingBehaviour target = te.edgePoint; BlockPos targetPosition = target.getGlobalPosition(); Level level = te.getLevel(); @@ -38,7 +38,7 @@ public class StationRenderer extends SafeTileEntityRenderer { if (!(block instanceof ITrackBlock)) return; - GlobalStation station = te.getOrCreateGlobalStation(); + GlobalStation station = te.getStation(); if (!te.getBlockState() .getValue(StationBlock.ASSEMBLING) || station == null || station.getPresentTrain() != null) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationTileEntity.java index a4099e1a3..703bb1a10 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationTileEntity.java @@ -9,6 +9,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.UUID; +import javax.annotation.Nullable; + import com.simibubi.create.Create; import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException; import com.simibubi.create.content.logistics.trains.IBogeyBlock; @@ -22,6 +24,7 @@ import com.simibubi.create.content.logistics.trains.entity.Carriage.CarriageBoge import com.simibubi.create.content.logistics.trains.entity.CarriageContraption; import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.utility.Iterate; @@ -46,91 +49,42 @@ import net.minecraftforge.api.distmarker.OnlyIn; public class StationTileEntity extends SmartTileEntity { - UUID id; + public TrackTargetingBehaviour edgePoint; protected int failedCarriageIndex; protected AssemblyException lastException; - protected CompoundTag toMigrate; public StationTileEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); setLazyTickRate(20); - id = UUID.randomUUID(); lastException = null; - toMigrate = null; failedCarriageIndex = -1; } - public void migrate(GlobalStation globalStation) { - if (toMigrate != null) - return; - toMigrate = globalStation.write(); - setChanged(); - } - @Override public void addBehaviours(List behaviours) { - behaviours.add(new TrackTargetingBehaviour(this)); - } - - public TrackTargetingBehaviour getTarget() { - return getBehaviour(TrackTargetingBehaviour.TYPE); - } - - @Override - public void initialize() { - if (!level.isClientSide) - getOrCreateGlobalStation(); - super.initialize(); - } - - public GlobalStation getOrCreateGlobalStation() { - for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values()) { // TODO thread breach - GlobalStation station = trackGraph.getStation(id); - if (station == null) - continue; - return station; - } - - if (level.isClientSide) - return null; - - TrackTargetingBehaviour target = getTarget(); - if (!target.hasValidTrack()) - return null; - GraphLocation loc = target.determineGraphLocation(); - if (loc == null) - return null; - - GlobalStation globalStation = - toMigrate != null ? new GlobalStation(toMigrate) : new GlobalStation(id, worldPosition); - globalStation.setLocation(loc.edge, loc.position); - loc.graph.addStation(globalStation); - toMigrate = null; - setChanged(); - - return globalStation; + edgePoint = new TrackTargetingBehaviour<>(this, EdgePointType.STATION); + behaviours.add(edgePoint); } @Override protected void read(CompoundTag tag, boolean clientPacket) { - id = tag.getUUID("Id"); lastException = AssemblyException.read(tag); failedCarriageIndex = tag.getInt("FailedCarriageIndex"); super.read(tag, clientPacket); - if (tag.contains("ToMigrate")) - toMigrate = tag.getCompound("ToMigrate"); invalidateRenderBoundingBox(); } @Override protected void write(CompoundTag tag, boolean clientPacket) { - tag.putUUID("Id", id); AssemblyException.write(tag, lastException); tag.putInt("FailedCarriageIndex", failedCarriageIndex); super.write(tag, clientPacket); - if (!clientPacket && toMigrate != null) - tag.put("ToMigrate", toMigrate); + } + + @Nullable + public GlobalStation getStation() { + return edgePoint.getEdgePoint(); } // Train Assembly @@ -147,8 +101,6 @@ public class StationTileEntity extends SmartTileEntity { public void lazyTick() { if (isAssembling() && !level.isClientSide) refreshAssemblyInfo(); - if (!level.isClientSide) - getOrCreateGlobalStation(); super.lazyTick(); } @@ -157,12 +109,6 @@ public class StationTileEntity extends SmartTileEntity { if (isAssembling() && level.isClientSide) refreshAssemblyInfo(); super.tick(); - - if (level.isClientSide) - return; - if (toMigrate == null) - return; - getOrCreateGlobalStation(); } public void trackClicked(Player player, ITrackBlock track, BlockState state, BlockPos pos) { @@ -172,7 +118,7 @@ public class StationTileEntity extends SmartTileEntity { if (bb == null || !bb.isInside(pos)) return; - int bogeyOffset = pos.distManhattan(getTarget().getGlobalPosition()) - 1; + int bogeyOffset = pos.distManhattan(edgePoint.getGlobalPosition()) - 1; if (!isValidBogeyOffset(bogeyOffset)) return; @@ -187,13 +133,12 @@ public class StationTileEntity extends SmartTileEntity { } public boolean tryEnterAssemblyMode() { - TrackTargetingBehaviour target = getTarget(); - if (!target.hasValidTrack()) + if (!edgePoint.hasValidTrack()) return false; - BlockPos targetPosition = target.getGlobalPosition(); - BlockState trackState = target.getTrackBlockState(); - ITrackBlock track = target.getTrack(); + BlockPos targetPosition = edgePoint.getGlobalPosition(); + BlockState trackState = edgePoint.getTrackBlockState(); + ITrackBlock track = edgePoint.getTrack(); Vec3 trackAxis = track.getTrackAxes(level, targetPosition, trackState) .get(0); @@ -210,18 +155,17 @@ public class StationTileEntity extends SmartTileEntity { } public void refreshAssemblyInfo() { - TrackTargetingBehaviour target = getTarget(); - if (!target.hasValidTrack()) + if (!edgePoint.hasValidTrack()) return; - GlobalStation station = getOrCreateGlobalStation(); + GlobalStation station = getStation(); if (station == null || station.getPresentTrain() != null) return; int prevLength = assemblyLength; - BlockPos targetPosition = target.getGlobalPosition(); - BlockState trackState = target.getTrackBlockState(); - ITrackBlock track = target.getTrack(); + BlockPos targetPosition = edgePoint.getGlobalPosition(); + BlockState trackState = edgePoint.getTrackBlockState(); + ITrackBlock track = edgePoint.getTrack(); getAssemblyDirection(); MutableBlockPos currentPos = targetPosition.mutable(); @@ -248,7 +192,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++; @@ -285,13 +229,12 @@ public class StationTileEntity extends SmartTileEntity { public Direction getAssemblyDirection() { if (assemblyDirection != null) return assemblyDirection; - TrackTargetingBehaviour target = getTarget(); - if (!target.hasValidTrack()) + if (!edgePoint.hasValidTrack()) return null; - BlockPos targetPosition = target.getGlobalPosition(); - BlockState trackState = target.getTrackBlockState(); - ITrackBlock track = target.getTrack(); - AxisDirection axisDirection = target.getTargetDirection(); + BlockPos targetPosition = edgePoint.getGlobalPosition(); + BlockState trackState = edgePoint.getTrackBlockState(); + ITrackBlock track = edgePoint.getTrack(); + AxisDirection axisDirection = edgePoint.getTargetDirection(); Vec3 axis = track.getTrackAxes(level, targetPosition, trackState) .get(0) .normalize() @@ -303,8 +246,7 @@ public class StationTileEntity extends SmartTileEntity { protected void setRemovedNotDueToChunkUnload() { assemblyAreas.get(level) .remove(worldPosition); - for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values()) - trackGraph.removeStation(id); + super.setRemovedNotDueToChunkUnload(); } @@ -316,13 +258,12 @@ public class StationTileEntity extends SmartTileEntity { return; } - TrackTargetingBehaviour target = getTarget(); - if (!target.hasValidTrack()) + if (!edgePoint.hasValidTrack()) return; - BlockPos trackPosition = target.getGlobalPosition(); - BlockState trackState = target.getTrackBlockState(); - ITrackBlock track = target.getTrack(); + BlockPos trackPosition = edgePoint.getGlobalPosition(); + BlockState trackState = edgePoint.getTrackBlockState(); + ITrackBlock track = edgePoint.getTrack(); BlockPos bogeyOffset = new BlockPos(track.getUpNormal(level, trackPosition, trackState)); DiscoveredLocation location = null; @@ -487,11 +428,13 @@ public class StationTileEntity extends SmartTileEntity { } Train train = new Train(UUID.randomUUID(), playerUUID, graph, carriages, spacing); - GlobalStation station = getOrCreateGlobalStation(); - train.setCurrentStation(station); - station.reserveFor(train); + GlobalStation station = getStation(); + if (station != null) { + train.setCurrentStation(station); + station.reserveFor(train); + } + train.collectInitiallyOccupiedSignalBlocks(); - Create.RAILWAYS.trains.put(train.id, train); clearException(); } @@ -523,7 +466,7 @@ public class StationTileEntity extends SmartTileEntity { @Override protected AABB createRenderBoundingBox() { - return new AABB(worldPosition, getTarget().getGlobalPosition()).inflate(2); + return new AABB(worldPosition, edgePoint.getGlobalPosition()).inflate(2); } } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/TrackTargetingBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/TrackTargetingBehaviour.java index f0c5346fa..f4ec00935 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/TrackTargetingBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/TrackTargetingBehaviour.java @@ -1,8 +1,20 @@ package com.simibubi.create.content.logistics.trains.management; +import java.util.UUID; + +import javax.annotation.Nullable; + import com.mojang.blaze3d.vertex.PoseStack; +import com.simibubi.create.Create; import com.simibubi.create.content.logistics.trains.ITrackBlock; +import com.simibubi.create.content.logistics.trains.TrackEdge; +import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraphHelper; +import com.simibubi.create.content.logistics.trains.TrackNode; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; +import com.simibubi.create.content.logistics.trains.management.signal.EdgeData; +import com.simibubi.create.content.logistics.trains.management.signal.SingleTileEdgePoint; +import com.simibubi.create.content.logistics.trains.management.signal.TrackEdgePoint; import com.simibubi.create.foundation.render.CachedBufferer; import com.simibubi.create.foundation.render.SuperByteBuffer; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; @@ -23,33 +35,143 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -public class TrackTargetingBehaviour extends TileEntityBehaviour { +public class TrackTargetingBehaviour extends TileEntityBehaviour { - public static final BehaviourType TYPE = new BehaviourType<>(); + public static final BehaviourType> TYPE = new BehaviourType<>(); private BlockPos targetTrack; private AxisDirection targetDirection; + private UUID id; - public TrackTargetingBehaviour(SmartTileEntity te) { + private CompoundTag migrationData; + private EdgePointType edgePointType; + private T edgePoint; + + public TrackTargetingBehaviour(SmartTileEntity te, EdgePointType edgePointType) { super(te); + this.edgePointType = edgePointType; targetDirection = AxisDirection.POSITIVE; targetTrack = BlockPos.ZERO; + id = UUID.randomUUID(); + migrationData = null; } @Override public void write(CompoundTag nbt, boolean clientPacket) { + nbt.putUUID("Id", id); nbt.put("TargetTrack", NbtUtils.writeBlockPos(targetTrack)); nbt.putBoolean("TargetDirection", targetDirection == AxisDirection.POSITIVE); + if (migrationData != null && !clientPacket) + nbt.put("Migrate", migrationData); super.write(nbt, clientPacket); } @Override public void read(CompoundTag nbt, boolean clientPacket) { + UUID prevId = id; + id = nbt.getUUID("Id"); targetTrack = NbtUtils.readBlockPos(nbt.getCompound("TargetTrack")); targetDirection = nbt.getBoolean("TargetDirection") ? AxisDirection.POSITIVE : AxisDirection.NEGATIVE; + if (nbt.contains("Migrate")) + migrationData = nbt.getCompound("Migrate"); + if (clientPacket && !prevId.equals(id)) + edgePoint = null; super.read(nbt, clientPacket); } + @Nullable + public T getEdgePoint() { + return edgePoint; + } + + public void invalidateEdgePoint(CompoundTag migrationData) { + this.migrationData = migrationData; + id = UUID.randomUUID(); + edgePoint = null; + tileEntity.sendData(); + } + + @Override + public void tick() { + super.tick(); + if (edgePoint == null) + edgePoint = createEdgePoint(); + } + + @SuppressWarnings("unchecked") + public T createEdgePoint() { + for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values()) { // TODO thread breach + T point = trackGraph.getPoint(edgePointType, id); + if (point == null) + continue; + return point; + } + + if (getWorld().isClientSide) + return null; + if (!hasValidTrack()) + return null; + GraphLocation loc = determineGraphLocation(); + if (loc == null) + return null; + + TrackGraph graph = loc.graph; + TrackNode node1 = graph.locateNode(loc.edge.getFirst()); + TrackNode node2 = graph.locateNode(loc.edge.getSecond()); + TrackEdge edge = graph.getConnectionsFrom(node1) + .get(node2); + + boolean front = getTargetDirection() == AxisDirection.POSITIVE; + + if (edge == null) + return null; + + EdgeData signalData = edge.getEdgeData(); + if (signalData.hasPoints()) { + for (EdgePointType otherType : EdgePointType.TYPES.values()) { + TrackEdgePoint otherPoint = signalData.get(otherType, node1, node2, edge, loc.position); + if (otherPoint == null) + continue; + if (otherType != edgePointType) { + if (!otherPoint.canCoexistWith(edgePointType, front)) + return null; + continue; + } + if (!otherPoint.canMerge()) + return null; + otherPoint.tileAdded(getPos(), front); + id = otherPoint.getId(); + tileEntity.setChanged(); + return (T) otherPoint; + } + } + + T point = edgePointType.create(); + point.setId(id); + + if (point instanceof SingleTileEdgePoint step) { + point.setLocation(loc.edge, loc.position); + if (migrationData != null) { + step.read(migrationData, true); + migrationData = null; + tileEntity.setChanged(); + } + } else + point.setLocation(front ? loc.edge : loc.edge.swap(), + front ? loc.position : edge.getLength(node1, node2) - loc.position); + + point.tileAdded(getPos(), front); + loc.graph.addPoint(edgePointType, point); + return point; + } + + @Override + public void remove() { + if (edgePoint != null && !getWorld().isClientSide) + edgePoint.tileRemoved(getPos(), getTargetDirection() == AxisDirection.POSITIVE); + super.remove(); + } + @Override public BehaviourType getType() { return TYPE; 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 new file mode 100644 index 000000000..312521f11 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointManager.java @@ -0,0 +1,40 @@ +package com.simibubi.create.content.logistics.trains.management.edgePoint; + +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.signal.TrackEdgePoint; +import com.simibubi.create.foundation.utility.Couple; +import com.simibubi.create.foundation.utility.Iterate; + +public class EdgePointManager { + + public static void onEdgePointAdded(TrackGraph graph, T point, EdgePointType type) { + Couple edgeLocation = point.edgeLocation; + Couple startNodes = edgeLocation.map(graph::locateNode); + Couple startEdges = startNodes.mapWithParams((l1, l2) -> graph.getConnectionsFrom(l1) + .get(l2), startNodes.swap()); + + for (boolean front : Iterate.trueAndFalse) { + TrackNode node1 = startNodes.get(front); + TrackNode node2 = startNodes.get(!front); + TrackEdge startEdge = startEdges.get(front); + startEdge.getEdgeData() + .addPoint(node1, node2, startEdge, point); + } + } + + public static void onEdgePointRemoved(TrackGraph graph, T point, EdgePointType type) { + point.onRemoved(graph); + Couple edgeLocation = point.edgeLocation; + Couple startNodes = edgeLocation.map(graph::locateNode); + startNodes.forEachWithParams((l1, l2) -> { + TrackEdge trackEdge = graph.getConnectionsFrom(l1) + .get(l2); + trackEdge.getEdgeData() + .removePoint(l1, l2, trackEdge, point); + }, startNodes.swap()); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointStorage.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointStorage.java new file mode 100644 index 000000000..95c5d3dd0 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointStorage.java @@ -0,0 +1,92 @@ +package com.simibubi.create.content.logistics.trains.management.edgePoint; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; + +import com.simibubi.create.content.logistics.trains.TrackGraph; +import com.simibubi.create.content.logistics.trains.management.signal.TrackEdgePoint; +import com.simibubi.create.foundation.utility.NBTHelper; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; + +public class EdgePointStorage { + + private Map, Map> pointsByType; + + public EdgePointStorage() { + pointsByType = new HashMap<>(); + } + + public void put(EdgePointType type, TrackEdgePoint point) { + getMap(type).put(point.getId(), point); + } + + @SuppressWarnings("unchecked") + public T get(EdgePointType type, UUID id) { + return (T) getMap(type).get(id); + } + + @SuppressWarnings("unchecked") + public T remove(EdgePointType type, UUID id) { + return (T) getMap(type).remove(id); + } + + @SuppressWarnings("unchecked") + public Collection values(EdgePointType type) { + return getMap(type).values() + .stream() + .map(e -> (T) e) + .toList(); + } + + public Map getMap(EdgePointType type) { + return pointsByType.computeIfAbsent(type, t -> new HashMap<>()); + } + + public void tick(TrackGraph graph) { + pointsByType.values() + .forEach(map -> map.values() + .forEach(p -> p.tick(graph))); + } + + public void transferAll(EdgePointStorage other) { + pointsByType.forEach((type, map) -> other.getMap(type) + .putAll(map)); + pointsByType.clear(); + } + + public CompoundTag write() { + CompoundTag nbt = new CompoundTag(); + for (Entry, Map> entry : pointsByType.entrySet()) { + EdgePointType type = entry.getKey(); + ListTag list = NBTHelper.writeCompoundList(entry.getValue() + .values(), edgePoint -> { + CompoundTag tag = new CompoundTag(); + edgePoint.write(tag); + return tag; + }); + nbt.put(type.getId() + .toString(), list); + } + return nbt; + } + + public void read(CompoundTag nbt) { + for (EdgePointType type : EdgePointType.TYPES.values()) { + ListTag list = nbt.getList(type.getId() + .toString(), Tag.TAG_COMPOUND); + Map map = getMap(type); + NBTHelper.iterateCompoundList(list, tag -> { + TrackEdgePoint edgePoint = type.create(); + edgePoint.read(tag); + map.put(edgePoint.getId(), edgePoint); + }); + } + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointType.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointType.java new file mode 100644 index 000000000..cd260161a --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointType.java @@ -0,0 +1,46 @@ +package com.simibubi.create.content.logistics.trains.management.edgePoint; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.management.GlobalStation; +import com.simibubi.create.content.logistics.trains.management.signal.SignalBoundary; +import com.simibubi.create.content.logistics.trains.management.signal.TrackEdgePoint; + +import net.minecraft.resources.ResourceLocation; + +public class EdgePointType { + + public static final Map> TYPES = new HashMap<>(); + private ResourceLocation id; + private Supplier factory; + + public static final EdgePointType SIGNAL = + register(Create.asResource("signal"), SignalBoundary::new); + public static final EdgePointType STATION = + register(Create.asResource("station"), GlobalStation::new); + + public static EdgePointType register(ResourceLocation id, Supplier factory) { + EdgePointType type = new EdgePointType<>(id, factory); + TYPES.put(id, type); + return type; + } + + public EdgePointType(ResourceLocation id, Supplier factory) { + this.id = id; + this.factory = factory; + } + + public T create() { + T t = factory.get(); + t.setType(this); + return t; + } + + public ResourceLocation getId() { + return id; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduledDelay.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduledDelay.java index 1836e3e4f..f1ce3b0d1 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduledDelay.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduledDelay.java @@ -19,7 +19,7 @@ public class ScheduledDelay extends TimedWaitCondition { @Override public boolean tickCompletion(Level level, Train train, CompoundTag context) { - int time = context.getInt("Time") + 1900; + int time = context.getInt("Time"); if (time >= value * timeUnit.ticksPer) return true; context.putInt("Time", time + 1); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationPoweredCondition.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationPoweredCondition.java index 806b0440d..df309c9ae 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationPoweredCondition.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationPoweredCondition.java @@ -24,7 +24,7 @@ public class StationPoweredCondition extends ScheduleWaitCondition { GlobalStation currentStation = train.getCurrentStation(); if (currentStation == null) return false; - BlockPos stationPos = currentStation.stationPos; + BlockPos stationPos = currentStation.getTilePos(); if (!level.isLoaded(stationPos)) return false; return level.hasNeighborSignal(stationPos); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationUnloadedCondition.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationUnloadedCondition.java index b6da20b72..026b686bc 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationUnloadedCondition.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationUnloadedCondition.java @@ -25,7 +25,7 @@ public class StationUnloadedCondition extends ScheduleWaitCondition { if (currentStation == null) return false; if (level instanceof ServerLevel serverLevel) - return !serverLevel.isPositionEntityTicking(currentStation.stationPos); + return !serverLevel.isPositionEntityTicking(currentStation.getTilePos()); return false; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/EdgeData.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/EdgeData.java index 737f66080..64ed5d18c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/EdgeData.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/EdgeData.java @@ -1,109 +1,77 @@ package com.simibubi.create.content.logistics.trains.management.signal; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.UUID; -import java.util.function.Consumer; -import java.util.function.Function; import javax.annotation.Nullable; 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.management.GlobalStation; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.foundation.utility.NBTHelper; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; public class EdgeData { + public static final UUID passiveGroup = UUID.fromString("00000000-0000-0000-0000-000000000000"); + public UUID singleSignalGroup; - private List boundaries; - private List stations; + private List points; public EdgeData() { - boundaries = new ArrayList<>(); - stations = new ArrayList<>(); - singleSignalGroup = null; + points = new ArrayList<>(); + singleSignalGroup = passiveGroup; } - public boolean hasBoundaries() { - return !boundaries.isEmpty(); + public boolean hasSignalBoundaries() { + return singleSignalGroup == null; } - public boolean hasStations() { - return !stations.isEmpty(); + public boolean hasPoints() { + return !points.isEmpty(); } - public List getBoundaries() { - return boundaries; - } - - public List getStations() { - return stations; + public List getPoints() { + return points; } public void removePoint(TrackNode node1, TrackNode node2, TrackEdge edge, TrackEdgePoint point) { - if (point instanceof GlobalStation gs) - stations.remove(gs); - if (point instanceof SignalBoundary sb) - boundaries.remove(sb); - updateDelegates(node1, node2, edge); + points.remove(point); + if (point.getType() == EdgePointType.SIGNAL) + singleSignalGroup = next(point.getType(), node1, node2, edge, 0) == null ? passiveGroup : null; } - public void addPoint(TrackNode node1, TrackNode node2, TrackEdge edge, T boundary, - Class type) { - T next = next(type, node1, node2, edge, boundary.getLocationOn(node1, node2, edge)); - if (boundary instanceof GlobalStation gs) - stations.add(next == null ? stations.size() : stations.indexOf(next), gs); - if (boundary instanceof SignalBoundary sb) - boundaries.add(next == null ? boundaries.size() : boundaries.indexOf(next), sb); - updateDelegates(node1, node2, edge); - } - - public void updateDelegates(TrackNode node1, TrackNode node2, TrackEdge edge) { - for (GlobalStation globalStation : stations) - globalStation.boundary = getBoundary(node1, node2, edge, globalStation.getLocationOn(node1, node2, edge)); - for (SignalBoundary boundary : boundaries) - boundary.station = getStation(node1, node2, edge, boundary.getLocationOn(node1, node2, edge)); - } - - @Nullable - public SignalBoundary nextBoundary(TrackNode node1, TrackNode node2, TrackEdge edge, double minPosition) { - return next(SignalBoundary.class, node1, node2, edge, minPosition); - } - - @Nullable - public GlobalStation nextStation(TrackNode node1, TrackNode node2, TrackEdge edge, double minPosition) { - return next(GlobalStation.class, node1, node2, edge, minPosition); - } - - @Nullable - public SignalBoundary getBoundary(TrackNode node1, TrackNode node2, TrackEdge edge, double exactPosition) { - return get(SignalBoundary.class, node1, node2, edge, exactPosition); - } - - @Nullable - public GlobalStation getStation(TrackNode node1, TrackNode node2, TrackEdge edge, double exactPosition) { - return get(GlobalStation.class, node1, node2, edge, exactPosition); + public void addPoint(TrackNode node1, TrackNode node2, TrackEdge edge, + TrackEdgePoint point) { + if (point.getType() == EdgePointType.SIGNAL) + singleSignalGroup = null; + double locationOn = point.getLocationOn(node1, node2, edge); + int i = 0; + for (; i < points.size(); i++) + if (points.get(i) + .getLocationOn(node1, node2, edge) > locationOn) + break; + points.add(i, point); } @Nullable @SuppressWarnings("unchecked") - private T next(Class type, TrackNode node1, TrackNode node2, TrackEdge edge, + public T next(EdgePointType type, TrackNode node1, TrackNode node2, TrackEdge edge, double minPosition) { - for (TrackEdgePoint point : type == GlobalStation.class ? stations : boundaries) - if (point.getLocationOn(node1, node2, edge) > minPosition) + for (TrackEdgePoint point : points) + if (point.getType() == type && point.getLocationOn(node1, node2, edge) > minPosition) return (T) point; return null; } @Nullable - private T get(Class type, TrackNode node1, TrackNode node2, TrackEdge edge, + 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)) @@ -112,43 +80,42 @@ public class EdgeData { } public CompoundTag write() { - CompoundTag signalCompound = new CompoundTag(); - if (hasBoundaries()) { - signalCompound.put("Boundaries", NBTHelper.writeCompoundList(boundaries, this::writePoint)); - } else if (singleSignalGroup != null) - signalCompound.putUUID("Group", singleSignalGroup); - if (hasStations()) - signalCompound.put("Stations", NBTHelper.writeCompoundList(stations, this::writePoint)); - return signalCompound; + CompoundTag nbt = new CompoundTag(); + if (singleSignalGroup == passiveGroup) + NBTHelper.putMarker(nbt, "PassiveGroup"); + else if (singleSignalGroup != null) + nbt.putUUID("SignalGroup", singleSignalGroup); + + if (hasPoints()) + nbt.put("Points", NBTHelper.writeCompoundList(points, point -> { + CompoundTag tag = new CompoundTag(); + tag.putUUID("Id", point.id); + tag.putString("Type", point.getType() + .getId() + .toString()); + return tag; + })); + return nbt; } - public static EdgeData read(CompoundTag tag, TrackGraph graph) { - EdgeData signalEdgeData = new EdgeData(); - if (tag.contains("Group")) - signalEdgeData.singleSignalGroup = tag.getUUID("Group"); - if (tag.contains("Boundaries")) - NBTHelper.iterateCompoundList(tag.getList("Boundaries", Tag.TAG_COMPOUND), - readPoint(graph::getSignal, signalEdgeData.boundaries)); - if (tag.contains("Stations")) - NBTHelper.iterateCompoundList(tag.getList("Stations", Tag.TAG_COMPOUND), - readPoint(graph::getStation, signalEdgeData.stations)); - return signalEdgeData; - } + public static EdgeData read(CompoundTag nbt, TrackGraph graph) { + EdgeData data = new EdgeData(); + if (nbt.contains("SignalGroup")) + data.singleSignalGroup = nbt.getUUID("Group"); + else if (!nbt.contains("PassiveGroup")) + data.singleSignalGroup = null; - private CompoundTag writePoint(T point) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putUUID("Id", point.id); - return compoundTag; - } - - private static Consumer readPoint(Function lookup, - Collection target) { - return tag -> { - UUID id = tag.getUUID("Id"); - T signal = lookup.apply(id); - if (signal != null) - target.add(signal); - }; + if (nbt.contains("Points")) + NBTHelper.iterateCompoundList(nbt.getList("Points", Tag.TAG_COMPOUND), tag -> { + ResourceLocation location = new ResourceLocation(tag.getString("Type")); + EdgePointType type = EdgePointType.TYPES.get(location); + if (type == null || !tag.contains("Id")) + return; + TrackEdgePoint point = graph.getPoint(type, tag.getUUID("Id")); + if (point != null) + data.points.add(point); + }); + return data; } } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalBoundary.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalBoundary.java index 662ea069f..5ba3e8047 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalBoundary.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalBoundary.java @@ -5,13 +5,10 @@ import java.util.Map; import java.util.Set; 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.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackNode; -import com.simibubi.create.content.logistics.trains.management.GlobalStation; import com.simibubi.create.content.logistics.trains.management.signal.SignalTileEntity.OverlayState; import com.simibubi.create.content.logistics.trains.management.signal.SignalTileEntity.SignalState; import com.simibubi.create.foundation.utility.Couple; @@ -22,6 +19,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; +import net.minecraft.world.level.LevelAccessor; public class SignalBoundary extends TrackEdgePoint { @@ -29,16 +27,10 @@ public class SignalBoundary extends TrackEdgePoint { public Couple groups; public Couple sidesToUpdate; - @Nullable - public GlobalStation station; - - public SignalBoundary(UUID id, BlockPos tilePosition, boolean front) { - super(id); + public SignalBoundary() { signals = Couple.create(HashSet::new); groups = Couple.create(null, null); sidesToUpdate = Couple.create(true, true); - this.signals.get(front) - .add(tilePosition); } public void setGroup(TrackNode side, UUID groupId) { @@ -47,6 +39,35 @@ public class SignalBoundary extends TrackEdgePoint { sidesToUpdate.set(primary, false); } + @Override + public boolean canMerge() { + return true; + } + + @Override + public void invalidate(LevelAccessor level) { + signals.forEach(s -> s.forEach(pos -> invalidateAt(level, pos))); + } + + @Override + public void tileAdded(BlockPos tilePos, boolean front) { + signals.get(front) + .add(tilePos); + } + + @Override + public void tileRemoved(BlockPos tilePos, boolean front) { + signals.forEach(s -> s.remove(tilePos)); + if (signals.both(Set::isEmpty)) + removeFromAllGraphs(); + } + + @Override + public void onRemoved(TrackGraph graph) { + super.onRemoved(graph); + SignalPropagator.onSignalRemoved(graph, this); + } + public void queueUpdate(TrackNode side) { sidesToUpdate.set(isPrimary(side), true); } @@ -55,14 +76,13 @@ public class SignalBoundary extends TrackEdgePoint { return groups.get(isPrimary(side)); } + @Override public boolean canNavigateVia(TrackNode side) { - return !signals.get(!isPrimary(side)) + return !signals.get(isPrimary(side)) .isEmpty(); } public OverlayState getOverlayFor(BlockPos tile) { - if (station != null) - return OverlayState.SKIP; for (boolean first : Iterate.trueAndFalse) { Set set = signals.get(first); for (BlockPos blockPos : set) { @@ -92,7 +112,9 @@ public class SignalBoundary extends TrackEdgePoint { return SignalState.INVALID; } + @Override public void tick(TrackGraph graph) { + super.tick(graph); for (boolean front : Iterate.trueAndFalse) { if (!sidesToUpdate.get(front)) continue; @@ -101,12 +123,13 @@ public class SignalBoundary extends TrackEdgePoint { } } - public SignalBoundary(CompoundTag nbt) { - super(nbt); + @Override + public void read(CompoundTag nbt) { + super.read(nbt); + sidesToUpdate = Couple.create(true, true); signals = Couple.create(HashSet::new); groups = Couple.create(null, null); - sidesToUpdate = Couple.create(true, true); for (int i = 1; i <= 2; i++) if (nbt.contains("Tiles" + i)) { @@ -121,12 +144,9 @@ public class SignalBoundary extends TrackEdgePoint { sidesToUpdate.set(i == 1, nbt.contains("Update" + i)); } - public CompoundTag write() { - CompoundTag nbt = new CompoundTag(); - nbt.putUUID("Id", id); - nbt.putDouble("Position", position); - nbt.put("Edge", edgeLocation.serializeEach(loc -> NbtUtils.writeBlockPos(new BlockPos(loc)))); - + @Override + public void write(CompoundTag nbt) { + super.write(nbt); for (int i = 1; i <= 2; i++) if (!signals.get(i == 1) .isEmpty()) @@ -137,7 +157,6 @@ public class SignalBoundary extends TrackEdgePoint { for (int i = 1; i <= 2; i++) if (sidesToUpdate.get(i == 1)) nbt.putBoolean("Update" + i, true); - return nbt; } } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalPropagator.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalPropagator.java index 008813a99..b6cca8556 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalPropagator.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalPropagator.java @@ -16,38 +16,13 @@ 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.entity.Train; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Pair; public class SignalPropagator { - public static void onEdgePointAdded(TrackGraph graph, T point, Class type) { - Couple edgeLocation = point.edgeLocation; - Couple startNodes = edgeLocation.map(graph::locateNode); - Couple startEdges = startNodes.mapWithParams((l1, l2) -> graph.getConnectionsFrom(l1) - .get(l2), startNodes.swap()); - - for (boolean front : Iterate.trueAndFalse) { - TrackNode node1 = startNodes.get(front); - TrackNode node2 = startNodes.get(!front); - TrackEdge startEdge = startEdges.get(front); - startEdge.getEdgeData() - .addPoint(node1, node2, startEdge, point, type); - } - } - - public static void onEdgePointRemoved(TrackGraph graph, T point, Class type) { - Couple edgeLocation = point.edgeLocation; - Couple startNodes = edgeLocation.map(graph::locateNode); - startNodes.forEachWithParams((l1, l2) -> { - TrackEdge trackEdge = graph.getConnectionsFrom(l1) - .get(l2); - trackEdge.getEdgeData() - .removePoint(l1, l2, trackEdge, point); - }, startNodes.swap()); - } - public static void onSignalRemoved(TrackGraph graph, SignalBoundary signal) { signal.sidesToUpdate.map($ -> false); for (boolean front : Iterate.trueAndFalse) { @@ -61,8 +36,6 @@ public class SignalPropagator { return false; }, Predicates.alwaysFalse()); } - - onEdgePointRemoved(graph, signal, SignalBoundary.class); } public static void notifySignalsOfNewNode(TrackGraph graph, TrackNode node) { @@ -118,7 +91,7 @@ public class SignalPropagator { // Check for signal on the same edge SignalBoundary immediateBoundary = startEdge.getEdgeData() - .nextBoundary(node1, node2, startEdge, signal.getLocationOn(node1, node2, startEdge)); + .next(EdgePointType.SIGNAL, node1, node2, startEdge, signal.getLocationOn(node1, node2, startEdge)); if (immediateBoundary != null) { if (boundaryCallback.test(Pair.of(node1, immediateBoundary))) notifyTrains(graph, startEdge, startEdges.get(!front)); @@ -160,14 +133,15 @@ public class SignalPropagator { EdgeData signalData = currentEdge.getEdgeData(); // no boundary- update group of edge - if (!signalData.hasBoundaries()) { + if (!signalData.hasSignalBoundaries()) { if (nonBoundaryCallback.test(signalData)) notifyTrains(graph, currentEdge); continue; } // other/own boundary found - SignalBoundary nextBoundary = signalData.nextBoundary(currentNode, nextNode, currentEdge, 0); + SignalBoundary nextBoundary = + signalData.next(EdgePointType.SIGNAL, currentNode, nextNode, currentEdge, 0); if (boundaryCallback.test(Pair.of(currentNode, nextBoundary))) notifyTrains(graph, edge, oppositeEdge); continue EdgeWalk; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalRenderer.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalRenderer.java index 60b8b8e83..552a698a4 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalRenderer.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalRenderer.java @@ -39,7 +39,7 @@ public class SignalRenderer extends SafeTileEntityRenderer { .renderInto(ms, buffer.getBuffer(RenderType.solid())); BlockPos pos = te.getBlockPos(); - TrackTargetingBehaviour target = te.getTarget(); + TrackTargetingBehaviour target = te.edgePoint; BlockPos targetPosition = target.getGlobalPosition(); Level level = te.getLevel(); BlockState trackState = level.getBlockState(targetPosition); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalTileEntity.java index 11470e3af..50c1f6cba 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SignalTileEntity.java @@ -1,23 +1,17 @@ package com.simibubi.create.content.logistics.trains.management.signal; import java.util.List; -import java.util.UUID; -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.management.GraphLocation; +import javax.annotation.Nullable; + import com.simibubi.create.content.logistics.trains.management.TrackTargetingBehaviour; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; -import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.NBTHelper; import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction.AxisDirection; import net.minecraft.nbt.CompoundTag; -import net.minecraft.util.Mth; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -37,20 +31,19 @@ public class SignalTileEntity extends SmartTileEntity { return this == RED || this == INVALID && renderTime % 40 < 3; } - public boolean isGreenLight(float renderTime) { + public boolean isGreenLight(float renderTime) { return this == GREEN || this == TRAIN_ENTERING; } } - public UUID id; - + public TrackTargetingBehaviour edgePoint; + private SignalState state; private OverlayState overlay; private int switchToRedAfterTrainEntered; public SignalTileEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); - id = UUID.randomUUID(); this.state = SignalState.INVALID; this.overlay = OverlayState.SKIP; } @@ -58,7 +51,6 @@ public class SignalTileEntity extends SmartTileEntity { @Override protected void write(CompoundTag tag, boolean clientPacket) { super.write(tag, clientPacket); - tag.putUUID("Id", id); NBTHelper.writeEnum(tag, "State", state); NBTHelper.writeEnum(tag, "Overlay", overlay); } @@ -66,12 +58,16 @@ public class SignalTileEntity extends SmartTileEntity { @Override protected void read(CompoundTag tag, boolean clientPacket) { super.read(tag, clientPacket); - id = tag.getUUID("Id"); state = NBTHelper.readEnum(tag, "State", SignalState.class); overlay = NBTHelper.readEnum(tag, "Overlay", OverlayState.class); invalidateRenderBoundingBox(); } + @Nullable + public SignalBoundary getSignal() { + return edgePoint.getEdgePoint(); + } + public boolean isPowered() { return state == SignalState.RED; } @@ -89,7 +85,8 @@ public class SignalTileEntity extends SmartTileEntity { @Override public void addBehaviours(List behaviours) { - behaviours.add(new TrackTargetingBehaviour(this)); + edgePoint = new TrackTargetingBehaviour<>(this, EdgePointType.SIGNAL); + behaviours.add(edgePoint); } @Override @@ -97,7 +94,7 @@ public class SignalTileEntity extends SmartTileEntity { super.tick(); if (level.isClientSide) return; - SignalBoundary boundary = getOrCreateSignalBoundary(); + SignalBoundary boundary = getSignal(); if (boundary == null) { enterState(SignalState.INVALID); setOverlay(OverlayState.RENDER); @@ -107,86 +104,10 @@ public class SignalTileEntity extends SmartTileEntity { setOverlay(boundary.getOverlayFor(worldPosition)); } - @Override - protected void setRemovedNotDueToChunkUnload() { - if (!getTarget().hasValidTrack() || level.isClientSide) { - super.setRemovedNotDueToChunkUnload(); - return; - } - - for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values()) { - SignalBoundary signal = trackGraph.getSignal(id); - if (signal == null) - continue; - for (boolean front : Iterate.trueAndFalse) - signal.signals.get(front) - .remove(worldPosition); - if (signal.signals.getFirst() - .isEmpty() - && signal.signals.getSecond() - .isEmpty()) - trackGraph.removeSignal(id); - } - - super.setRemovedNotDueToChunkUnload(); - } - - public SignalBoundary getOrCreateSignalBoundary() { - for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values()) { - SignalBoundary signal = trackGraph.getSignal(id); - if (signal == null) - continue; - return signal; - } - - if (level.isClientSide) - return null; - - TrackTargetingBehaviour target = getTarget(); - if (!target.hasValidTrack()) - return null; - GraphLocation loc = target.determineGraphLocation(); - if (loc == null) - return null; - - TrackGraph graph = loc.graph; - TrackNode node1 = graph.locateNode(loc.edge.getFirst()); - TrackNode node2 = graph.locateNode(loc.edge.getSecond()); - TrackEdge edge = graph.getConnectionsFrom(node1) - .get(node2); - boolean positive = target.getTargetDirection() == AxisDirection.POSITIVE; - - if (edge == null) - return null; - - EdgeData signalData = edge.getEdgeData(); - if (signalData.hasBoundaries()) { - SignalBoundary nextBoundary = signalData.nextBoundary(node1, node2, edge, loc.position - .25f); - if (nextBoundary != null && Mth.equal(nextBoundary.getLocationOn(node1, node2, edge), loc.position)) { - nextBoundary.signals.get(positive) - .add(worldPosition); - id = nextBoundary.id; - setChanged(); - return nextBoundary; - } - } - - SignalBoundary signal = new SignalBoundary(id, worldPosition, positive); - signal.setLocation(positive ? loc.edge : loc.edge.swap(), - positive ? loc.position : edge.getLength(node1, node2) - loc.position); - graph.addSignal(signal); - setChanged(); - return signal; - } - - public TrackTargetingBehaviour getTarget() { - return getBehaviour(TrackTargetingBehaviour.TYPE); - } - public SignalState getState() { return state; } - + public OverlayState getOverlay() { return overlay; } @@ -199,7 +120,7 @@ public class SignalTileEntity extends SmartTileEntity { } public void enterState(SignalState state) { - if (switchToRedAfterTrainEntered > 0) + if (switchToRedAfterTrainEntered > 0) switchToRedAfterTrainEntered--; if (this.state == state) return; @@ -213,7 +134,7 @@ public class SignalTileEntity extends SmartTileEntity { @Override protected AABB createRenderBoundingBox() { - return new AABB(worldPosition, getTarget().getGlobalPosition()).inflate(2); + return new AABB(worldPosition, edgePoint.getGlobalPosition()).inflate(2); } } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SingleTileEdgePoint.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SingleTileEdgePoint.java new file mode 100644 index 000000000..d55080d54 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/SingleTileEdgePoint.java @@ -0,0 +1,55 @@ +package com.simibubi.create.content.logistics.trains.management.signal; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.world.level.LevelAccessor; + +public abstract class SingleTileEdgePoint extends TrackEdgePoint { + + public BlockPos tilePos; + + public BlockPos getTilePos() { + return tilePos; + } + + @Override + public void tileAdded(BlockPos tilePos, boolean front) { + this.tilePos = tilePos; + } + + @Override + public void tileRemoved(BlockPos tilePos, boolean front) { + removeFromAllGraphs(); + } + + @Override + public void invalidate(LevelAccessor level) { + invalidateAt(level, tilePos); + } + + @Override + public boolean canMerge() { + return false; + } + + @Override + public final void read(CompoundTag nbt) { + read(nbt, false); + } + + public void read(CompoundTag nbt, boolean migration) { + if (migration) + return; + + super.read(nbt); + tilePos = NbtUtils.readBlockPos(nbt.getCompound("TilePos")); + } + + @Override + public void write(CompoundTag nbt) { + super.write(nbt); + nbt.put("TilePos", NbtUtils.writeBlockPos(tilePos)); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/TrackEdgePoint.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/TrackEdgePoint.java index 4c44729fd..e14733c00 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/TrackEdgePoint.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/signal/TrackEdgePoint.java @@ -2,32 +2,68 @@ package com.simibubi.create.content.logistics.trains.management.signal; import java.util.UUID; +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.TrackTargetingBehaviour; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; +import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; 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; +import net.minecraft.world.level.LevelAccessor; -public class TrackEdgePoint { +public abstract class TrackEdgePoint { public UUID id; public Couple edgeLocation; public double position; + private EdgePointType type; - public TrackEdgePoint(UUID id) { + public void setId(UUID id) { this.id = id; } - - public TrackEdgePoint(CompoundTag nbt) { - id = nbt.getUUID("Id"); - position = nbt.getDouble("Position"); - edgeLocation = Couple.deserializeEach(nbt.getList("Edge", Tag.TAG_COMPOUND), - tag -> TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(tag))); + + public UUID getId() { + return id; } + public void setType(EdgePointType type) { + this.type = type; + } + + public EdgePointType getType() { + return type; + } + + public abstract boolean canMerge(); + + public boolean canCoexistWith(EdgePointType otherType, boolean front) { + return false; + } + + public abstract void invalidate(LevelAccessor level); + + protected void invalidateAt(LevelAccessor level, BlockPos tilePos) { + TrackTargetingBehaviour behaviour = TileEntityBehaviour.get(level, tilePos, TrackTargetingBehaviour.TYPE); + if (behaviour == null) + return; + CompoundTag migrationData = new CompoundTag(); + write(migrationData); + behaviour.invalidateEdgePoint(migrationData); + } + + public abstract void tileAdded(BlockPos tilePos, boolean front); + + public abstract void tileRemoved(BlockPos tilePos, boolean front); + + public void onRemoved(TrackGraph graph) {} + public void setLocation(Couple nodes, double position) { this.edgeLocation = nodes; this.position = position; @@ -37,9 +73,34 @@ public class TrackEdgePoint { return isPrimary(node1) ? edge.getLength(node1, node2) - position : position; } + public boolean canNavigateVia(TrackNode side) { + return true; + } + public boolean isPrimary(TrackNode node1) { return edgeLocation.getSecond() .equals(node1.getLocation()); } + public void read(CompoundTag nbt) { + id = nbt.getUUID("Id"); + position = nbt.getDouble("Position"); + edgeLocation = Couple.deserializeEach(nbt.getList("Edge", Tag.TAG_COMPOUND), + tag -> TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(tag))); + } + + public void write(CompoundTag nbt) { + nbt.putUUID("Id", id); + nbt.putDouble("Position", position); + nbt.put("Edge", edgeLocation.serializeEach(loc -> NbtUtils.writeBlockPos(new BlockPos(loc)))); + } + + public void tick(TrackGraph graph) {} + + protected void removeFromAllGraphs() { + for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values()) + if (trackGraph.removePoint(getType(), id) != null) + return; + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java index 476e924ec..2035c344d 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java @@ -228,7 +228,7 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac if (!entry.getValue() .isInside(pos)) continue; - if (world.getBlockEntity(entry.getKey())instanceof StationTileEntity station) + if (world.getBlockEntity(entry.getKey()) instanceof StationTileEntity station) station.trackClicked(player, this, state, pos); } return InteractionResult.SUCCESS; @@ -245,7 +245,7 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac BlockPos girderPos = pPos.below() .offset(vec3.z * side, 0, vec3.x * side); BlockState girderState = pLevel.getBlockState(girderPos); - if (girderState.getBlock()instanceof GirderBlock girderBlock + if (girderState.getBlock() instanceof GirderBlock girderBlock && !blockTicks.hasScheduledTick(girderPos, girderBlock)) pLevel.scheduleTick(girderPos, girderBlock, 1); } @@ -348,8 +348,8 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac @OnlyIn(Dist.CLIENT) public PartialModel prepareAssemblyOverlay(BlockGetter world, BlockPos pos, BlockState state, Direction direction, PoseStack ms) { - TransformStack.cast(ms).rotateCentered(Direction.UP, - AngleHelper.rad(AngleHelper.horizontalAngle(direction))); + TransformStack.cast(ms) + .rotateCentered(Direction.UP, AngleHelper.rad(AngleHelper.horizontalAngle(direction))); return AllBlockPartials.TRACK_ASSEMBLING_OVERLAY; } @@ -365,11 +365,13 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac Vec3 normal = getUpNormal(world, pos, state); Vec3 angles = TrackRenderer.getModelAngles(normal, directionVec); - TransformStack.cast(ms).centre() + TransformStack.cast(ms) + .centre() .rotateYRadians(angles.y) .rotateXRadians(angles.x) .unCentre() - .translate(0, axis.y != 0 ? 7 / 16f : 0, axis.y != 0 ? direction.getStep() * 2.5f / 16f : 0); + .translate(0, axis.y != 0 ? 7 / 16f : 0, axis.y != 0 ? direction.getStep() * 2.5f / 16f : 0) + .scale(type == RenderedTrackOverlayType.STATION ? 1 + 1 / 512f : 1); return type == RenderedTrackOverlayType.STATION ? AllBlockPartials.TRACK_STATION_OVERLAY : type == RenderedTrackOverlayType.SIGNAL ? AllBlockPartials.TRACK_SIGNAL_OVERLAY diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java index 9e87ab88e..0fbf46901 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java @@ -93,6 +93,7 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE TrackTileEntity other = (TrackTileEntity) blockEntity; other.removeConnection(bezierConnection.tePositions.getFirst()); } + AllPackets.channel.send(packetTarget(), new RemoveTileEntityPacket(worldPosition)); } @Override diff --git a/src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java b/src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java index 458f30566..88df753b2 100644 --- a/src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java +++ b/src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java @@ -13,6 +13,7 @@ import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.content.logistics.trains.management.GlobalStation; import com.simibubi.create.content.logistics.trains.management.ScheduleRuntime; import com.simibubi.create.content.logistics.trains.management.ScheduleRuntime.State; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.signal.SignalBoundary; import net.minecraft.ChatFormatting; @@ -62,11 +63,11 @@ public class DumpRailwaysCommand { + graph.getNodes() .size() + " Nodes", graph.color.getRGB()); - Collection signals = graph.getSignals(); + Collection signals = graph.getPoints(EdgePointType.SIGNAL); if (!signals.isEmpty()) chat.accept(" -> " + signals.size() + " registered Signals", blue); - for (GlobalStation globalStation : graph.getStations()) { - BlockPos pos = globalStation.stationPos; + for (GlobalStation globalStation : graph.getPoints(EdgePointType.STATION)) { + BlockPos pos = globalStation.getTilePos(); chat.accept(" -> " + globalStation.name + " (" + globalStation.id.toString() .substring(0, 5) + ") [" + pos.getX() + "," + pos.getY() + "," + pos.getZ() + "]", darkBlue); if (globalStation.getPresentTrain() != null) { diff --git a/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java b/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java index 4355880b0..a7a333a82 100644 --- a/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java +++ b/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java @@ -129,12 +129,11 @@ public abstract class SmartTileEntity extends CachedRenderBBTileEntity implement } protected void setRemovedNotDueToChunkUnload() { - + forEachBehaviour(TileEntityBehaviour::remove); } @Override public void setRemoved() { - forEachBehaviour(TileEntityBehaviour::remove); super.setRemoved(); if (!unloaded) { diff --git a/src/main/java/com/simibubi/create/foundation/utility/Couple.java b/src/main/java/com/simibubi/create/foundation/utility/Couple.java index 2e3763938..36b257a23 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/Couple.java +++ b/src/main/java/com/simibubi/create/foundation/utility/Couple.java @@ -6,6 +6,7 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; @@ -62,6 +63,14 @@ public class Couple extends Pair implements Iterable { return Couple.create(function.apply(first, values.first), function.apply(second, values.second)); } + public boolean both(Predicate test) { + return test.test(getFirst()) && test.test(getSecond()); + } + + public boolean either(Predicate test) { + return test.test(getFirst()) || test.test(getSecond()); + } + public void replace(Function function) { setFirst(function.apply(getFirst())); setSecond(function.apply(getSecond()));