The Client is always right

- Fixed edge points getting wiped near where two graphs just merged
- Fixed edge points not migrating to modified edges properly
- Graph edge points are now synched by netcode (signals, stations)
- Fixed incorrect stopping position when approaching station manually while the train is still moving backwards
- Fixed single-group edges not being deserialised correctly
- Edge data is now synched by netcode
- Signal edge groups are now synched by netcode
- Signal block indication can now get necessary information client-side
This commit is contained in:
simibubi 2022-03-04 18:30:18 +01:00
parent 3362e6a05e
commit 3c64ada850
23 changed files with 592 additions and 245 deletions

View file

@ -46,6 +46,7 @@ public class GlobalRailwayManager {
loadTrackData(serverPlayer.getServer());
trackNetworks.values()
.forEach(g -> sync.sendFullGraphTo(g, serverPlayer));
sync.sendEdgeGroups(signalEdgeGroups.keySet(), serverPlayer);
}
}
@ -150,21 +151,21 @@ public class GlobalRailwayManager {
for (Train train : trains.values())
train.tick(level);
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H)) {
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H) && AllKeys.altDown())
trackNetworks.values()
.forEach(TrackGraph::debugViewSignalData);
}
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && AllKeys.altDown()) {
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && AllKeys.altDown())
trackNetworks.values()
.forEach(TrackGraph::debugViewNodes);
}
}
public void clientTick() {
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && !AllKeys.altDown()) {
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H) && !AllKeys.altDown())
trackNetworks.values()
.forEach(TrackGraph::debugViewSignalData);
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && !AllKeys.altDown())
trackNetworks.values()
.forEach(TrackGraph::debugViewNodes);
}
}
}

View file

@ -75,6 +75,7 @@ public class TrackGraph {
public <T extends TrackEdgePoint> void addPoint(EdgePointType<T> type, T point) {
edgePoints.put(type, point);
EdgePointManager.onEdgePointAdded(this, point, type);
Create.RAILWAYS.sync.pointAdded(this, point);
markDirty();
}
@ -91,6 +92,7 @@ public class TrackGraph {
if (removed == null)
return null;
EdgePointManager.onEdgePointRemoved(this, removed, type);
Create.RAILWAYS.sync.pointRemoved(this, removed);
markDirty();
return removed;
}
@ -197,12 +199,13 @@ public class TrackGraph {
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);
if (toOther.putConnection(n1, n2, edge)) {
Create.RAILWAYS.sync.edgeAdded(toOther, n1, n2, edge);
Create.RAILWAYS.sync.edgeDataChanged(toOther, n1, n2, edge);
}
}));
edgePoints.transferAll(toOther.edgePoints);
edgePoints.transferAll(toOther, toOther.edgePoints);
nodes.clear();
connectionsByNode.clear();
@ -325,9 +328,13 @@ public class TrackGraph {
map2.remove(node1);
}
public void putConnection(TrackNode node1, TrackNode node2, TrackEdge edge) {
connectionsByNode.computeIfAbsent(node1, n -> new IdentityHashMap<>())
.put(node2, edge);
public boolean putConnection(TrackNode node1, TrackNode node2, TrackEdge edge) {
Map<TrackNode, TrackEdge> connections = connectionsByNode.computeIfAbsent(node1, n -> new IdentityHashMap<>());
if (connections.containsKey(node2) && connections.get(node2)
.getEdgeData()
.hasPoints())
return false;
return connections.put(node2, edge) == null;
}
public void markDirty() {

View file

@ -0,0 +1,26 @@
package com.simibubi.create.content.logistics.trains;
import java.util.UUID;
import java.util.function.Supplier;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraftforge.network.NetworkEvent.Context;
public abstract class TrackGraphPacket extends SimplePacketBase {
public UUID graphId;
public boolean packetDeletesGraph;
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> handle(CreateClient.RAILWAYS, CreateClient.RAILWAYS.getOrCreateGraph(graphId)));
context.get()
.setPacketHandled(true);
}
protected abstract void handle(GlobalRailwayManager manager, TrackGraph graph);
}

View file

