Reincartnated

- Trains now get saved to disk
- Trains now sync raw position data to clients while in the derailed state
- Trains can no longer be relocated into another train
- Train relocation now shows indicators
- Fixed single-bogey carriages not sending enough position context when derailed
This commit is contained in:
simibubi 2022-03-09 21:33:28 +01:00
parent ed6712fd0b
commit 9e0f35e4d7
34 changed files with 611 additions and 175 deletions

View file

@ -541,21 +541,21 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
255e47ab7894ba35851a2f34c82be3dc9c9e8784 assets/create/lang/en_ud.json
3610238d5807c0d4f04a7632a871487ba8cc2c1f assets/create/lang/en_us.json
6a67c98238de2e57b5f2b36b3e988e70616c2ab0 assets/create/lang/unfinished/de_de.json
f9cb3ca070daff2503223de083c95e7e176b10fd assets/create/lang/unfinished/es_cl.json
35bb65eb644b10adbf55bc03913db056bc88f32c assets/create/lang/unfinished/es_es.json
9e7c2ea229f44ff57fc7ba2bc6f94638d2a68af0 assets/create/lang/unfinished/fr_fr.json
0e4bc37cd97dd28107593eb11d936101a2a3ebb6 assets/create/lang/unfinished/it_it.json
11034cb1b7e049c403f4a1e4774c2e1611af5b10 assets/create/lang/unfinished/ja_jp.json
626ef47b3ee504f922b6fa7cde416ae284f22236 assets/create/lang/unfinished/ko_kr.json
1025d8531ada9f4a521aba15a153de59a3bfd596 assets/create/lang/unfinished/nl_nl.json
4113a74ef44f7c30285b578eea239fee70646702 assets/create/lang/unfinished/pl_pl.json
ecb8680bae4a7ce8c6e7f1ae45f74992a6f152a6 assets/create/lang/unfinished/pt_br.json
db520fa1663bbecea9e39a1acfa6514740e74445 assets/create/lang/unfinished/pt_pt.json
718e9841f70866b4fbbe397f8fdd1600e2c044a8 assets/create/lang/unfinished/ru_ru.json
c8094e8ebf65b51fe1156955e2968709543c0592 assets/create/lang/unfinished/zh_cn.json
949a8d2f792ff994dbe2339dda5d08543d62fd55 assets/create/lang/unfinished/zh_tw.json
b908dd7c47b286d05a57b8a58c60051363ffff86 assets/create/lang/en_us.json
29ad91d27ee183b9d4de8d152d0fb8ed2a388a0c assets/create/lang/unfinished/de_de.json
8d33ba82d3cca307c5ae0dda41a49ec16f1c7b74 assets/create/lang/unfinished/es_cl.json
ae674553b564a9e09a7d10c9c2d8c1be1307f40a assets/create/lang/unfinished/es_es.json
318149855f383fb90439f2f0818e8e83fee3924e assets/create/lang/unfinished/fr_fr.json
e7321dbe8f98c96e778b5c46823c98500b3001bc assets/create/lang/unfinished/it_it.json
5a2da425fd0dd276c3d7cf66f6bb43f229574306 assets/create/lang/unfinished/ja_jp.json
98fc1956e8b25aec27e889ada3a160800b958dfd assets/create/lang/unfinished/ko_kr.json
67cefb6690f5cccce93d776a760ebdadc7f2631e assets/create/lang/unfinished/nl_nl.json
9a7f4d369d2a54aaf5c422fae740244025a13af4 assets/create/lang/unfinished/pl_pl.json
3656897ffc5c89bf16a32ad11efe33cd5b4bfb23 assets/create/lang/unfinished/pt_br.json
c84995ec524b6469fc42f39574be21b9dbee24e3 assets/create/lang/unfinished/pt_pt.json
bcf41d9fd8454296dc56326b6e26ee41e2ad52e5 assets/create/lang/unfinished/ru_ru.json
d3dfdab017748fa82f9e952a35bd6e82fe063771 assets/create/lang/unfinished/zh_cn.json
dda3aee2ece5503edd53dbd4aa9db7363e4beafc assets/create/lang/unfinished/zh_tw.json
487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json

View file

@ -1421,7 +1421,7 @@
"create.train.relocate.abort": "Relocation aborted",
"create.train.relocate.success": "Relocation successful",
"create.train.relocate.valid": "Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "Cannot relocate Train to this Track",
"create.train.relocate.invalid": "Cannot relocate Train to here",
"create.train.relocate.too_far": "Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -1422,7 +1422,7 @@
"create.train.relocate.abort": "UNLOCALIZED: Relocation aborted",
"create.train.relocate.success": "UNLOCALIZED: Relocation successful",
"create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track",
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",

View file

@ -64,8 +64,8 @@ public class ControlsInputPacket extends SimplePacketBase {
Entity entity = world.getEntity(contraptionEntityId);
if (!(entity instanceof AbstractContraptionEntity ace))
return;
if (ace.toGlobalVector(Vec3.atCenterOf(controlsPos), 0)
.distanceTo(player.position()) > 10)
if (!ace.toGlobalVector(Vec3.atCenterOf(controlsPos), 0)
.closerThan(player.position(), 16))
return;
ControlsServerHandler.receivePressed(world, ace, controlsPos, uniqueID, activatedButtons, press);

View file

@ -53,7 +53,7 @@ public class GlobalRailwayManager {
sync.sendEdgeGroups(signalEdgeGroups.keySet(), serverPlayer);
for (Train train : trains.values())
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> serverPlayer),
new TrainPacket(train, false));
new TrainPacket(train, true));
}
}
@ -70,6 +70,7 @@ public class GlobalRailwayManager {
if (savedData != null)
return;
savedData = RailwaySavedData.load(server);
trains = savedData.getTrains();
trackNetworks = savedData.getTrackNetworks();
signalEdgeGroups = savedData.getSignalBlocks();
}

View file

