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
This commit is contained in:
simibubi 2022-02-28 22:52:14 +01:00
parent cf308cdc7f
commit 3362e6a05e
32 changed files with 884 additions and 659 deletions

View file

@ -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<TrackGraph> getGraphs(LevelAccessor level, TrackNodeLocation vertex) {
if (trackNetworks == null)
return Collections.emptyList();
ArrayList<TrackGraph> 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);
}

View file

@ -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<TrackNodeLocation, TrackNode> nodes;
Map<Integer, TrackNode> nodesById;
Map<TrackNode, Map<TrackNode, TrackEdge>> connectionsByNode;
private Map<UUID, GlobalStation> stations;
private Map<UUID, SignalBoundary> 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<GlobalStation> getStations() {
return stations.values();
}
public Collection<SignalBoundary> getSignals() {
return signals.values();
}
public void addStation(GlobalStation station) {
stations.put(station.id, station);
SignalPropagator.onEdgePointAdded(this, station, GlobalStation.class);
public <T extends TrackEdgePoint> void addPoint(EdgePointType<T> 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 extends TrackEdgePoint> T getPoint(EdgePointType<T> type, UUID id) {
return edgePoints.get(type, id);
}
public void removeStation(UUID id) {
stations.remove(id);
markDirty();
public <T extends TrackEdgePoint> Collection<T> getPoints(EdgePointType<T> 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 extends TrackEdgePoint> T removePoint(EdgePointType<T> 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<UUID> iterator = stations.keySet()
.iterator(); iterator.hasNext();) {
UUID uuid = iterator.next();
GlobalStation globalStation = stations.get(uuid);
Couple<TrackNodeLocation> loc = globalStation.edgeLocation;
if (loc.getFirst()
.equals(location)
|| loc.getSecond()
.equals(location)) {
globalStation.migrate(level);
iterator.remove();
}
}
}
Map<UUID, Train> trains = Create.RAILWAYS.trains;
for (Iterator<UUID> iterator = trains.keySet()
.iterator(); iterator.hasNext();) {
@ -193,13 +167,18 @@ public class TrackGraph {
Map<TrackNode, TrackEdge> 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<UUID, Train> 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<TrackNode, TrackEdge> connections = getConnectionsFrom(node);
Map<UUID, Train> trains = Create.RAILWAYS.trains;
if (!connections.isEmpty()) {
target.connectionsByNode.put(node, connections);
for (TrackNode entry : connections.keySet()) {
for (Iterator<UUID> iterator = stations.keySet()
.iterator(); iterator.hasNext();) {
UUID uuid = iterator.next();
GlobalStation globalStation = stations.get(uuid);
Couple<TrackNodeLocation> 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<UUID, Train> trains = Create.RAILWAYS.trains;
for (Iterator<UUID> 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<Integer, TrackNode> 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)

View file

@ -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<Integer> ids = pair.getFirst();
return ids.getFirst()
.intValue() == nodeId
|| ids.getSecond()
.intValue() == nodeId;
});
}
public void graphSplit(TrackGraph graph, Set<TrackGraph> 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<Integer, Pair<TrackNodeLocation, Vec3>> entry : addedNodes.entrySet())
railGraph.createSpecificNode(entry.getValue()
.getFirst(), entry.getKey(),
entry.getValue()
.getSecond());
for (Entry<Integer, Pair<TrackNodeLocation, Vec3>> entry : addedNodes.entrySet()) {
Integer nodeId = entry.getKey();
Pair<TrackNodeLocation, Vec3> nodeLocation = entry.getValue();
railGraph.loadNode(nodeLocation.getFirst(), nodeId, nodeLocation.getSecond());
}
for (Pair<Couple<Integer>, TrackEdge> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst()

View file

@ -38,27 +38,24 @@ public class TrackPropagator {
Collection<DiscoveredLocation> 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<TrackGraph> 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<BlockPos> 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<TrackGraph> 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<DiscoveredLocation> 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<TrackNode> 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;

View file

@ -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<TrackNodeLocation> target = destination.edgeLocation;
MutableObject<Pair<Double, List<TrackEdge>>> frontResult = new MutableObject<>(Pair.of(null, path));
MutableObject<Pair<Double, List<TrackEdge>>> 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<Couple<TrackNode>, 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<Boolean, TrackEdge> 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<Couple<TrackNode>, 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<TrackNodeLocation> 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<Map<TrackEdge, Pair<Boolean, TrackEdge>>, Pair<Double, Pair<Couple<TrackNode>, 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<Double, Pair<Couple<TrackNode>, 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<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>();
EdgeWalk: for (Entry<TrackNode, TrackEdge> entry : graph.getConnectionsFrom(node2)
.entrySet()) {
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node2);
for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) {
TrackNode newNode = entry.getKey();
TrackEdge newEdge = entry.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false);
@ -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<TrackEdge, Pair<Boolean, TrackEdge>> reachedVia,
Pair<Couple<TrackNode>, TrackEdge> current, GlobalStation station);
}
}

View file

@ -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<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.signalEdgeGroups;
MutableObject<UUID> 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);

View file

@ -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<SignalBoundary> 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)

View file

@ -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<Train> nearestTrain;
@Nullable
public SignalBoundary boundary;
public GlobalStation(UUID id, BlockPos stationPos) {
super(id);
this.stationPos = stationPos;
public GlobalStation() {
name = "Track Station";
nearestTrain = new WeakReference<Train>(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<Train>(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<TrackNodeLocation> 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;
}
}

View file

@ -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);

View file

@ -75,10 +75,10 @@ public class StationBlock extends HorizontalDirectionalBlock implements ITE<Stat
protected void displayScreen(StationTileEntity te, Player player) {
if (!(player instanceof LocalPlayer))
return;
GlobalStation station = te.getOrCreateGlobalStation();
if (station == null)
return;
GlobalStation station = te.getStation();
BlockState blockState = te.getBlockState();
if (station == null || blockState == null)
return;
boolean assembling = blockState.getBlock() == this && blockState.getValue(ASSEMBLING);
ScreenOpener.open(assembling ? new AssemblyScreen(te, station) : new StationScreen(te, station));
}

View file

@ -73,7 +73,7 @@ public class StationEditPacket extends TileEntityConfigurationPacket<StationTile
BlockState blockState = level.getBlockState(blockPos);
if (!name.isBlank()) {
GlobalStation station = te.getOrCreateGlobalStation();
GlobalStation station = te.getStation();
if (station != null)
station.name = name;
Create.RAILWAYS.markTracksDirty();
@ -113,12 +113,11 @@ public class StationEditPacket extends TileEntityConfigurationPacket<StationTile
}
private boolean disassembleAndEnterMode(StationTileEntity te) {
GlobalStation station = te.getOrCreateGlobalStation();
GlobalStation station = te.getStation();
if (station != null) {
Train train = station.getPresentTrain();
if (train != null && !train.disassemble(te.getAssemblyDirection(), te.getTarget()
.getGlobalPosition()
.above()))
BlockPos trackPosition = te.edgePoint.getGlobalPosition();
if (train != null && !train.disassemble(te.getAssemblyDirection(), trackPosition.above()))
return false;
}
return te.tryEnterAssemblyMode();

View file

@ -29,7 +29,7 @@ public class StationRenderer extends SafeTileEntityRenderer<StationTileEntity> {
int light, int overlay) {
BlockPos pos = te.getBlockPos();
TrackTargetingBehaviour target = te.getTarget();
TrackTargetingBehaviour<GlobalStation> target = te.edgePoint;
BlockPos targetPosition = target.getGlobalPosition();
Level level = te.getLevel();
@ -38,7 +38,7 @@ public class StationRenderer extends SafeTileEntityRenderer<StationTileEntity> {
if (!(block instanceof ITrackBlock))
return;
GlobalStation station = te.getOrCreateGlobalStation();
GlobalStation station = te.getStation();
if (!te.getBlockState()
.getValue(StationBlock.ASSEMBLING) || station == null || station.getPresentTrain() != null) {

View file

@ -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<GlobalStation> 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<TileEntityBehaviour> 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);
}
}

View file

@ -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<T extends TrackEdgePoint> extends TileEntityBehaviour {
public static final BehaviourType<TrackTargetingBehaviour> TYPE = new BehaviourType<>();
public static final BehaviourType<TrackTargetingBehaviour<?>> TYPE = new BehaviourType<>();
private BlockPos targetTrack;
private AxisDirection targetDirection;
private UUID id;
public TrackTargetingBehaviour(SmartTileEntity te) {
private CompoundTag migrationData;
private EdgePointType<T> edgePointType;
private T edgePoint;
public TrackTargetingBehaviour(SmartTileEntity te, EdgePointType<T> 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;

View file

@ -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 <T extends TrackEdgePoint> void onEdgePointAdded(TrackGraph graph, T point, EdgePointType<T> type) {
Couple<TrackNodeLocation> edgeLocation = point.edgeLocation;
Couple<TrackNode> startNodes = edgeLocation.map(graph::locateNode);
Couple<TrackEdge> 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 <T extends TrackEdgePoint> void onEdgePointRemoved(TrackGraph graph, T point, EdgePointType<T> type) {
point.onRemoved(graph);
Couple<TrackNodeLocation> edgeLocation = point.edgeLocation;
Couple<TrackNode> 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());
}
}

View file

@ -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<EdgePointType<?>, Map<UUID, TrackEdgePoint>> pointsByType;
public EdgePointStorage() {
pointsByType = new HashMap<>();
}
public <T extends TrackEdgePoint> void put(EdgePointType<T> type, TrackEdgePoint point) {
getMap(type).put(point.getId(), point);
}
@SuppressWarnings("unchecked")
public <T extends TrackEdgePoint> T get(EdgePointType<T> type, UUID id) {
return (T) getMap(type).get(id);
}
@SuppressWarnings("unchecked")
public <T extends TrackEdgePoint> T remove(EdgePointType<T> type, UUID id) {
return (T) getMap(type).remove(id);
}
@SuppressWarnings("unchecked")
public <T extends TrackEdgePoint> Collection<T> values(EdgePointType<T> type) {
return getMap(type).values()
.stream()
.map(e -> (T) e)
.toList();
}
public Map<UUID, TrackEdgePoint> getMap(EdgePointType<? extends TrackEdgePoint> 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<EdgePointType<?>, Map<UUID, TrackEdgePoint>> 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<UUID, TrackEdgePoint> map = getMap(type);
NBTHelper.iterateCompoundList(list, tag -> {
TrackEdgePoint edgePoint = type.create();
edgePoint.read(tag);
map.put(edgePoint.getId(), edgePoint);
});
}
}
}

View file

@ -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<T extends TrackEdgePoint> {
public static final Map<ResourceLocation, EdgePointType<?>> TYPES = new HashMap<>();
private ResourceLocation id;
private Supplier<T> factory;
public static final EdgePointType<SignalBoundary> SIGNAL =
register(Create.asResource("signal"), SignalBoundary::new);
public static final EdgePointType<GlobalStation> STATION =
register(Create.asResource("station"), GlobalStation::new);
public static <T extends TrackEdgePoint> EdgePointType<T> register(ResourceLocation id, Supplier<T> factory) {
EdgePointType<T> type = new EdgePointType<>(id, factory);
TYPES.put(id, type);
return type;
}
public EdgePointType(ResourceLocation id, Supplier<T> factory) {
this.id = id;
this.factory = factory;
}
public T create() {
T t = factory.get();
t.setType(this);
return t;
}
public ResourceLocation getId() {
return id;
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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<SignalBoundary> boundaries;
private List<GlobalStation> stations;
private List<TrackEdgePoint> 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<SignalBoundary> getBoundaries() {
return boundaries;
}
public List<GlobalStation> getStations() {
return stations;
public List<TrackEdgePoint> 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 <T extends TrackEdgePoint> void addPoint(TrackNode node1, TrackNode node2, TrackEdge edge, T boundary,
Class<T> 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 <T extends TrackEdgePoint> 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 extends TrackEdgePoint> T next(Class<T> type, TrackNode node1, TrackNode node2, TrackEdge edge,
public <T extends TrackEdgePoint> T next(EdgePointType<T> 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 extends TrackEdgePoint> T get(Class<T> type, TrackNode node1, TrackNode node2, TrackEdge edge,
public <T extends TrackEdgePoint> T get(EdgePointType<T> type, TrackNode node1, TrackNode node2, TrackEdge edge,
double exactPosition) {
T next = next(type, node1, node2, edge, exactPosition - .5f);
if (next != null && Mth.equal(next.getLocationOn(node1, node2, edge), exactPosition))
@ -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 <T extends TrackEdgePoint> CompoundTag writePoint(T point) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putUUID("Id", point.id);
return compoundTag;
}
private static <T extends TrackEdgePoint> Consumer<CompoundTag> readPoint(Function<UUID, T> lookup,
Collection<T> 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;
}
}

View file

@ -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<UUID> groups;
public Couple<Boolean> 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<BlockPos> 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;
}
}

View file

@ -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 <T extends TrackEdgePoint> void onEdgePointAdded(TrackGraph graph, T point, Class<T> type) {
Couple<TrackNodeLocation> edgeLocation = point.edgeLocation;
Couple<TrackNode> startNodes = edgeLocation.map(graph::locateNode);
Couple<TrackEdge> 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 <T extends TrackEdgePoint> void onEdgePointRemoved(TrackGraph graph, T point, Class<T> type) {
Couple<TrackNodeLocation> edgeLocation = point.edgeLocation;
Couple<TrackNode> 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;

View file

@ -39,7 +39,7 @@ public class SignalRenderer extends SafeTileEntityRenderer<SignalTileEntity> {
.renderInto(ms, buffer.getBuffer(RenderType.solid()));
BlockPos pos = te.getBlockPos();
TrackTargetingBehaviour target = te.getTarget();
TrackTargetingBehaviour<SignalBoundary> target = te.edgePoint;
BlockPos targetPosition = target.getGlobalPosition();
Level level = te.getLevel();
BlockState trackState = level.getBlockState(targetPosition);

View file

@ -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<SignalBoundary> 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<TileEntityBehaviour> 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);
}
}

View file

@ -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));
}
}

View file

@ -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<TrackNodeLocation> 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<TrackNodeLocation> 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;
}
}

View file

@ -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

View file

@ -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

View file

@ -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<SignalBoundary> signals = graph.getSignals();
Collection<SignalBoundary> 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) {

View file

@ -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) {

View file

@ -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<T> extends Pair<T, T> implements Iterable<T> {
return Couple.create(function.apply(first, values.first), function.apply(second, values.second));
}
public boolean both(Predicate<T> test) {
return test.test(getFirst()) && test.test(getSecond());
}
public boolean either(Predicate<T> test) {
return test.test(getFirst()) || test.test(getSecond());
}
public void replace(Function<T, T> function) {
setFirst(function.apply(getFirst()));
setSecond(function.apply(getSecond()));