@ -1,43 +1,37 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.signal.SignalEdgeGroupPacket;
import com.simibubi.create.content.logistics.trains.management.signal.TrackEdgePoint;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.network.NetworkEvent.Context;
import net.minecraftforge.network.PacketDistributor;
public class TrackGraphSync {
List<RailGraphSyncPacket> queuedPackets = new ArrayList<>();
List<TrackGraphPacket> queuedPackets = new ArrayList<>();
public void serverTick() {
finish();
flushGraphPacket(null);
if (queuedPackets.isEmpty())
return;
for (RailGraphSyncPacket packet : queuedPackets) {
if (packet.delete || Create.RAILWAYS.trackNetworks.containsKey(packet.graphId)) {
AllPackets.channel.send(PacketDistributor.ALL.noArg(), packet);
}
for (TrackGraphPacket packet : queuedPackets) {
if (!packet.packetDeletesGraph && !Create.RAILWAYS.trackNetworks.containsKey(packet.graphId))
continue;
AllPackets.channel.send(PacketDistributor.ALL.noArg(), packet);
}
queuedPackets.clear();
}
@ -45,21 +39,31 @@ public class TrackGraphSync {
//
public void nodeAdded(TrackGraph graph, TrackNode node) {
flushPacket(graph.id);
currentPacket.addedNodes.put(node.getNetId(), Pair.of(node.getLocation(), node.getNormal()));
flushGraphPacket(graph.id);
currentGraphSyncPacket.addedNodes.put(node.getNetId(), Pair.of(node.getLocation(), node.getNormal()));
}
public void edgeAdded(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge) {
flushPacket(graph.id);
currentPacket.addedEdges.add(Pair.of(Couple.create(node1.getNetId(), node2.getNetId()), edge));
flushGraphPacket(graph.id);
currentGraphSyncPacket.addedEdges.add(Pair.of(Couple.create(node1.getNetId(), node2.getNetId()), edge));
}
public void pointAdded(TrackGraph graph, TrackEdgePoint point) {
flushGraphPacket(graph.id);
currentGraphSyncPacket.addedEdgePoints.add(point);
}
public void pointRemoved(TrackGraph graph, TrackEdgePoint point) {
flushGraphPacket(graph.id);
currentGraphSyncPacket.removedEdgePoints.add(point.getId());
}
public void nodeRemoved(TrackGraph graph, TrackNode node) {
flushPacket(graph.id);
flushGraphPacket(graph.id);
int nodeId = node.getNetId();
if (currentPacket.addedNodes.remove(nodeId) == null)
currentPacket.removedNodes.add(nodeId);
currentPacket.addedEdges.removeIf(pair -> {
if (currentGraphSyncPacket.addedNodes.remove(nodeId) == null)
currentGraphSyncPacket.removedNodes.add(nodeId);
currentGraphSyncPacket.addedEdges.removeIf(pair -> {
Couple<Integer> ids = pair.getFirst();
return ids.getFirst()
.intValue() == nodeId
@ -69,183 +73,104 @@ public class TrackGraphSync {
}
public void graphSplit(TrackGraph graph, Set<TrackGraph> additional) {
flushPacket(graph.id);
additional.forEach(rg -> currentPacket.splitSubGraphs.put(rg.nodesById.keySet()
flushGraphPacket(graph.id);
additional.forEach(rg -> currentGraphSyncPacket.splitSubGraphs.put(rg.nodesById.keySet()
.stream()
.findFirst()
.get(), rg.id));
}
public void graphRemoved(TrackGraph graph) {
flushPacket(graph.id);
currentPacket.delete = true;
}
public void finish() {
flushPacket(null);
flushGraphPacket(graph.id);
currentGraphSyncPacket.packetDeletesGraph = true;
}
//
public void sendEdgeGroups(Collection<UUID> ids, ServerPlayer player) {
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SignalEdgeGroupPacket(ids, true));
}
public void edgeGroupCreated(UUID id) {
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new SignalEdgeGroupPacket(ImmutableList.of(id), true));
}
public void edgeGroupRemoved(UUID id) {
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new SignalEdgeGroupPacket(ImmutableList.of(id), false));
}
//
public void edgeDataChanged(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge) {
flushGraphPacket(graph.id);
currentGraphSyncPacket.syncEdgeData(node1, node2, edge);
}
public void edgeDataChanged(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge, TrackEdge edge2) {
flushGraphPacket(graph.id);
currentGraphSyncPacket.syncEdgeData(node1, node2, edge);
currentGraphSyncPacket.syncEdgeData(node2, node1, edge2);
}
public void sendFullGraphTo(TrackGraph graph, ServerPlayer player) {
RailGraphSyncPacket packet = new RailGraphSyncPacket(graph.id);
TrackGraphSyncPacket packet = new TrackGraphSyncPacket(graph.id);
int sent = 0;
for (TrackNode node : graph.nodes.values()) {
RailGraphSyncPacket currentPacket = packet;
TrackGraphSyncPacket 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) -> currentPacket.addedEdges
.add(Pair.of(Couple.create(node.getNetId(), node2.getNetId()), edge)));
.forEach((node2, edge) -> {
Couple<Integer> key = Couple.create(node.getNetId(), node2.getNetId());
currentPacket.addedEdges.add(Pair.of(key, edge));
currentPacket.syncEdgeData(node, node2, edge);
});
if (sent++ < 1000)
continue;
sent = 0;
packet = flushAndCreateNew(graph, player, packet);
}
for (EdgePointType<?> type : EdgePointType.TYPES.values()) {
for (TrackEdgePoint point : graph.getPoints(type)) {
packet.addedEdgePoints.add(point);
if (sent++ < 1000)
continue;
if (sent++ > 1000) {
sent = 0;
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), packet);
packet = new RailGraphSyncPacket(graph.id);
packet = flushAndCreateNew(graph, player, packet);
}
}
if (sent > 0)
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), packet);
flushAndCreateNew(graph, player, packet);
}
private RailGraphSyncPacket currentPacket;
private TrackGraphSyncPacket flushAndCreateNew(TrackGraph graph, ServerPlayer player, TrackGraphSyncPacket packet) {
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), packet);
packet = new TrackGraphSyncPacket(graph.id);
return packet;
}
private void flushPacket(@Nullable UUID graphId) {
if (currentPacket != null) {
if (currentPacket.graphId.equals(graphId))
//
private TrackGraphSyncPacket currentGraphSyncPacket;
private void flushGraphPacket(@Nullable UUID graphId) {
if (currentGraphSyncPacket != null) {
if (currentGraphSyncPacket.graphId.equals(graphId))
return;
queuedPackets.add(currentPacket);
currentPacket = null;
queuedPackets.add(currentGraphSyncPacket);
currentGraphSyncPacket = null;
}
if (graphId != null)
currentPacket = new RailGraphSyncPacket(graphId);
}
public static class RailGraphSyncPacket extends SimplePacketBase {
UUID graphId;
Map<Integer, Pair<TrackNodeLocation, Vec3>> addedNodes;
List<Pair<Couple<Integer>, TrackEdge>> addedEdges;
List<Integer> removedNodes;
Map<Integer, UUID> splitSubGraphs;
boolean delete;
public RailGraphSyncPacket(UUID graphId) {
this.graphId = graphId;
addedNodes = new HashMap<>();
addedEdges = new ArrayList<>();
removedNodes = new ArrayList<>();
splitSubGraphs = new HashMap<>();
delete = false;
}
public RailGraphSyncPacket(FriendlyByteBuf buffer) {
int size;
graphId = buffer.readUUID();
delete = buffer.readBoolean();
if (delete)
return;
addedNodes = new HashMap<>();
addedEdges = new ArrayList<>();
removedNodes = new ArrayList<>();
splitSubGraphs = new HashMap<>();
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
removedNodes.add(buffer.readVarInt());
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
addedNodes.put(buffer.readVarInt(),
Pair.of(TrackNodeLocation.fromPackedPos(buffer.readBlockPos()), VecHelper.read(buffer)));
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
addedEdges.add(Pair.of(Couple.create(buffer::readVarInt), TrackEdge.read(buffer)));
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
splitSubGraphs.put(buffer.readVarInt(), buffer.readUUID());
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeUUID(graphId);
buffer.writeBoolean(delete);
if (delete)
return;
buffer.writeVarInt(removedNodes.size());
removedNodes.forEach(buffer::writeVarInt);
buffer.writeVarInt(addedNodes.size());
addedNodes.forEach((node, loc) -> {
buffer.writeVarInt(node);
buffer.writeBlockPos(new BlockPos(loc.getFirst()));
VecHelper.write(loc.getSecond(), buffer);
});
buffer.writeVarInt(addedEdges.size());
addedEdges.forEach(pair -> {
pair.getFirst()
.forEach(buffer::writeVarInt);
pair.getSecond()
.write(buffer);
});
buffer.writeVarInt(splitSubGraphs.size());
splitSubGraphs.forEach((node, uuid) -> {
buffer.writeVarInt(node);
buffer.writeUUID(uuid);
});
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> {
GlobalRailwayManager manager = CreateClient.RAILWAYS;
TrackGraph railGraph = manager.getOrCreateGraph(graphId);
if (delete) {
manager.removeGraph(railGraph);
return;
}
for (int nodeId : removedNodes) {
TrackNode node = railGraph.getNode(nodeId);
if (node != null)
railGraph.removeNode(null, node.getLocation());
}
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()
.map(railGraph::getNode);
if (nodes.getFirst() != null && nodes.getSecond() != null)
railGraph.putConnection(nodes.getFirst(), nodes.getSecond(), pair.getSecond());
}
if (!splitSubGraphs.isEmpty())
railGraph.findDisconnectedGraphs(splitSubGraphs)
.forEach(manager::putGraph);
});
context.get()
.setPacketHandled(true);
}
currentGraphSyncPacket = new TrackGraphSyncPacket(graphId);
}
}