@ -5,6 +5,7 @@ import java.util.Map;
import java.util.UUID;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.foundation.utility.NBTHelper;
@ -17,23 +18,33 @@ public class RailwaySavedData extends SavedData {
private Map<UUID, TrackGraph> trackNetworks = new HashMap<>();
private Map<UUID, SignalEdgeGroup> signalEdgeGroups = new HashMap<>();
private Map<UUID, Train> trains = new HashMap<>();
@Override
public CompoundTag save(CompoundTag nbt) {
nbt.put("RailGraphs", NBTHelper.writeCompoundList(Create.RAILWAYS.trackNetworks.values(), TrackGraph::write));
GlobalRailwayManager railways = Create.RAILWAYS;
Create.LOGGER.info("Saving Railway Information...");
nbt.put("RailGraphs", NBTHelper.writeCompoundList(railways.trackNetworks.values(), TrackGraph::write));
nbt.put("SignalBlocks",
NBTHelper.writeCompoundList(Create.RAILWAYS.signalEdgeGroups.values(), SignalEdgeGroup::write));
NBTHelper.writeCompoundList(railways.signalEdgeGroups.values(), SignalEdgeGroup::write));
nbt.put("Trains", NBTHelper.writeCompoundList(railways.trains.values(), Train::write));
return nbt;
}
private static RailwaySavedData load(CompoundTag nbt) {
RailwaySavedData sd = new RailwaySavedData();//TODO load trains before everything else
RailwaySavedData sd = new RailwaySavedData();
sd.trackNetworks = new HashMap<>();
sd.signalEdgeGroups = new HashMap<>();
sd.trains = new HashMap<>();
Create.LOGGER.info("Loading Railway Information...");
NBTHelper.iterateCompoundList(nbt.getList("RailGraphs", Tag.TAG_COMPOUND), c -> {
TrackGraph graph = TrackGraph.read(c);
sd.trackNetworks.put(graph.id, graph);
});
NBTHelper.iterateCompoundList(nbt.getList("Trains", Tag.TAG_COMPOUND), c -> {
Train train = Train.read(c, sd.trackNetworks);
sd.trains.put(train.id, train);
});
NBTHelper.iterateCompoundList(nbt.getList("SignalBlocks", Tag.TAG_COMPOUND), c -> {
SignalEdgeGroup group = SignalEdgeGroup.read(c);
sd.signalEdgeGroups.put(group.id, group);
@ -45,6 +56,10 @@ public class RailwaySavedData extends SavedData {
return trackNetworks;
}
public Map<UUID, Train> getTrains() {
return trains;
}
public Map<UUID, SignalEdgeGroup> getSignalBlocks() {
return signalEdgeGroups;
}

View file

@ -32,6 +32,7 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
@ -307,6 +308,10 @@ public class TrackGraph {
return connectionsByNode.getOrDefault(node, new HashMap<>());
}
public TrackEdge getConnection(Couple<TrackNode> nodes) {
return getConnectionsFrom(nodes.getFirst()).get(nodes.getSecond());
}
public void connectNodes(TrackNodeLocation location, TrackNodeLocation location2, TrackEdge edge) {
TrackNode node1 = nodes.get(location);
TrackNode node2 = nodes.get(location2);

View file

@ -15,9 +15,11 @@ import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ISign
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Entity.RemovalReason;
@ -37,7 +39,10 @@ public class Carriage {
public Couple<Boolean> presentConductors;
public int bogeySpacing;
Couple<CarriageBogey> bogeys;
public Couple<CarriageBogey> bogeys;
public Vec3 positionAnchor;
public Couple<Vec3> rotationAnchors;
CompoundTag serialisedEntity;
WeakReference<CarriageContraptionEntity> entity;
@ -54,6 +59,9 @@ public class Carriage {
this.id = netIdGenerator.incrementAndGet();
this.serialisedEntity = new CompoundTag();
this.pointsInitialised = false;
this.rotationAnchors = Couple.create(null, null);
updateContraptionAnchors();
bogey1.carriage = this;
if (bogey2 != null)
@ -69,9 +77,7 @@ public class Carriage {
entity.setCarriage(this);
contraption.startMoving(level);
contraption.onEntityInitialize(level, entity);
for (CarriageBogey carriageBogey : bogeys)
if (carriageBogey != null)
carriageBogey.updateAnchorPosition();
updateContraptionAnchors();
alignEntity(entity);
List<Entity> players = new ArrayList<>();
@ -89,10 +95,8 @@ public class Carriage {
public double travel(Level level, TrackGraph graph, double distance,
Function<TravellingPoint, ITrackSelector> forwardControl,
Function<TravellingPoint, ITrackSelector> backwardControl, int type) {
Vec3 leadingAnchor = leadingBogey().anchorPosition;
Vec3 trailingAnchor = trailingBogey().anchorPosition;
boolean onTwoBogeys = isOnTwoBogeys();
double stress = onTwoBogeys ? bogeySpacing - leadingAnchor.distanceTo(trailingAnchor) : 0;
double stress = onTwoBogeys ? bogeySpacing - leadingAnchor().distanceTo(trailingAnchor()) : 0;
blocked = false;
MutableDouble distanceMoved = new MutableDouble(distance);
@ -139,13 +143,11 @@ public class Carriage {
distanceMoved.setValue(moved);
}
bogey.updateAnchorPosition();
}
double actualMovement = distanceMoved.getValue();
manageEntity(level, actualMovement);
return actualMovement;
updateContraptionAnchors();
manageEntity(level);
return distanceMoved.getValue();
}
public void updateConductors() {
@ -162,7 +164,7 @@ public class Carriage {
if (!(entity instanceof CarriageContraptionEntity cce))
return;
Vec3 pos = leadingBogey().anchorPosition;
Vec3 pos = positionAnchor;
cce.setPos(pos);
cce.setCarriage(this);
cce.setGraph(train.graph == null ? null : train.graph.id);
@ -171,10 +173,10 @@ public class Carriage {
}
public ChunkPos getChunk() {
return new ChunkPos(new BlockPos(leadingBogey().anchorPosition));
return new ChunkPos(new BlockPos(positionAnchor));
}
protected void manageEntity(Level level, double actualMovement) {
public void manageEntity(Level level) {
CarriageContraptionEntity entity = this.entity.get();
if (entity == null) {
if (CarriageEntityHandler.isActiveChunk(level, getChunk()))
@ -182,12 +184,7 @@ public class Carriage {
} else {
CarriageEntityHandler.validateCarriageEntity(entity);
if (!entity.isAlive() || entity.leftTickingChunks) {
for (Entity passenger : entity.getPassengers())
if (!(passenger instanceof Player))
passenger.remove(RemovalReason.UNLOADED_WITH_PLAYER);
serialisedEntity = entity.serializeNBT();
entity.discard();
this.entity.clear();
removeAndSaveEntity(entity);
return;
}
}
@ -200,19 +197,34 @@ public class Carriage {
entity.syncCarriage();
}
private void removeAndSaveEntity(CarriageContraptionEntity entity) {
for (Entity passenger : entity.getPassengers())
if (!(passenger instanceof Player))
passenger.remove(RemovalReason.UNLOADED_WITH_PLAYER);
serialisedEntity = entity.serializeNBT();
entity.discard();
this.entity.clear();
}
public void updateContraptionAnchors() {
CarriageBogey leadingBogey = leadingBogey();
if (leadingBogey.points.either(t -> t.edge == null))
return;
positionAnchor = leadingBogey.getAnchorPosition();
rotationAnchors = bogeys.mapWithContext((b, first) -> isOnTwoBogeys() ? b.getAnchorPosition()
: leadingBogey.points.get(first)
.getPosition());
}
public void alignEntity(CarriageContraptionEntity entity) {
Vec3 positionVec = isOnTwoBogeys() ? leadingBogey().anchorPosition
: leadingBogey().leading()
.getPosition();
Vec3 coupledVec = isOnTwoBogeys() ? trailingBogey().anchorPosition
: leadingBogey().trailing()
.getPosition();
Vec3 positionVec = rotationAnchors.getFirst();
Vec3 coupledVec = rotationAnchors.getSecond();
double diffX = positionVec.x - coupledVec.x;
double diffY = positionVec.y - coupledVec.y;
double diffZ = positionVec.z - coupledVec.z;
entity.setPos(leadingBogey().anchorPosition);
entity.setPos(positionAnchor);
entity.prevYaw = entity.yaw;
entity.prevPitch = entity.pitch;
entity.yaw = (float) (Mth.atan2(diffZ, diffX) * 180 / Math.PI) + 180;
@ -239,4 +251,54 @@ public class Carriage {
return bogeys.getSecond() != null;
}
public Vec3 leadingAnchor() {
return isOnTwoBogeys() ? rotationAnchors.getFirst() : positionAnchor;
}
public Vec3 trailingAnchor() {
return isOnTwoBogeys() ? rotationAnchors.getSecond() : positionAnchor;
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.put("FirstBogey", bogeys.getFirst()
.write());
if (isOnTwoBogeys())
tag.put("SecondBogey", bogeys.getSecond()
.write());
tag.putInt("Spacing", bogeySpacing);
tag.putBoolean("FrontConductor", presentConductors.getFirst());
tag.putBoolean("BackConductor", presentConductors.getSecond());
CarriageContraptionEntity entity = this.entity.get();
if (entity != null)
serialisedEntity = entity.serializeNBT();
tag.put("Entity", serialisedEntity.copy());
tag.put("PositionAnchor", VecHelper.writeNBT(positionAnchor));
tag.put("RotationAnchors", rotationAnchors.serializeEach(VecHelper::writeNBTCompound));
return tag;
}
public static Carriage read(CompoundTag tag, TrackGraph graph) {
CarriageBogey bogey1 = CarriageBogey.read(tag.getCompound("FirstBogey"), graph);
CarriageBogey bogey2 =
tag.contains("SecondBogey") ? CarriageBogey.read(tag.getCompound("SecondBogey"), graph) : null;
Carriage carriage = new Carriage(bogey1, bogey2, tag.getInt("Spacing"));
carriage.presentConductors = Couple.create(tag.getBoolean("FrontConductor"), tag.getBoolean("BackConductor"));
carriage.serialisedEntity = tag.getCompound("Entity")
.copy();
if (carriage.positionAnchor == null) {
carriage.positionAnchor = VecHelper.readNBT(tag.getList("PositionAnchor", Tag.TAG_DOUBLE));
carriage.rotationAnchors =
Couple.deserializeEach(tag.getList("RotationAnchors", Tag.TAG_COMPOUND), VecHelper::readNBTCompound);
}
return carriage;
}
}

View file

@ -1,15 +1,23 @@
package com.simibubi.create.content.logistics.trains.entity;
import javax.annotation.Nullable;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import net.minecraft.core.Direction.Axis;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.registries.ForgeRegistries;
public class CarriageBogey {
@ -17,14 +25,12 @@ public class CarriageBogey {
IBogeyBlock type;
Couple<TravellingPoint> points;
Vec3 anchorPosition;
LerpedFloat wheelAngle;
LerpedFloat yaw;
LerpedFloat pitch;
public Vec3 leadingCouplingAnchor;
public Vec3 trailingCouplingAnchor;
public Couple<Vec3> couplingAnchors;
int derailAngle;
@ -34,22 +40,27 @@ public class CarriageBogey {
wheelAngle = LerpedFloat.angular();
yaw = LerpedFloat.angular();
pitch = LerpedFloat.angular();
updateAnchorPosition();
derailAngle = Create.RANDOM.nextInt(90) - 45;
derailAngle = Create.RANDOM.nextInt(60) - 30;
couplingAnchors = Couple.create(null, null);
}
public void updateAngles(double distanceMoved) {
public void updateAngles(CarriageContraptionEntity entity, double distanceMoved) {
double angleDiff = 360 * distanceMoved / (Math.PI * 2 * type.getWheelRadius());
Vec3 positionVec = leading().getPosition();
Vec3 coupledVec = trailing().getPosition();
double diffX = positionVec.x - coupledVec.x;
double diffY = positionVec.y - coupledVec.y;
double diffZ = positionVec.z - coupledVec.z;
float yRot = AngleHelper.deg(Mth.atan2(diffZ, diffX)) + 90;
float xRot = AngleHelper.deg(Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)));
if (carriage.train.derailed)
yRot += derailAngle;
float xRot = 0;
float yRot = 0;
if (leading().edge == null || carriage.train.derailed) {
yRot = -90 + entity.yaw - derailAngle;
} else {
Vec3 positionVec = leading().getPosition();
Vec3 coupledVec = trailing().getPosition();
double diffX = positionVec.x - coupledVec.x;
double diffY = positionVec.y - coupledVec.y;
double diffZ = positionVec.z - coupledVec.z;
yRot = AngleHelper.deg(Mth.atan2(diffZ, diffX)) + 90;
xRot = AngleHelper.deg(Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)));
}
wheelAngle.setValue((wheelAngle.getValue() - angleDiff) % 360);
pitch.setValue(xRot);
@ -69,10 +80,11 @@ public class CarriageBogey {
.distanceTo(trailing().getPosition());
}
public void updateAnchorPosition() {
if (points.getFirst().node1 == null)
return;
anchorPosition = points.getFirst()
@Nullable
public Vec3 getAnchorPosition() {
if (leading().edge == null)
return null;
return points.getFirst()
.getPosition()
.add(points.getSecond()
.getPosition())
@ -94,10 +106,24 @@ public class CarriageBogey {
thisOffset = VecHelper.rotate(thisOffset, -entityXRot, Axis.X);
thisOffset = VecHelper.rotate(thisOffset, entityYRot + 90, Axis.Y);
if (leading)
leadingCouplingAnchor = entityPos.add(thisOffset);
else
trailingCouplingAnchor = entityPos.add(thisOffset);
couplingAnchors.set(leading, entityPos.add(thisOffset));
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.putString("Type", ((Block) type).getRegistryName()
.toString());
tag.put("Points", points.serializeEach(TravellingPoint::write));
return tag;
}
public static CarriageBogey read(CompoundTag tag, TrackGraph graph) {
ResourceLocation location = new ResourceLocation(tag.getString("Type"));
IBogeyBlock type = (IBogeyBlock) ForgeRegistries.BLOCKS.getValue(location);
Couple<TravellingPoint> points =
Couple.deserializeEach(tag.getList("Points", Tag.TAG_COMPOUND), c -> TravellingPoint.read(c, graph));
CarriageBogey carriageBogey = new CarriageBogey(type, points.getFirst(), points.getSecond());
return carriageBogey;
}
}

View file

@ -163,11 +163,8 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
carriageData.approach(this, carriage, 1f / getType().updateInterval());
carriage.bogeys.getFirst()
.updateAnchorPosition();
if (carriage.isOnTwoBogeys())
carriage.bogeys.getSecond()
.updateAnchorPosition();
if (!carriage.train.derailed)
carriage.updateContraptionAnchors();
xo = getX();
yo = getY();
@ -182,10 +179,10 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
movingBackwards = signum < 0;
carriage.bogeys.getFirst()
.updateAngles(distanceTo);
.updateAngles(this, distanceTo);
if (carriage.isOnTwoBogeys())
carriage.bogeys.getSecond()
.updateAngles(distanceTo);
.updateAngles(this, distanceTo);
if (carriage.train.derailed)
spawnDerailParticles(carriage);
@ -278,7 +275,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
return true;
if (player.isSpectator())
return false;
if (!toGlobalVector(VecHelper.getCenterOf(controlsLocalPos), 1).closerThan(player.position(), 10))
if (!toGlobalVector(VecHelper.getCenterOf(controlsLocalPos), 1).closerThan(player.position(), 8))
return false;
if (heldControls.contains(5))
return false;

View file

@ -22,7 +22,7 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
if (carriage != null)
for (CarriageBogey bogey : carriage.bogeys)
if (bogey != null)
bogey.leadingCouplingAnchor = bogey.trailingCouplingAnchor = null;
bogey.couplingAnchors.replace(v -> null);
if (!super.shouldRender(entity, clippingHelper, cameraX, cameraY, cameraZ))
return false;
return entity.validForRender;

View file

@ -49,8 +49,8 @@ public class CarriageCouplingRenderer {
CarriageBogey bogey1 = carriage.trailingBogey();
CarriageBogey bogey2 = carriage2.leadingBogey();
Vec3 anchor = bogey1.trailingCouplingAnchor;
Vec3 anchor2 = bogey2.leadingCouplingAnchor;
Vec3 anchor = bogey1.couplingAnchors.getSecond();
Vec3 anchor2 = bogey2.couplingAnchors.getFirst();
if (anchor == null || anchor2 == null)
continue;

View file

@ -19,6 +19,7 @@ import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Mth;
@ -27,18 +28,22 @@ import net.minecraft.world.phys.Vec3;
public class CarriageSyncData {
public Vector<Pair<Couple<Integer>, Float>> wheelLocations;
public Pair<Vec3, Couple<Vec3>> fallbackLocations;
public float distanceToDestination;
public boolean leadingCarriage;
// For Client interpolation
private Pair<Vec3, Couple<Vec3>> fallbackPointSnapshot;
private TravellingPoint[] pointsToApproach;
private float[] pointDistanceSnapshot;
private float destinationDistanceSnapshot;
public CarriageSyncData() {
wheelLocations = new Vector<>(4);
fallbackLocations = null;
pointDistanceSnapshot = new float[4];
pointsToApproach = new TravellingPoint[4];
fallbackPointSnapshot = null;
destinationDistanceSnapshot = 0;
leadingCarriage = false;
for (int i = 0; i < 4; i++) {
@ -51,12 +56,26 @@ public class CarriageSyncData {
CarriageSyncData data = new CarriageSyncData();
for (int i = 0; i < 4; i++)
data.wheelLocations.set(i, wheelLocations.get(i));
if (fallbackLocations != null)
data.fallbackLocations = fallbackLocations.copy();
data.distanceToDestination = distanceToDestination;
data.leadingCarriage = leadingCarriage;
return data;
}
public void write(FriendlyByteBuf buffer) {
buffer.writeBoolean(leadingCarriage);
buffer.writeBoolean(fallbackLocations != null);
if (fallbackLocations != null) {
Vec3 contraptionAnchor = fallbackLocations.getFirst();
Couple<Vec3> rotationAnchors = fallbackLocations.getSecond();
VecHelper.write(contraptionAnchor, buffer);
VecHelper.write(rotationAnchors.getFirst(), buffer);
VecHelper.write(rotationAnchors.getSecond(), buffer);
return;
}
for (Pair<Couple<Integer>, Float> pair : wheelLocations) {
buffer.writeBoolean(pair == null);
if (pair == null)
@ -66,24 +85,37 @@ public class CarriageSyncData {
buffer.writeFloat(pair.getSecond());
}
buffer.writeFloat(distanceToDestination);
buffer.writeBoolean(leadingCarriage);
}
public void read(FriendlyByteBuf buffer) {
leadingCarriage = buffer.readBoolean();
boolean fallback = buffer.readBoolean();
if (fallback) {
fallbackLocations =
Pair.of(VecHelper.read(buffer), Couple.create(VecHelper.read(buffer), VecHelper.read(buffer)));
return;
}
fallbackLocations = null;
for (int i = 0; i < 4; i++) {
if (buffer.readBoolean())
break;
wheelLocations.set(i, Pair.of(Couple.create(buffer::readInt), buffer.readFloat()));
}
distanceToDestination = buffer.readFloat();
leadingCarriage = buffer.readBoolean();
}
public void update(CarriageContraptionEntity entity, Carriage carriage) {
TrackGraph graph = carriage.train.graph;
if (graph == null)
if (graph == null) {
fallbackLocations = Pair.of(carriage.positionAnchor, carriage.rotationAnchors);
carriage.pointsInitialised = true;
setDirty(true);
return;
}
fallbackLocations = null;
leadingCarriage = entity.carriageIndex == (carriage.train.speed >= 0 ? 0 : carriage.train.carriages.size() - 1);
for (boolean first : Iterate.trueAndFalse) {
@ -104,6 +136,13 @@ public class CarriageSyncData {
}
public void apply(CarriageContraptionEntity entity, Carriage carriage) {
fallbackPointSnapshot = null;
if (fallbackLocations != null) {
fallbackPointSnapshot = Pair.of(carriage.positionAnchor, carriage.rotationAnchors);
carriage.pointsInitialised = true;
return;
}
TrackGraph graph = carriage.train.graph;
if (graph == null)
return;
@ -163,11 +202,22 @@ public class CarriageSyncData {
if (!leadingCarriage)
return;
destinationDistanceSnapshot = (float) (distanceToDestination - carriage.train.navigation.distanceToDestination);
}
public void approach(CarriageContraptionEntity entity, Carriage carriage, float partial) {
if (fallbackLocations != null && fallbackPointSnapshot != null) {
carriage.positionAnchor = approachVector(partial, carriage.positionAnchor, fallbackLocations.getFirst(),
fallbackPointSnapshot.getFirst());
carriage.rotationAnchors.replaceWithContext((current, first) -> approachVector(partial, current,
fallbackLocations.getSecond()
.get(first),
fallbackPointSnapshot.getSecond()
.get(first)));
return;
}
TrackGraph graph = carriage.train.graph;
if (graph == null)
return;
@ -204,6 +254,13 @@ public class CarriageSyncData {
}
}
private Vec3 approachVector(float partial, Vec3 current, Vec3 target, Vec3 snapshot) {
if (current == null || snapshot == null)
return target;
return current.add(target.subtract(snapshot)
.scale(partial));
}
public float getDistanceTo(TrackGraph graph, TravellingPoint current, TravellingPoint target, float maxDistance,
boolean forward) {
if (maxDistance == -1)

View file

@ -16,6 +16,7 @@ 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.edgePoint.EdgeData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
@ -26,18 +27,24 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.station
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
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.Level;
import net.minecraft.world.phys.Vec3;
public class Navigation {
Train train;
public Train train;
public GlobalStation destination;
public double distanceToDestination;
public boolean destinationBehindTrain;
List<TrackEdge> currentPath;
List<Couple<TrackNode>> currentPath;
private TravellingPoint signalScout;
public Pair<UUID, Boolean> waitingForSignal;
@ -186,39 +193,29 @@ public class Navigation {
public ITrackSelector control(TravellingPoint mp) {
if (destination == null)
return mp.steer(train.manualSteer, new Vec3(0, 1, 0));
return (graph, pair) -> {
List<Entry<TrackNode, TrackEdge>> options = pair.getSecond();
if (currentPath.isEmpty())
return options.get(0);
TrackEdge target = currentPath.get(0);
for (Entry<TrackNode, TrackEdge> entry : options) {
if (entry.getValue() != target)
continue;
currentPath.remove(0);
return entry;
}
return options.get(0);
};
return (graph, pair) -> navigateOptions(currentPath, graph, pair.getSecond());
}
public ITrackSelector controlSignalScout() {
if (destination == null)
return signalScout.steer(train.manualSteer, new Vec3(0, 1, 0));
List<TrackEdge> pathCopy = new ArrayList<>(currentPath);
return (graph, pair) -> {
List<Entry<TrackNode, TrackEdge>> options = pair.getSecond();
if (pathCopy.isEmpty())
return options.get(0);
TrackEdge target = pathCopy.get(0);
for (Entry<TrackNode, TrackEdge> entry : options) {
if (entry.getValue() != target)
continue;
pathCopy.remove(0);
return entry;
}
return options.get(0);
};
List<Couple<TrackNode>> pathCopy = new ArrayList<>(currentPath);
return (graph, pair) -> navigateOptions(pathCopy, graph, pair.getSecond());
}
private Entry<TrackNode, TrackEdge> navigateOptions(List<Couple<TrackNode>> path, TrackGraph graph,
List<Entry<TrackNode, TrackEdge>> options) {
if (path.isEmpty())
return options.get(0);
Couple<TrackNode> nodes = path.get(0);
TrackEdge targetEdge = graph.getConnection(nodes);
for (Entry<TrackNode, TrackEdge> entry : options) {
if (entry.getValue() != targetEdge)
continue;
path.remove(0);
return entry;
}
return options.get(0);
}
public void cancelNavigation() {
@ -232,7 +229,7 @@ public class Navigation {
}
public double startNavigation(GlobalStation destination, boolean simulate) {
Pair<Double, List<TrackEdge>> pathTo = findPathTo(destination);
Pair<Double, List<Couple<TrackNode>>> pathTo = findPathTo(destination);
boolean noneFound = pathTo.getFirst() == null;
double distance = noneFound ? -1 : Math.abs(pathTo.getFirst());
@ -280,21 +277,21 @@ public class Navigation {
return distanceToDestination;
}
private Pair<Double, List<TrackEdge>> findPathTo(GlobalStation destination) {
private Pair<Double, List<Couple<TrackNode>>> findPathTo(GlobalStation destination) {
TrackGraph graph = train.graph;
List<TrackEdge> path = new ArrayList<>();
List<Couple<TrackNode>> path = new ArrayList<>();
if (graph == null)
return Pair.of(null, path);
MutableObject<Pair<Double, List<TrackEdge>>> frontResult = new MutableObject<>(Pair.of(null, path));
MutableObject<Pair<Double, List<TrackEdge>>> backResult = new MutableObject<>(Pair.of(null, path));
MutableObject<Pair<Double, List<Couple<TrackNode>>>> frontResult = new MutableObject<>(Pair.of(null, path));
MutableObject<Pair<Double, List<Couple<TrackNode>>>> backResult = new MutableObject<>(Pair.of(null, path));
for (boolean forward : Iterate.trueAndFalse) {
if (this.destination == destination && destinationBehindTrain == forward)
continue;
List<TrackEdge> currentPath = new ArrayList<>();
List<Couple<TrackNode>> currentPath = new ArrayList<>();
TravellingPoint initialPoint = forward ? train.carriages.get(0)
.getLeadingPoint()
: train.carriages.get(train.carriages.size() - 1)
@ -313,13 +310,17 @@ public class Navigation {
TrackNode node2 = currentEntry.getFirst()
.getSecond();
Pair<Boolean, TrackEdge> backTrack = reachedVia.get(edge);
TrackEdge toReach = edge;
while (backTrack != null && toReach != initialEdge) {
Pair<Boolean, Couple<TrackNode>> backTrack = reachedVia.get(edge);
Couple<TrackNode> toReach = Couple.create(node1, node2);
TrackEdge edgeReached = edge;
while (backTrack != null) {
if (edgeReached == initialEdge)
break;
if (backTrack.getFirst())
currentPath.add(0, toReach);
toReach = backTrack.getSecond();
backTrack = reachedVia.get(backTrack.getSecond());
edgeReached = graph.getConnection(toReach);
backTrack = reachedVia.get(edgeReached);
}
double position = edge.getLength(node1, node2) - destination.getLocationOn(node1, node2, edge);
@ -336,8 +337,8 @@ public class Navigation {
break;
}
Pair<Double, List<TrackEdge>> front = frontResult.getValue();
Pair<Double, List<TrackEdge>> back = backResult.getValue();
Pair<Double, List<Couple<TrackNode>>> front = frontResult.getValue();
Pair<Double, List<Couple<TrackNode>>> back = backResult.getValue();
boolean frontEmpty = front.getFirst() == null;
boolean backEmpty = back.getFirst() == null;
@ -398,7 +399,7 @@ public class Navigation {
.getTrailingPoint();
Set<TrackEdge> visited = new HashSet<>();
Map<TrackEdge, Pair<Boolean, TrackEdge>> reachedVia = new IdentityHashMap<>();
Map<TrackEdge, Pair<Boolean, Couple<TrackNode>>> reachedVia = new IdentityHashMap<>();
PriorityQueue<Pair<Double, Pair<Couple<TrackNode>, TrackEdge>>> frontier =
new PriorityQueue<>((p1, p2) -> Double.compare(p1.getFirst(), p2.getFirst()));
@ -458,7 +459,7 @@ public class Navigation {
for (Entry<TrackNode, TrackEdge> entry : validTargets) {
TrackNode newNode = entry.getKey();
TrackEdge newEdge = entry.getValue();
reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, edge));
reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2)));
frontier.add(Pair.of(newEdge.getLength(node2, newNode) + distance,
Pair.of(Couple.create(node2, newNode), newEdge)));
}
@ -467,8 +468,51 @@ public class Navigation {
@FunctionalInterface
public interface StationTest {
boolean test(double distance, Map<TrackEdge, Pair<Boolean, TrackEdge>> reachedVia,
boolean test(double distance, Map<TrackEdge, Pair<Boolean, Couple<TrackNode>>> reachedVia,
Pair<Couple<TrackNode>, TrackEdge> current, GlobalStation station);
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
if (destination == null)
return tag;
tag.putUUID("Destination", destination.id);
tag.putDouble("DistanceToDestination", distanceToDestination);
tag.putBoolean("BehindTrain", destinationBehindTrain);
tag.put("Path", NBTHelper.writeCompoundList(currentPath, c -> {
CompoundTag nbt = new CompoundTag();
nbt.put("Nodes", c.map(TrackNode::getLocation)
.map(BlockPos::new)
.serializeEach(NbtUtils::writeBlockPos));
return nbt;
}));
if (waitingForSignal == null)
return tag;
tag.putUUID("BlockingSignal", waitingForSignal.getFirst());
tag.putBoolean("BlockingSignalSide", waitingForSignal.getSecond());
tag.putDouble("DistanceToSignal", distanceToSignal);
return tag;
}
public void read(CompoundTag tag, TrackGraph graph) {
destination =
tag.contains("Destination") ? graph.getPoint(EdgePointType.STATION, tag.getUUID("Destination")) : null;
if (destination == null)
return;
distanceToDestination = tag.getDouble("DistanceToDestination");
destinationBehindTrain = tag.getBoolean("BehindTrain");
currentPath.clear();
NBTHelper.iterateCompoundList(tag.getList("Path", Tag.TAG_COMPOUND),
c -> currentPath.add(Couple
.deserializeEach(c.getList("Nodes", Tag.TAG_COMPOUND),
c2 -> TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(c2)))
.map(graph::locateNode)));
waitingForSignal = tag.contains("BlockingSignal")
? Pair.of(tag.getUUID("BlockingSignal"), tag.getBoolean("BlockingSignalSide"))
: null;
if (waitingForSignal == null)
return;
distanceToSignal = tag.getDouble("DistanceToSignal");
}
}

View file

@ -37,12 +37,17 @@ import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Explosion.BlockInteraction;
@ -132,8 +137,13 @@ public class Train {
}
public void tick(Level level) {
if (graph == null)
Create.RAILWAYS.markTracksDirty();
if (graph == null) {
carriages.forEach(c -> c.manageEntity(level));
updateConductors();
return;
}
updateConductors();
runtime.tick(level);
@ -151,8 +161,8 @@ public class Train {
Carriage carriage = carriages.get(i);
if (previousCarriage != null) {
int target = carriageSpacing.get(i - 1);
Vec3 leadingAnchor = carriage.leadingBogey().anchorPosition;
Vec3 trailingAnchor = previousCarriage.trailingBogey().anchorPosition;
Vec3 leadingAnchor = carriage.leadingAnchor();
Vec3 trailingAnchor = previousCarriage.trailingAnchor();
double actual = leadingAnchor.distanceTo(trailingAnchor);
stress[i - 1] = target - actual;
}
@ -296,11 +306,31 @@ public class Train {
private void collideWithOtherTrains(Level level, Carriage carriage) {
if (derailed)
return;
Collision: for (Train train : Create.RAILWAYS.trains.values()) {
if (train == this)
Vec3 start = (speed < 0 ? carriage.getTrailingPoint() : carriage.getLeadingPoint()).getPosition();
Vec3 end = (speed < 0 ? carriage.getLeadingPoint() : carriage.getTrailingPoint()).getPosition();
Pair<Train, Vec3> collision = findCollidingTrain(level, start, end, this);
if (collision == null)
return;
Train train = collision.getFirst();
double combinedSpeed = Math.abs(speed) + Math.abs(train.speed);
if (combinedSpeed > .2f) {
Vec3 v = collision.getSecond();
level.explode(null, v.x, v.y, v.z, (float) Math.min(3 * combinedSpeed, 5), BlockInteraction.NONE);
}
crash();
train.crash();
}
public static Pair<Train, Vec3> findCollidingTrain(Level level, Vec3 start, Vec3 end, Train ignore) {
for (Train train : Create.RAILWAYS.sided(level).trains.values()) {
if (train == ignore)
continue;
Vec3 start = (speed < 0 ? carriage.getTrailingPoint() : carriage.getLeadingPoint()).getPosition();
Vec3 end = (speed < 0 ? carriage.getLeadingPoint() : carriage.getTrailingPoint()).getPosition();
Vec3 diff = end.subtract(start);
Vec3 lastPoint = null;
@ -309,10 +339,13 @@ public class Train {
if (betweenBits && lastPoint == null)
continue;
Vec3 start2 = otherCarriage.getLeadingPoint()
.getPosition();
Vec3 end2 = otherCarriage.getTrailingPoint()
.getPosition();
TravellingPoint otherLeading = otherCarriage.getLeadingPoint();
TravellingPoint otherTrailing = otherCarriage.getTrailingPoint();
if (otherLeading.edge == null || otherTrailing.edge == null)
continue;
Vec3 start2 = otherLeading.getPosition();
Vec3 end2 = otherTrailing.getPosition();
if (betweenBits) {
end2 = start2;
start2 = lastPoint;
@ -328,6 +361,7 @@ public class Train {
Vec3 normedDiff = diff.normalize();
Vec3 normedDiff2 = diff2.normalize();
double[] intersect = VecHelper.intersect(start, start2, normedDiff, normedDiff2, Axis.Y);
if (intersect == null) {
Vec3 intersectSphere = VecHelper.intersectSphere(start2, normedDiff2, start, .125f);
if (intersectSphere == null)
@ -339,6 +373,7 @@ public class Train {
intersect[0] = intersectSphere.distanceTo(start) - .125;
intersect[1] = intersectSphere.distanceTo(start2) - .125;
}
if (intersect[0] > diff.length())
continue;
if (intersect[1] > diff2.length())
@ -348,18 +383,11 @@ public class Train {
if (intersect[1] < 0)
continue;
double combinedSpeed = Math.abs(speed) + Math.abs(train.speed);
if (combinedSpeed > .2f) {
Vec3 v = start.add(normedDiff.scale(intersect[0]));
level.explode(null, v.x, v.y, v.z, (float) Math.min(3 * combinedSpeed, 5),
BlockInteraction.NONE);
}
crash();
train.crash();
break Collision;
return Pair.of(train, start.add(normedDiff.scale(intersect[0])));
}
}
}
return null;
}
public void crash() {
@ -552,9 +580,8 @@ public class Train {
public void leaveStation() {
GlobalStation currentStation = getCurrentStation();
if (currentStation == null)
return;
currentStation.trainDeparted(this);
if (currentStation != null)
currentStation.trainDeparted(this);
this.currentStation = null;
}
@ -658,4 +685,77 @@ public class Train {
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.putUUID("Id", id);
tag.putUUID("Owner", owner);
if (graph != null)
tag.putUUID("Graph", graph.id);
tag.put("Carriages", NBTHelper.writeCompoundList(carriages, Carriage::write));
tag.putIntArray("CarriageSpacing", carriageSpacing);
tag.putBoolean("DoubleEnded", doubleEnded);
tag.putDouble("Speed", speed);
tag.putDouble("TargetSpeed", targetSpeed);
tag.putString("IconType", icon.id.toString());
tag.putString("Name", Component.Serializer.toJson(name));
if (currentStation != null)
tag.putUUID("Station", currentStation);
tag.putBoolean("Backwards", currentlyBackwards);
tag.putBoolean("StillAssembling", heldForAssembly);
tag.putBoolean("Derailed", derailed);
tag.putBoolean("UpdateSignals", updateSignalBlocks);
tag.put("SignalBlocks", NBTHelper.writeCompoundList(occupiedSignalBlocks, uid -> {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putUUID("Id", uid);
return compoundTag;
}));
tag.put("MigratingPoints", NBTHelper.writeCompoundList(migratingPoints, TrainMigration::write));
tag.put("Runtime", runtime.write());
tag.put("Navigation", navigation.write());
return tag;
}
public static Train read(CompoundTag tag, Map<UUID, TrackGraph> trackNetworks) {
UUID id = tag.getUUID("Id");
UUID owner = tag.getUUID("Owner");
UUID graphId = tag.contains("Graph") ? tag.getUUID("Graph") : null;
TrackGraph graph = graphId == null ? null : trackNetworks.get(graphId);
List<Carriage> carriages = new ArrayList<>();
NBTHelper.iterateCompoundList(tag.getList("Carriages", Tag.TAG_COMPOUND),
c -> carriages.add(Carriage.read(c, graph)));
List<Integer> carriageSpacing = new ArrayList<>();
for (int i : tag.getIntArray("CarriageSpacing"))
carriageSpacing.add(i);
boolean doubleEnded = tag.getBoolean("DoubleEnded");
Train train = new Train(id, owner, graph, carriages, carriageSpacing, doubleEnded);
train.speed = tag.getDouble("Speed");
train.targetSpeed = tag.getDouble("TargetSpeed");
train.icon = TrainIconType.byId(new ResourceLocation(tag.getString("IconType")));
train.name = Component.Serializer.fromJson(tag.getString("Name"));
train.currentStation = tag.contains("Station") ? tag.getUUID("Station") : null;
train.currentlyBackwards = tag.getBoolean("Backwards");
train.heldForAssembly = tag.getBoolean("StillAssembling");
train.derailed = tag.getBoolean("Derailed");
train.updateSignalBlocks = tag.getBoolean("UpdateSignals");
NBTHelper.iterateCompoundList(tag.getList("SignalBlocks", Tag.TAG_COMPOUND),
c -> train.occupiedSignalBlocks.add(c.getUUID("Id")));
NBTHelper.iterateCompoundList(tag.getList("MigratingPoints", Tag.TAG_COMPOUND),
c -> train.migratingPoints.add(TrainMigration.read(c)));
train.runtime.read(tag.getCompound("Runtime"));
train.navigation.read(tag.getCompound("Navigation"), graph);
if (train.getCurrentStation() != null)
train.getCurrentStation()
.reserveFor(train);
return train;
}
}

View file

@ -10,6 +10,10 @@ import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
@ -20,6 +24,8 @@ public class TrainMigration {
boolean curve;
Vec3 fallback;
public TrainMigration() {}
public TrainMigration(TravellingPoint point) {
double t = point.position / point.edge.getLength(point.node1, point.node2);
fallback = point.edge.getPosition(point.node1, point.node2, t);
@ -94,4 +100,25 @@ public class TrainMigration {
return null;
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.putBoolean("Curve", curve);
tag.put("Fallback", VecHelper.writeNBT(fallback));
tag.putDouble("Position", positionOnOldEdge);
tag.put("Nodes", locations.map(BlockPos::new)
.serializeEach(NbtUtils::writeBlockPos));
return tag;
}
public static TrainMigration read(CompoundTag tag) {
TrainMigration trainMigration = new TrainMigration();
trainMigration.curve = tag.getBoolean("Curve");
trainMigration.fallback = VecHelper.readNBT(tag.getList("Fallback", Tag.TAG_DOUBLE));
trainMigration.positionOnOldEdge = tag.getDouble("Position");
trainMigration.locations =
Couple.deserializeEach(tag.getList("Nodes", Tag.TAG_COMPOUND), NbtUtils::readBlockPos)
.map(TrackNodeLocation::fromPackedPos);
return trainMigration;
}
}

View file

@ -13,6 +13,7 @@ import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.network.NetworkEvent.Context;
import net.minecraftforge.registries.ForgeRegistries;
@ -57,8 +58,11 @@ public class TrainPacket extends SimplePacketBase {
carriageSpacing.add(buffer.readVarInt());
boolean doubleEnded = buffer.readBoolean();
train = new Train(trainId, owner, null, carriages, carriageSpacing, doubleEnded);
train.heldForAssembly = buffer.readBoolean();
train.name = Component.Serializer.fromJson(buffer.readUtf());
train.icon = TrainIconType.byId(buffer.readResourceLocation());
}
@Override
@ -90,6 +94,9 @@ public class TrainPacket extends SimplePacketBase {
train.carriageSpacing.forEach(buffer::writeVarInt);
buffer.writeBoolean(train.doubleEnded);
buffer.writeBoolean(train.heldForAssembly);
buffer.writeUtf(Component.Serializer.toJson(train.name));
buffer.writeResourceLocation(train.icon.id);
}
@Override

View file

@ -9,9 +9,11 @@ import java.util.function.Consumer;
import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableInt;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandlerClient;
import com.simibubi.create.content.logistics.trains.GraphLocation;
@ -56,6 +58,7 @@ public class TrainRelocator {
static BlockPos lastHoveredPos;
static Boolean lastHoveredResult;
static List<Vec3> toVisualise;
@OnlyIn(Dist.CLIENT)
public static void onClicked(ClickInputEvent event) {
@ -98,10 +101,22 @@ public class TrainRelocator {
return null;
BlockPos blockPos = blockhit.getBlockPos();
if (simulate && toVisualise != null) {
for (int i = 0; i < toVisualise.size() - 1; i++) {
Vec3 vec1 = toVisualise.get(i);
Vec3 vec2 = toVisualise.get(i + 1);
CreateClient.OUTLINER.showLine(Pair.of(relocating, i), vec1.add(0, -.925f, 0), vec2.add(0, -.925f, 0))
.colored(lastHoveredResult || i != toVisualise.size() - 2 ? 0x95CD41 : 0xEA5C2B)
.disableNormals()
.lineWidth(i % 2 == 1 ? 1 / 6f : 1 / 4f);
}
}
if (simulate) {
if (lastHoveredPos != null && lastHoveredPos.equals(blockPos))
return lastHoveredResult;
lastHoveredPos = blockPos;
toVisualise = null;
}
BlockState blockState = mc.level.getBlockState(blockPos);
@ -113,6 +128,7 @@ public class TrainRelocator {
if (!simulate && result)
AllPackets.channel
.sendToServer(new TrainRelocationPacket(relocatingTrain, blockPos, lookAngle, relocatingEntityId));
return lastHoveredResult = result;
}
@ -139,22 +155,43 @@ public class TrainRelocator {
TravellingPoint probe = new TravellingPoint(node1, node2, edge, graphLocation.position);
ISignalBoundaryListener ignoreSignals = probe.ignoreSignals();
List<Pair<Couple<TrackNode>, Double>> recordedLocations = new ArrayList<>();
Consumer<TravellingPoint> recorder =
tp -> recordedLocations.add(Pair.of(Couple.create(tp.node1, tp.node2), tp.position));
List<Vec3> recordedVecs = new ArrayList<>();
Consumer<TravellingPoint> recorder = tp -> {
recordedLocations.add(Pair.of(Couple.create(tp.node1, tp.node2), tp.position));
recordedVecs.add(tp.getPosition());
};
ITrackSelector steer = probe.steer(SteerDirection.NONE, track.getUpNormal(level, pos, blockState));
MutableBoolean blocked = new MutableBoolean(false);
MutableInt blockingIndex = new MutableInt(0);
train.forEachTravellingPointBackwards((tp, d) -> {
if (blocked.booleanValue())
return;
probe.travel(graph, d, steer, ignoreSignals);
recorder.accept(probe);
if (probe.blocked) {
blocked.setTrue();
return;
}
recorder.accept(probe);
blockingIndex.increment();
});
if (level.isClientSide && simulate && !recordedVecs.isEmpty()) {
toVisualise = new ArrayList<>();
toVisualise.add(recordedVecs.get(0));
}
for (int i = 0; i < recordedVecs.size() - 1; i++) {
Vec3 vec1 = recordedVecs.get(i);
Vec3 vec2 = recordedVecs.get(i + 1);
boolean blocking = i >= blockingIndex.intValue() - 1;
boolean collided = !blocked.booleanValue() && Train.findCollidingTrain(level, vec1, vec2, train) != null;
if (level.isClientSide && simulate)
toVisualise.add(vec2);
if (collided || blocking)
return false;
}
if (blocked.booleanValue())
return false;
@ -167,6 +204,7 @@ public class TrainRelocator {
train.occupiedSignalBlocks.clear();
train.graph = graph;
train.speed = 0;
train.migratingPoints.clear();
if (train.navigation.destination != null)
train.navigation.cancelNavigation();
@ -182,11 +220,22 @@ public class TrainRelocator {
.get(tp.node2);
});
for (Carriage carriage : train.carriages)
carriage.updateContraptionAnchors();
train.status.successfulMigration();
train.collectInitiallyOccupiedSignalBlocks();
return true;
}
@OnlyIn(Dist.CLIENT)
public static void visualise(Train train, int i, Vec3 v1, Vec3 v2, boolean valid) {
CreateClient.OUTLINER.showLine(Pair.of(train, i), v1.add(0, -.825f, 0), v2.add(0, -.825f, 0))
.colored(valid ? 0x95CD41 : 0xEA5C2B)
.disableNormals()
.lineWidth(i % 2 == 1 ? 1 / 6f : 1 / 4f);
}
@OnlyIn(Dist.CLIENT)
public static void clientTick() {
Minecraft mc = Minecraft.getInstance();

View file

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.Vector;
@ -18,12 +19,17 @@ import com.simibubi.create.content.logistics.trains.GraphLocation;
import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.world.phys.Vec3;
public class TravellingPoint {
@ -73,7 +79,7 @@ public class TravellingPoint {
public ITrackSelector follow(TravellingPoint other) {
return follow(other, null);
}
public ITrackSelector follow(TravellingPoint other, @Nullable Consumer<Boolean> success) {
return (graph, pair) -> {
List<Entry<TrackNode, TrackEdge>> validTargets = pair.getSecond();
@ -331,4 +337,35 @@ public class TravellingPoint {
.get(node2);
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
Couple<TrackNode> nodes = Couple.create(node1, node2);
if (nodes.either(Objects::isNull))
return tag;
tag.put("Nodes", nodes.map(TrackNode::getLocation)
.map(BlockPos::new)
.serializeEach(NbtUtils::writeBlockPos));
tag.putDouble("Position", position);
return tag;
}
public static TravellingPoint read(CompoundTag tag, TrackGraph graph) {
if (graph == null)
return new TravellingPoint(null, null, null, 0);
Couple<TrackNode> locs =
tag.contains("Nodes")
? Couple.deserializeEach(tag.getList("Nodes", Tag.TAG_COMPOUND), NbtUtils::readBlockPos)
.map(TrackNodeLocation::fromPackedPos)
.map(graph::locateNode)
: Couple.create(null, null);
if (locs.either(Objects::isNull))
return new TravellingPoint(null, null, null, 0);
double position = tag.getDouble("Position");
return new TravellingPoint(locs.getFirst(), locs.getSecond(), graph.getConnectionsFrom(locs.getFirst())
.get(locs.getSecond()), position);
}
}

View file

@ -9,8 +9,10 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.station
import com.simibubi.create.content.logistics.trains.management.schedule.condition.ScheduleWaitCondition;
import com.simibubi.create.content.logistics.trains.management.schedule.destination.FilteredDestination;
import com.simibubi.create.content.logistics.trains.management.schedule.destination.ScheduleDestination;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.Level;
public class ScheduleRuntime {
@ -180,6 +182,9 @@ public class ScheduleRuntime {
tag.putBoolean("Paused", paused);
if (schedule != null)
tag.put("Schedule", schedule.write());
NBTHelper.writeEnum(tag, "State", state);
tag.putIntArray("ConditionProgress", conditionProgress);
tag.put("ConditionContext", NBTHelper.writeCompoundList(conditionContext, CompoundTag::copy));
return tag;
}
@ -190,6 +195,10 @@ public class ScheduleRuntime {
currentEntry = tag.getInt("CurrentEntry");
if (tag.contains("Schedule"))
schedule = Schedule.fromTag(tag.getCompound("Schedule"));
state = NBTHelper.readEnum(tag, "State", State.class);
for (int i : tag.getIntArray("ConditionProgress"))
conditionProgress.add(i);
NBTHelper.iterateCompoundList(tag.getList("ConditionContext", Tag.TAG_COMPOUND), conditionContext::add);
}
}

View file

@ -645,7 +645,7 @@
"create.train.relocate.abort": "Relocation aborted",
"create.train.relocate.success": "Relocation successful",
"create.train.relocate.valid": "Can relocate to here, Click to Confirm",
"create.train.relocate.invalid": "Cannot relocate Train to this Track",
"create.train.relocate.invalid": "Cannot relocate Train to here",
"create.train.relocate.too_far": "Cannot relocate Train this far away",
"create.contraption.controls.start_controlling": "Now controlling: %1$s",