View file

@ -0,0 +1,243 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.UUID;
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.TrackEdgePoint;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.phys.Vec3;
public class TrackGraphSyncPacket extends TrackGraphPacket {
Map<Integer, Pair<TrackNodeLocation, Vec3>> addedNodes;
List<Pair<Couple<Integer>, TrackEdge>> addedEdges;
List<Integer> removedNodes;
List<TrackEdgePoint> addedEdgePoints;
List<UUID> removedEdgePoints;
Map<Integer, UUID> splitSubGraphs;
Map<Couple<Integer>, Pair<Integer, List<UUID>>> updatedEdgeData;
static final int NULL_GROUP = 0, PASSIVE_GROUP = 1, GROUP = 2;
public TrackGraphSyncPacket(UUID graphId) {
this.graphId = graphId;
addedNodes = new HashMap<>();
addedEdges = new ArrayList<>();
removedNodes = new ArrayList<>();
addedEdgePoints = new ArrayList<>();
removedEdgePoints = new ArrayList<>();
updatedEdgeData = new HashMap<>();
splitSubGraphs = new HashMap<>();
packetDeletesGraph = false;
}
public TrackGraphSyncPacket(FriendlyByteBuf buffer) {
int size;
graphId = buffer.readUUID();
packetDeletesGraph = buffer.readBoolean();
if (packetDeletesGraph)
return;
addedNodes = new HashMap<>();
addedEdges = new ArrayList<>();
addedEdgePoints = new ArrayList<>();
removedEdgePoints = new ArrayList<>();
removedNodes = new ArrayList<>();
splitSubGraphs = new HashMap<>();
updatedEdgeData = new HashMap<>();
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
removedNodes.add(buffer.readVarInt());
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
addedNodes.put(buffer.readVarInt(),
Pair.of(TrackNodeLocation.fromPackedPos(buffer.readBlockPos()), VecHelper.read(buffer)));
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
addedEdges.add(Pair.of(Couple.create(buffer::readVarInt), TrackEdge.read(buffer)));
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
addedEdgePoints.add(EdgePointType.read(buffer));
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
removedEdgePoints.add(buffer.readUUID());
size = buffer.readVarInt();
for (int i = 0; i < size; i++) {
ArrayList<UUID> list = new ArrayList<>();
Couple<Integer> key = Couple.create(buffer::readInt);
Pair<Integer, List<UUID>> entry = Pair.of(buffer.readVarInt(), list);
int size2 = buffer.readVarInt();
for (int j = 0; j < size2; j++)
list.add(buffer.readUUID());
updatedEdgeData.put(key, entry);
}
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
splitSubGraphs.put(buffer.readVarInt(), buffer.readUUID());
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeUUID(graphId);
buffer.writeBoolean(packetDeletesGraph);
if (packetDeletesGraph)
return;
buffer.writeVarInt(removedNodes.size());
removedNodes.forEach(buffer::writeVarInt);
buffer.writeVarInt(addedNodes.size());
addedNodes.forEach((node, loc) -> {
buffer.writeVarInt(node);
buffer.writeBlockPos(new BlockPos(loc.getFirst()));
VecHelper.write(loc.getSecond(), buffer);
});
buffer.writeVarInt(addedEdges.size());
addedEdges.forEach(pair -> {
pair.getFirst()
.forEach(buffer::writeVarInt);
pair.getSecond()
.write(buffer);
});
buffer.writeVarInt(addedEdgePoints.size());
addedEdgePoints.forEach(ep -> ep.write(buffer));
buffer.writeVarInt(removedEdgePoints.size());
removedEdgePoints.forEach(buffer::writeUUID);
buffer.writeVarInt(updatedEdgeData.size());
for (Entry<Couple<Integer>, Pair<Integer, List<UUID>>> entry : updatedEdgeData.entrySet()) {
entry.getKey()
.forEach(buffer::writeInt);
Pair<Integer, List<UUID>> pair = entry.getValue();
buffer.writeVarInt(pair.getFirst());
List<UUID> list = pair.getSecond();
buffer.writeVarInt(list.size());
list.forEach(buffer::writeUUID);
}
buffer.writeVarInt(splitSubGraphs.size());
splitSubGraphs.forEach((node, uuid) -> {
buffer.writeVarInt(node);
buffer.writeUUID(uuid);
});
}
@Override
protected void handle(GlobalRailwayManager manager, TrackGraph graph) {
if (packetDeletesGraph) {
manager.removeGraph(graph);
return;
}
for (int nodeId : removedNodes) {
TrackNode node = graph.getNode(nodeId);
if (node != null)
graph.removeNode(null, node.getLocation());
}
for (Entry<Integer, Pair<TrackNodeLocation, Vec3>> entry : addedNodes.entrySet()) {
Integer nodeId = entry.getKey();
Pair<TrackNodeLocation, Vec3> nodeLocation = entry.getValue();
graph.loadNode(nodeLocation.getFirst(), nodeId, nodeLocation.getSecond());
}
for (Pair<Couple<Integer>, TrackEdge> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst()
.map(graph::getNode);
if (nodes.getFirst() != null && nodes.getSecond() != null)
graph.putConnection(nodes.getFirst(), nodes.getSecond(), pair.getSecond());
}
for (TrackEdgePoint edgePoint : addedEdgePoints)
graph.edgePoints.put(edgePoint.getType(), edgePoint);
for (UUID uuid : removedEdgePoints)
for (EdgePointType<?> type : EdgePointType.TYPES.values())
graph.edgePoints.remove(type, uuid);
handleEdgeData(manager, graph);
if (!splitSubGraphs.isEmpty())
graph.findDisconnectedGraphs(splitSubGraphs)
.forEach(manager::putGraph);
}
protected void handleEdgeData(GlobalRailwayManager manager, TrackGraph graph) {
for (Entry<Couple<Integer>, Pair<Integer, List<UUID>>> entry : updatedEdgeData.entrySet()) {
List<UUID> idList = entry.getValue()
.getSecond();
int groupType = entry.getValue()
.getFirst();
Couple<TrackNode> nodes = entry.getKey()
.map(graph::getNode);
if (nodes.either(Objects::isNull))
continue;
TrackEdge edge = graph.getConnectionsFrom(nodes.getFirst())
.get(nodes.getSecond());
if (edge == null)
continue;
EdgeData edgeData = new EdgeData();
if (groupType == NULL_GROUP)
edgeData.singleSignalGroup = null;
else if (groupType == PASSIVE_GROUP)
edgeData.singleSignalGroup = EdgeData.passiveGroup;
else
edgeData.singleSignalGroup = idList.get(0);
List<TrackEdgePoint> points = edgeData.getPoints();
edge.edgeData = edgeData;
for (int i = groupType == GROUP ? 1 : 0; i < idList.size(); i++) {
UUID uuid = idList.get(i);
for (EdgePointType<?> type : EdgePointType.TYPES.values()) {
TrackEdgePoint point = graph.edgePoints.get(type, uuid);
if (point == null)
continue;
points.add(point);
break;
}
}
}
}
public void syncEdgeData(TrackNode node1, TrackNode node2, TrackEdge edge) {
Couple<Integer> key = Couple.create(node1.getNetId(), node2.getNetId());
List<UUID> list = new ArrayList<>();
EdgeData edgeData = edge.getEdgeData();
int groupType = edgeData.hasSignalBoundaries() ? NULL_GROUP
: EdgeData.passiveGroup.equals(edgeData.singleSignalGroup) ? PASSIVE_GROUP : GROUP;
if (groupType == GROUP)
list.add(edgeData.singleSignalGroup);
for (TrackEdgePoint point : edgeData.getPoints())
list.add(point.getId());
updatedEdgeData.put(key, Pair.of(groupType, list));
}
}

View file

@ -27,7 +27,7 @@ public class TrackSavedData extends SavedData {
}
private static TrackSavedData load(CompoundTag nbt) {
TrackSavedData sd = new TrackSavedData();
TrackSavedData sd = new TrackSavedData();//TODO load trains before everything else
sd.trackNetworks = new HashMap<>();
sd.signalEdgeGroups = new HashMap<>();
NBTHelper.iterateCompoundList(nbt.getList("RailGraphs", Tag.TAG_COMPOUND), c -> {

View file

@ -94,11 +94,6 @@ 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;

View file

@ -241,10 +241,11 @@ public class Train {
if (navigation.destination != null) {
boolean recalculate = navigation.distanceToDestination % 100 > 20;
boolean imminentRecalculate = navigation.distanceToDestination > 5;
navigation.distanceToDestination -= Math.abs(distance);
double toSubstract = navigation.destinationBehindTrain ? -distance : distance;
navigation.distanceToDestination -= toSubstract;
boolean signalMode = navigation.waitingForSignal != null;
if (signalMode) {
navigation.distanceToSignal -= Math.abs(distance);
navigation.distanceToSignal -= toSubstract;
recalculate = navigation.distanceToSignal % 100 > 20;
}
if (recalculate && (signalMode ? navigation.distanceToSignal : navigation.distanceToDestination) % 100 <= 20

View file

@ -9,6 +9,7 @@ import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.signal.SingleTileEdgePoint;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
public class GlobalStation extends SingleTileEdgePoint {
@ -26,6 +27,12 @@ public class GlobalStation extends SingleTileEdgePoint {
name = nbt.getString("Name");
nearestTrain = new WeakReference<Train>(null);
}
@Override
public void read(FriendlyByteBuf buffer) {
super.read(buffer);
name = buffer.readUtf();
}
@Override
public void write(CompoundTag nbt) {
@ -33,6 +40,12 @@ public class GlobalStation extends SingleTileEdgePoint {
nbt.putString("Name", name);
}
@Override
public void write(FriendlyByteBuf buffer) {
super.write(buffer);
buffer.writeUtf(name);
}
public boolean canApproachFrom(TrackNode side) {
return isPrimary(side);
}

View file

@ -74,8 +74,11 @@ public class StationEditPacket extends TileEntityConfigurationPacket<StationTile
if (!name.isBlank()) {
GlobalStation station = te.getStation();
if (station != null)
GraphLocation graphLocation = te.edgePoint.determineGraphLocation();
if (station != null && graphLocation != null) {
station.name = name;
Create.RAILWAYS.sync.pointAdded(graphLocation.graph, station);
}
Create.RAILWAYS.markTracksDirty();
}

View file

@ -144,6 +144,8 @@ public class StationScreen extends AbstractStationScreen {
int trainIconWidth = getTrainIconWidth(imminentTrain);
int targetPos = background.width / 2 - trainIconWidth / 2;
float f = (float) (imminentTrain.navigation.distanceToDestination / 15f);
if (imminentTrain.currentStation.equals(station.getId()))
f = 0;
trainPosition.startWithValue(targetPos - (targetPos + 5) * f);
}
return;
@ -167,15 +169,16 @@ public class StationScreen extends AbstractStationScreen {
return;
}
if (train.navigation.destination != station && train.getCurrentStation() != station) {
boolean trainAtStation = train.currentStation != null && train.currentStation.equals(station.getId());
if (train.navigation.destination != station && !trainAtStation) {
leavingAnimation = 80;
return;
}
disassembleTrainButton.active = train.getCurrentStation() == station; // TODO te.canAssemble
disassembleTrainButton.active = trainAtStation; // TODO te.canAssemble
openScheduleButton.active = train.runtime.schedule != null;
float f = (float) (train.navigation.distanceToDestination / 30f);
float f = trainAtStation ? 0 : (float) (train.navigation.distanceToDestination / 30f);
trainPosition.setValue(targetPos - (targetPos + trainIconWidth) * f);
}

View file

@ -68,13 +68,12 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
@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))
if (clientPacket)
edgePoint = null;
super.read(nbt, clientPacket);
}
@ -86,7 +85,6 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
public void invalidateEdgePoint(CompoundTag migrationData) {
this.migrationData = migrationData;
id = UUID.randomUUID();
edgePoint = null;
tileEntity.sendData();
}
@ -100,14 +98,16 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
@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;
}
boolean isClientSide = getWorld().isClientSide;
if (migrationData == null || isClientSide)
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)
if (isClientSide)
return null;
if (!hasValidTrack())
return null;
@ -126,6 +126,10 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
if (edge == null)
return null;
double length = edge.getLength(node1, node2);
CompoundTag data = migrationData;
migrationData = null;
EdgeData signalData = edge.getEdgeData();
if (signalData.hasPoints()) {
for (EdgePointType<?> otherType : EdgePointType.TYPES.values()) {
@ -147,19 +151,13 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
}
T point = edgePointType.create();
boolean reverseEdge = front || point instanceof SingleTileEdgePoint;
if (data != null)
point.read(data, true);
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.setLocation(reverseEdge ? loc.edge : loc.edge.swap(), reverseEdge ? loc.position : length - loc.position);
point.tileAdded(getPos(), front);
loc.graph.addPoint(edgePointType, point);
return point;

View file

@ -1,5 +1,6 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint;
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;
@ -22,6 +23,7 @@ public class EdgePointManager {
TrackEdge startEdge = startEdges.get(front);
startEdge.getEdgeData()
.addPoint(node1, node2, startEdge, point);
Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge);
}
}
@ -32,8 +34,11 @@ public class EdgePointManager {
startNodes.forEachWithParams((l1, l2) -> {
TrackEdge trackEdge = graph.getConnectionsFrom(l1)
.get(l2);
if (trackEdge == null)
return;
trackEdge.getEdgeData()
.removePoint(l1, l2, trackEdge, point);
Create.RAILWAYS.sync.edgeDataChanged(graph, l1, l2, trackEdge);
}, startNodes.swap());
}

View file

@ -6,6 +6,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import com.simibubi.create.Create;
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;
@ -54,9 +55,13 @@ public class EdgePointStorage {
.forEach(p -> p.tick(graph)));
}
public void transferAll(EdgePointStorage other) {
pointsByType.forEach((type, map) -> other.getMap(type)
.putAll(map));
public void transferAll(TrackGraph target, EdgePointStorage other) {
pointsByType.forEach((type, map) -> {
other.getMap(type)
.putAll(map);
map.values()
.forEach(ep -> Create.RAILWAYS.sync.pointAdded(target, ep));
});
pointsByType.clear();
}
@ -83,7 +88,7 @@ public class EdgePointStorage {
Map<UUID, TrackEdgePoint> map = getMap(type);
NBTHelper.iterateCompoundList(list, tag -> {
TrackEdgePoint edgePoint = type.create();
edgePoint.read(tag);
edgePoint.read(tag, false);
map.put(edgePoint.getId(), edgePoint);
});
}

View file

@ -9,6 +9,7 @@ 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.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
public class EdgePointType<T extends TrackEdgePoint> {
@ -42,5 +43,13 @@ public class EdgePointType<T extends TrackEdgePoint> {
public ResourceLocation getId() {
return id;
}
public static TrackEdgePoint read(FriendlyByteBuf buffer) {
ResourceLocation type = buffer.readResourceLocation();
EdgePointType<?> edgePointType = TYPES.get(type);
TrackEdgePoint point = edgePointType.create();
point.read(buffer);
return point;
}
}

View file

@ -101,7 +101,7 @@ public class EdgeData {
public static EdgeData read(CompoundTag nbt, TrackGraph graph) {
EdgeData data = new EdgeData();
if (nbt.contains("SignalGroup"))
data.singleSignalGroup = nbt.getUUID("Group");
data.singleSignalGroup = nbt.getUUID("SignalGroup");
else if (!nbt.contains("PassiveGroup"))
data.singleSignalGroup = null;

View file

@ -19,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.network.FriendlyByteBuf;
import net.minecraft.world.level.LevelAccessor;
public class SignalBoundary extends TrackEdgePoint {
@ -124,8 +125,11 @@ public class SignalBoundary extends TrackEdgePoint {
}
@Override
public void read(CompoundTag nbt) {
super.read(nbt);
public void read(CompoundTag nbt, boolean migration) {
super.read(nbt, migration);
if (migration)
return;
sidesToUpdate = Couple.create(true, true);
signals = Couple.create(HashSet::new);
@ -143,6 +147,15 @@ public class SignalBoundary extends TrackEdgePoint {
for (int i = 1; i <= 2; i++)
sidesToUpdate.set(i == 1, nbt.contains("Update" + i));
}
@Override
public void read(FriendlyByteBuf buffer) {
super.read(buffer);
for (int i = 1; i <= 2; i++) {
if (buffer.readBoolean())
groups.set(i == 1, buffer.readUUID());
}
}
@Override
public void write(CompoundTag nbt) {
@ -158,5 +171,16 @@ public class SignalBoundary extends TrackEdgePoint {
if (sidesToUpdate.get(i == 1))
nbt.putBoolean("Update" + i, true);
}
@Override
public void write(FriendlyByteBuf buffer) {
super.write(buffer);
for (int i = 1; i <= 2; i++) {
boolean hasGroup = groups.get(i == 1) != null;
buffer.writeBoolean(hasGroup);
if (hasGroup)
buffer.writeUUID(groups.get(i == 1));
}
}
}

View file

@ -0,0 +1,56 @@
package com.simibubi.create.content.logistics.trains.management.signal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent.Context;
public class SignalEdgeGroupPacket extends SimplePacketBase {
Collection<UUID> ids;
boolean add;
public SignalEdgeGroupPacket(Collection<UUID> ids, boolean add) {
this.ids = ids;
this.add = add;
}
public SignalEdgeGroupPacket(FriendlyByteBuf buffer) {
ids = new ArrayList<>();
add = buffer.readBoolean();
int size = buffer.readVarInt();
for (int i = 0; i < size; i++)
ids.add(buffer.readUUID());
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeBoolean(add);
buffer.writeVarInt(ids.size());
ids.forEach(buffer::writeUUID);
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> {
Map<UUID, SignalEdgeGroup> signalEdgeGroups = CreateClient.RAILWAYS.signalEdgeGroups;
for (UUID id : ids) {
if (add)
signalEdgeGroups.put(id, new SignalEdgeGroup(id));
else
signalEdgeGroups.remove(id);
}
});
context.get()
.setPacketHandled(true);
}
}

View file

@ -13,6 +13,7 @@ import com.google.common.base.Predicates;
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.TrackGraphSync;
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;
@ -28,7 +29,9 @@ public class SignalPropagator {
for (boolean front : Iterate.trueAndFalse) {
if (signal.sidesToUpdate.get(front))
continue;
Create.RAILWAYS.signalEdgeGroups.remove(signal.groups.get(front));
UUID id = signal.groups.get(front);
if (Create.RAILWAYS.signalEdgeGroups.remove(id) != null)
Create.RAILWAYS.sync.edgeGroupRemoved(id);
walkSignals(graph, signal, front, pair -> {
TrackNode node1 = pair.getFirst();
SignalBoundary boundary = pair.getSecond();
@ -51,23 +54,30 @@ public class SignalPropagator {
public static void propagateSignalGroup(TrackGraph graph, SignalBoundary signal, boolean front) {
Map<UUID, SignalEdgeGroup> globalGroups = Create.RAILWAYS.signalEdgeGroups;
TrackGraphSync sync = Create.RAILWAYS.sync;
SignalEdgeGroup group = new SignalEdgeGroup(UUID.randomUUID());
UUID groupId = group.id;
globalGroups.put(groupId, group);
sync.edgeGroupCreated(groupId);
signal.groups.set(front, groupId);
sync.pointAdded(graph, signal);
walkSignals(graph, signal, front, pair -> {
TrackNode node1 = pair.getFirst();
SignalBoundary boundary = pair.getSecond();
UUID currentGroup = boundary.getGroup(node1);
if (currentGroup != null)
globalGroups.remove(currentGroup);
if (globalGroups.remove(currentGroup) != null)
sync.edgeGroupRemoved(currentGroup);
boundary.setGroup(node1, groupId);
sync.pointAdded(graph, boundary);
return true;
}, signalData -> {
if (signalData.singleSignalGroup != null)
globalGroups.remove(signalData.singleSignalGroup);
if (globalGroups.remove(signalData.singleSignalGroup) != null)
sync.edgeGroupRemoved(signalData.singleSignalGroup);
signalData.singleSignalGroup = groupId;
return true;
@ -85,16 +95,19 @@ public class SignalPropagator {
TrackNode node1 = startNodes.get(front);
TrackNode node2 = startNodes.get(!front);
TrackEdge startEdge = startEdges.get(front);
TrackEdge oppositeEdge = startEdges.get(!front);
if (startEdge == null)
return;
Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge, oppositeEdge);
// Check for signal on the same edge
SignalBoundary immediateBoundary = startEdge.getEdgeData()
.next(EdgePointType.SIGNAL, node1, node2, startEdge, signal.getLocationOn(node1, node2, startEdge));
if (immediateBoundary != null) {
if (boundaryCallback.test(Pair.of(node1, immediateBoundary)))
notifyTrains(graph, startEdge, startEdges.get(!front));
notifyTrains(graph, startEdge, oppositeEdge);
return;
}
@ -134,16 +147,20 @@ public class SignalPropagator {
// no boundary- update group of edge
if (!signalData.hasSignalBoundaries()) {
if (nonBoundaryCallback.test(signalData))
notifyTrains(graph, currentEdge);
if (!nonBoundaryCallback.test(signalData))
continue;
notifyTrains(graph, currentEdge);
Create.RAILWAYS.sync.edgeDataChanged(graph, currentNode, nextNode, edge, oppositeEdge);
continue;
}
// other/own boundary found
SignalBoundary nextBoundary =
signalData.next(EdgePointType.SIGNAL, currentNode, nextNode, currentEdge, 0);
if (boundaryCallback.test(Pair.of(currentNode, nextBoundary)))
if (boundaryCallback.test(Pair.of(currentNode, nextBoundary))) {
notifyTrains(graph, edge, oppositeEdge);
Create.RAILWAYS.sync.edgeDataChanged(graph, currentNode, nextNode, edge, oppositeEdge);
}
continue EdgeWalk;
}

View file

@ -34,15 +34,10 @@ public abstract class SingleTileEdgePoint extends TrackEdgePoint {
}
@Override
public final void read(CompoundTag nbt) {
read(nbt, false);
}
public void read(CompoundTag nbt, boolean migration) {
super.read(nbt, migration);
if (migration)
return;
super.read(nbt);
tilePos = NbtUtils.readBlockPos(nbt.getCompound("TilePos"));
}

View file

@ -16,6 +16,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.LevelAccessor;
public abstract class TrackEdgePoint {
@ -82,19 +83,35 @@ public abstract class TrackEdgePoint {
.equals(node1.getLocation());
}
public void read(CompoundTag nbt) {
public void read(CompoundTag nbt, boolean migration) {
if (migration)
return;
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 read(FriendlyByteBuf buffer) {
id = buffer.readUUID();
edgeLocation = Couple.create(() -> TrackNodeLocation.fromPackedPos(buffer.readBlockPos()));
position = buffer.readDouble();
}
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 write(FriendlyByteBuf buffer) {
buffer.writeResourceLocation(type.getId());
buffer.writeUUID(id);
edgeLocation.forEach(loc -> buffer.writeBlockPos(new BlockPos(loc)));
buffer.writeDouble(position);
}
public void tick(TrackGraph graph) {}
protected void removeFromAllGraphs() {

View file

@ -6,7 +6,7 @@ import java.util.UUID;
import java.util.function.BiConsumer;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.GlobalRailwayManager;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.entity.Train;
@ -40,7 +40,7 @@ public class DumpRailwaysCommand {
}
static void fillReport(ServerLevel level, BiConsumer<String, Integer> chat) {
GlobalRailwayManager railways = Create.RAILWAYS;
GlobalRailwayManager railways = CreateClient.RAILWAYS;
int white = ChatFormatting.WHITE.getColor();
int blue = 0xD3DEDC;
int darkBlue = 0x92A9BD;
@ -68,6 +68,8 @@ public class DumpRailwaysCommand {
chat.accept(" -> " + signals.size() + " registered Signals", blue);
for (GlobalStation globalStation : graph.getPoints(EdgePointType.STATION)) {
BlockPos pos = globalStation.getTilePos();
if (pos == null)
pos = BlockPos.ZERO;
chat.accept(" -> " + globalStation.name + " (" + globalStation.id.toString()
.substring(0, 5) + ") [" + pos.getX() + "," + pos.getY() + "," + pos.getZ() + "]", darkBlue);
if (globalStation.getPresentTrain() != null) {

View file

@ -43,10 +43,11 @@ import com.simibubi.create.content.logistics.item.filter.FilterScreenPacket;
import com.simibubi.create.content.logistics.packet.ConfigureStockswitchPacket;
import com.simibubi.create.content.logistics.packet.FunnelFlapPacket;
import com.simibubi.create.content.logistics.packet.TunnelFlapPacket;
import com.simibubi.create.content.logistics.trains.TrackGraphSync.RailGraphSyncPacket;
import com.simibubi.create.content.logistics.trains.TrackGraphSyncPacket;
import com.simibubi.create.content.logistics.trains.management.StationEditPacket;
import com.simibubi.create.content.logistics.trains.management.TrainEditPacket;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleEditPacket;
import com.simibubi.create.content.logistics.trains.management.signal.SignalEdgeGroupPacket;
import com.simibubi.create.content.schematics.packet.ConfigureSchematicannonPacket;
import com.simibubi.create.content.schematics.packet.InstantSchematicPacket;
import com.simibubi.create.content.schematics.packet.SchematicPlacePacket;
@ -132,7 +133,8 @@ public enum AllPackets {
SOUL_PULSE(SoulPulseEffectPacket.class, SoulPulseEffectPacket::new, PLAY_TO_CLIENT),
PERSISTENT_DATA(ISyncPersistentData.PersistentDataPacket.class, ISyncPersistentData.PersistentDataPacket::new, PLAY_TO_CLIENT),
SYNC_POTATO_PROJECTILE_TYPES(PotatoProjectileTypeManager.SyncPacket.class, PotatoProjectileTypeManager.SyncPacket::new, PLAY_TO_CLIENT),
SYNC_RAIL_GRAPH(RailGraphSyncPacket.class, RailGraphSyncPacket::new, PLAY_TO_CLIENT),
SYNC_RAIL_GRAPH(TrackGraphSyncPacket.class, TrackGraphSyncPacket::new, PLAY_TO_CLIENT),
SYNC_EDGE_GROUP(SignalEdgeGroupPacket.class, SignalEdgeGroupPacket::new, PLAY_TO_CLIENT),
REMOVE_TE(RemoveTileEntityPacket.class, RemoveTileEntityPacket::new, PLAY_TO_CLIENT),
;