Politeness Intensifies

- Train navigation now tries to pick destinations and directions avoiding other trains and stations on the way
- Trains now slow to a secondary top speed when approaching a turn
- Attempts to fix trains not always showing up when entering a clients tracking distance
- Substantial increase to carriage contraption's client tracking range
- Fixed Carriage Contraptions starting to render before fully aligned to their position/angle
- Fixed trains remaining stuck to far away signals after being controlled manually for a bit
- Fixed crash when placing tracks into a replaceable block
- Fixed a handful of dist issues for dedicated servers
- Fixed controls allowing control even when a train is not fully assembled yet
- Controls now disengage on relog/esc
This commit is contained in:
simibubi 2022-03-11 23:37:41 +01:00
parent 7ba4af1bea
commit 71e18eb505
25 changed files with 380 additions and 150 deletions

View file

@ -2143,7 +2143,7 @@ d080b1b25e5bc8baf5aee68691b08c7f12ece3b0 assets/create/models/item/windmill_bear
a80fb25a0b655e76be986b5b49fcb0f03461a1ab assets/create/models/item/zinc_nugget.json a80fb25a0b655e76be986b5b49fcb0f03461a1ab assets/create/models/item/zinc_nugget.json
b1689617190c05ef34bd18456b0c7ae09bb3210f assets/create/models/item/zinc_ore.json b1689617190c05ef34bd18456b0c7ae09bb3210f assets/create/models/item/zinc_ore.json
5049f72c327a88f175f6f9425909e098fc711100 assets/create/sounds.json 5049f72c327a88f175f6f9425909e098fc711100 assets/create/sounds.json
0f1b4b980afba9bf2caf583b88e261bba8b10313 data/create/advancements/aesthetics.json 5d0cc4c0255dc241e61c173b31ddca70c88d08e4 data/create/advancements/aesthetics.json
613e64b44bed959da899fdd54c1cacb227fb33f2 data/create/advancements/andesite_alloy.json 613e64b44bed959da899fdd54c1cacb227fb33f2 data/create/advancements/andesite_alloy.json
81885c6bfb85792c88aaa7c9b70f58832945d31f data/create/advancements/andesite_casing.json 81885c6bfb85792c88aaa7c9b70f58832945d31f data/create/advancements/andesite_casing.json
83c046bd200623933545c9e4326f782fb02c87fa data/create/advancements/arm_blaze_burner.json 83c046bd200623933545c9e4326f782fb02c87fa data/create/advancements/arm_blaze_burner.json

View file

@ -28,8 +28,8 @@
"trigger": "create:bracket_apply", "trigger": "create:bracket_apply",
"conditions": { "conditions": {
"accepted_entries": [ "accepted_entries": [
"create:cogwheel", "create:large_cogwheel",
"create:large_cogwheel" "create:cogwheel"
] ]
} }
}, },

View file

@ -41,7 +41,7 @@ public class AllEntityTypes {
GantryContraptionEntity::new, () -> ContraptionEntityRenderer::new, 10, 40, false); GantryContraptionEntity::new, () -> ContraptionEntityRenderer::new, 10, 40, false);
public static final EntityEntry<CarriageContraptionEntity> CARRIAGE_CONTRAPTION = public static final EntityEntry<CarriageContraptionEntity> CARRIAGE_CONTRAPTION =
contraption("carriage_contraption", CarriageContraptionEntity::new, contraption("carriage_contraption", CarriageContraptionEntity::new,
() -> CarriageContraptionEntityRenderer::new, 5, 3, true); () -> CarriageContraptionEntityRenderer::new, 15, 3, true);
public static final EntityEntry<SuperGlueEntity> SUPER_GLUE = public static final EntityEntry<SuperGlueEntity> SUPER_GLUE =
register("super_glue", SuperGlueEntity::new, () -> SuperGlueRenderer::new, MobCategory.MISC, 10, register("super_glue", SuperGlueEntity::new, () -> SuperGlueRenderer::new, MobCategory.MISC, 10,

View file

@ -5,6 +5,9 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Vector; import java.util.Vector;
import org.lwjgl.glfw.GLFW;
import com.mojang.blaze3d.platform.InputConstants;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.ControlsUtil; import com.simibubi.create.foundation.utility.ControlsUtil;
@ -13,6 +16,7 @@ import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelAccessor;
public class ControlsHandler { public class ControlsHandler {
@ -24,6 +28,13 @@ public class ControlsHandler {
static WeakReference<AbstractContraptionEntity> entityRef = new WeakReference<>(null); static WeakReference<AbstractContraptionEntity> entityRef = new WeakReference<>(null);
static BlockPos controlsPos; static BlockPos controlsPos;
public static void levelUnloaded(LevelAccessor level) {
packetCooldown = 0;
entityRef = new WeakReference<>(null);
controlsPos = null;
currentlyPressed.clear();
}
public static void startControlling(AbstractContraptionEntity entity, BlockPos controllerLocalPos) { public static void startControlling(AbstractContraptionEntity entity, BlockPos controllerLocalPos) {
entityRef = new WeakReference<AbstractContraptionEntity>(entity); entityRef = new WeakReference<AbstractContraptionEntity>(entity);
controlsPos = controllerLocalPos; controlsPos = controllerLocalPos;
@ -38,8 +49,8 @@ public class ControlsHandler {
AbstractContraptionEntity abstractContraptionEntity = entityRef.get(); AbstractContraptionEntity abstractContraptionEntity = entityRef.get();
if (!currentlyPressed.isEmpty() && abstractContraptionEntity != null) if (!currentlyPressed.isEmpty() && abstractContraptionEntity != null)
AllPackets.channel.sendToServer( AllPackets.channel.sendToServer(new ControlsInputPacket(currentlyPressed, false,
new ControlsInputPacket(currentlyPressed, false, abstractContraptionEntity.getId(), controlsPos)); abstractContraptionEntity.getId(), controlsPos, false));
packetCooldown = 0; packetCooldown = 0;
entityRef = new WeakReference<>(null); entityRef = new WeakReference<>(null);
@ -57,6 +68,16 @@ public class ControlsHandler {
if (packetCooldown > 0) if (packetCooldown > 0)
packetCooldown--; packetCooldown--;
if (InputConstants.isKeyDown(Minecraft.getInstance()
.getWindow()
.getWindow(), GLFW.GLFW_KEY_ESCAPE)) {
BlockPos pos = controlsPos;
stopControlling();
AllPackets.channel
.sendToServer(new ControlsInputPacket(currentlyPressed, false, entity.getId(), pos, true));
return;
}
Vector<KeyMapping> controls = ControlsUtil.getControls(); Vector<KeyMapping> controls = ControlsUtil.getControls();
Collection<Integer> pressedKeys = new HashSet<>(); Collection<Integer> pressedKeys = new HashSet<>();
for (int i = 0; i < controls.size(); i++) { for (int i = 0; i < controls.size(); i++) {
@ -71,13 +92,14 @@ public class ControlsHandler {
// Released Keys // Released Keys
if (!releasedKeys.isEmpty()) { if (!releasedKeys.isEmpty()) {
AllPackets.channel.sendToServer(new ControlsInputPacket(releasedKeys, false, entity.getId(), controlsPos)); AllPackets.channel
.sendToServer(new ControlsInputPacket(releasedKeys, false, entity.getId(), controlsPos, false));
// AllSoundEvents.CONTROLLER_CLICK.playAt(player.level, player.blockPosition(), 1f, .5f, true); // AllSoundEvents.CONTROLLER_CLICK.playAt(player.level, player.blockPosition(), 1f, .5f, true);
} }
// Newly Pressed Keys // Newly Pressed Keys
if (!newKeys.isEmpty()) { if (!newKeys.isEmpty()) {
AllPackets.channel.sendToServer(new ControlsInputPacket(newKeys, true, entity.getId(), controlsPos)); AllPackets.channel.sendToServer(new ControlsInputPacket(newKeys, true, entity.getId(), controlsPos, false));
packetCooldown = PACKET_RATE; packetCooldown = PACKET_RATE;
// AllSoundEvents.CONTROLLER_CLICK.playAt(player.level, player.blockPosition(), 1f, .75f, true); // AllSoundEvents.CONTROLLER_CLICK.playAt(player.level, player.blockPosition(), 1f, .75f, true);
} }
@ -86,7 +108,7 @@ public class ControlsHandler {
if (packetCooldown == 0) { if (packetCooldown == 0) {
if (!pressedKeys.isEmpty()) { if (!pressedKeys.isEmpty()) {
AllPackets.channel AllPackets.channel
.sendToServer(new ControlsInputPacket(pressedKeys, true, entity.getId(), controlsPos)); .sendToServer(new ControlsInputPacket(pressedKeys, true, entity.getId(), controlsPos, false));
packetCooldown = PACKET_RATE; packetCooldown = PACKET_RATE;
} }
} }

View file

@ -22,13 +22,15 @@ public class ControlsInputPacket extends SimplePacketBase {
private boolean press; private boolean press;
private int contraptionEntityId; private int contraptionEntityId;
private BlockPos controlsPos; private BlockPos controlsPos;
private boolean stopControlling;
public ControlsInputPacket(Collection<Integer> activatedButtons, boolean press, int contraptionEntityId, public ControlsInputPacket(Collection<Integer> activatedButtons, boolean press, int contraptionEntityId,
BlockPos controlsPos) { BlockPos controlsPos, boolean stopControlling) {
this.contraptionEntityId = contraptionEntityId; this.contraptionEntityId = contraptionEntityId;
this.activatedButtons = activatedButtons; this.activatedButtons = activatedButtons;
this.press = press; this.press = press;
this.controlsPos = controlsPos; this.controlsPos = controlsPos;
this.stopControlling = stopControlling;
} }
public ControlsInputPacket(FriendlyByteBuf buffer) { public ControlsInputPacket(FriendlyByteBuf buffer) {
@ -39,6 +41,7 @@ public class ControlsInputPacket extends SimplePacketBase {
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
activatedButtons.add(buffer.readVarInt()); activatedButtons.add(buffer.readVarInt());
controlsPos = buffer.readBlockPos(); controlsPos = buffer.readBlockPos();
stopControlling = buffer.readBoolean();
} }
@Override @Override
@ -48,6 +51,7 @@ public class ControlsInputPacket extends SimplePacketBase {
buffer.writeVarInt(activatedButtons.size()); buffer.writeVarInt(activatedButtons.size());
activatedButtons.forEach(buffer::writeVarInt); activatedButtons.forEach(buffer::writeVarInt);
buffer.writeBlockPos(controlsPos); buffer.writeBlockPos(controlsPos);
buffer.writeBoolean(stopControlling);
} }
@Override @Override
@ -64,11 +68,14 @@ public class ControlsInputPacket extends SimplePacketBase {
Entity entity = world.getEntity(contraptionEntityId); Entity entity = world.getEntity(contraptionEntityId);
if (!(entity instanceof AbstractContraptionEntity ace)) if (!(entity instanceof AbstractContraptionEntity ace))
return; return;
if (!ace.toGlobalVector(Vec3.atCenterOf(controlsPos), 0) if (stopControlling) {
.closerThan(player.position(), 16)) ace.stopControlling(controlsPos);
return; return;
}
ControlsServerHandler.receivePressed(world, ace, controlsPos, uniqueID, activatedButtons, press); if (ace.toGlobalVector(Vec3.atCenterOf(controlsPos), 0)
.closerThan(player.position(), 16))
ControlsServerHandler.receivePressed(world, ace, controlsPos, uniqueID, activatedButtons, press);
}); });
ctx.setPacketHandled(true); ctx.setPacketHandled(true);
} }

View file

@ -49,7 +49,8 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock
public NixieTubeBlock(Properties properties, DyeColor color) { public NixieTubeBlock(Properties properties, DyeColor color) {
super(properties); super(properties);
this.color = color; this.color = color;
registerDefaultState(defaultBlockState().setValue(FACE, DoubleAttachFace.FLOOR)); registerDefaultState(defaultBlockState().setValue(FACE, DoubleAttachFace.FLOOR)
.setValue(WATERLOGGED, false));
} }
@Override @Override

View file

@ -2,7 +2,6 @@ package com.simibubi.create.content.logistics.trains;
import java.util.Iterator; import java.util.Iterator;
import com.jozufozu.flywheel.repack.joml.Math;
import com.jozufozu.flywheel.util.transform.TransformStack; import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.PoseStack.Pose; import com.mojang.blaze3d.vertex.PoseStack.Pose;

View file

@ -57,6 +57,8 @@ public class GlobalRailwayManager {
} }
} }
public void playerLogout(Player player) {}
public void levelLoaded(LevelAccessor level) { public void levelLoaded(LevelAccessor level) {
MinecraftServer server = level.getServer(); MinecraftServer server = level.getServer();
if (server == null || server.overworld() != level) if (server == null || server.overworld() != level)
@ -75,13 +77,6 @@ public class GlobalRailwayManager {
signalEdgeGroups = savedData.getSignalBlocks(); signalEdgeGroups = savedData.getSignalBlocks();
} }
public void levelUnloaded(LevelAccessor level) {
// MinecraftServer server = level.getServer();
// if (server == null || server.overworld() != level)
// return;
// cleanUp();
}
public void cleanUp() { public void cleanUp() {
trackNetworks = new HashMap<>(); trackNetworks = new HashMap<>();
signalEdgeGroups = new HashMap<>(); signalEdgeGroups = new HashMap<>();

View file

@ -136,9 +136,12 @@ public class Carriage {
ISignalBoundaryListener passiveListener = point.ignoreSignals(); ISignalBoundaryListener passiveListener = point.ignoreSignals();
toMove += correction + bogeyCorrection; toMove += correction + bogeyCorrection;
double moved = point.travel(graph, toMove, toMove > 0 ? frontTrackSelector : backTrackSelector, double moved =
toMove > 0 ? atFront ? frontListener : atBack ? backListener : passiveListener point
: atFront ? backListener : atBack ? frontListener : passiveListener); .travel(graph, toMove, toMove > 0 ? frontTrackSelector : backTrackSelector,
toMove > 0 ? atFront ? frontListener : atBack ? backListener : passiveListener
: atFront ? backListener : atBack ? frontListener : passiveListener,
point.ignoreTurns());
blocked |= point.blocked; blocked |= point.blocked;
distanceMoved.setValue(moved); distanceMoved.setValue(moved);
@ -227,8 +230,17 @@ public class Carriage {
entity.setPos(positionAnchor); entity.setPos(positionAnchor);
entity.prevYaw = entity.yaw; entity.prevYaw = entity.yaw;
entity.prevPitch = entity.pitch; entity.prevPitch = entity.pitch;
entity.yaw = (float) (Mth.atan2(diffZ, diffX) * 180 / Math.PI) + 180; entity.yaw = (float) (Mth.atan2(diffZ, diffX) * 180 / Math.PI) + 180;
entity.pitch = (float) (Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)) * 180 / Math.PI) * -1; entity.pitch = (float) (Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)) * 180 / Math.PI) * -1;
if (entity.firstPositionUpdate) {
entity.xo = entity.getX();
entity.yo = entity.getY();
entity.zo = entity.getZ();
entity.prevYaw = entity.yaw;
entity.prevPitch = entity.pitch;
}
} }
public TravellingPoint getLeadingPoint() { public TravellingPoint getLeadingPoint() {

View file

@ -7,6 +7,7 @@ import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat; import com.simibubi.create.foundation.utility.animation.LerpedFloat;
@ -62,9 +63,15 @@ public class CarriageBogey {
xRot = AngleHelper.deg(Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ))); xRot = AngleHelper.deg(Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)));
} }
wheelAngle.setValue((wheelAngle.getValue() - angleDiff) % 360); double newWheelAngle = (wheelAngle.getValue() - angleDiff) % 360;
pitch.setValue(xRot);
yaw.setValue(-yRot); for (boolean twice : Iterate.trueAndFalse) {
if (twice && !entity.firstPositionUpdate)
continue;
wheelAngle.setValue(newWheelAngle);
pitch.setValue(xRot);
yaw.setValue(-yRot);
}
} }
public TravellingPoint leading() { public TravellingPoint leading() {

View file

@ -32,6 +32,8 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class CarriageContraption extends Contraption { public class CarriageContraption extends Contraption {
@ -177,6 +179,7 @@ public class CarriageContraption extends Contraption {
} }
@Override @Override
@OnlyIn(Dist.CLIENT)
public ContraptionLighter<?> makeLighter() { public ContraptionLighter<?> makeLighter() {
return new NonStationaryLighter<>(this); return new NonStationaryLighter<>(this);
} }

View file

@ -61,10 +61,12 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
public boolean movingBackwards; public boolean movingBackwards;
public boolean leftTickingChunks; public boolean leftTickingChunks;
public boolean firstPositionUpdate;
public CarriageContraptionEntity(EntityType<?> type, Level world) { public CarriageContraptionEntity(EntityType<?> type, Level world) {
super(type, world); super(type, world);
validForRender = false; validForRender = false;
firstPositionUpdate = true;
} }
@Override @Override
@ -172,12 +174,15 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
carriage.alignEntity(this); carriage.alignEntity(this);
Vec3 diff = position().subtract(xo, yo, zo); double distanceTo = 0;
Vec3 relativeDiff = VecHelper.rotate(diff, yaw, Axis.Y); if (!firstPositionUpdate) {
double signum = Math.signum(-relativeDiff.x); Vec3 diff = position().subtract(xo, yo, zo);
double distanceTo = diff.length() * signum; Vec3 relativeDiff = VecHelper.rotate(diff, yaw, Axis.Y);
double signum = Math.signum(-relativeDiff.x);
distanceTo = diff.length() * signum;
movingBackwards = signum < 0;
}
movingBackwards = signum < 0;
carriage.bogeys.getFirst() carriage.bogeys.getFirst()
.updateAngles(this, distanceTo); .updateAngles(this, distanceTo);
if (carriage.isOnTwoBogeys()) if (carriage.isOnTwoBogeys())
@ -187,6 +192,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
if (carriage.train.derailed) if (carriage.train.derailed)
spawnDerailParticles(carriage); spawnDerailParticles(carriage);
firstPositionUpdate = false;
validForRender = true; validForRender = true;
} }
@ -200,6 +206,18 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
} }
} }
@Override
public void onClientRemoval() {
super.onClientRemoval();
entityData.set(CARRIAGE_DATA, new CarriageSyncData());
if (carriage != null) {
carriage.pointsInitialised = false;
carriage.leadingBogey().couplingAnchors = Couple.create(null, null);
carriage.trailingBogey().couplingAnchors = Couple.create(null, null);
}
firstPositionUpdate = true;
}
@Override @Override
protected void writeAdditional(CompoundTag compound, boolean spawnPacket) { protected void writeAdditional(CompoundTag compound, boolean spawnPacket) {
super.writeAdditional(compound, spawnPacket); super.writeAdditional(compound, spawnPacket);
@ -250,7 +268,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
return false; return false;
if (carriage.train.derailed) if (carriage.train.derailed)
return false; return false;
if (!level.isClientSide && carriage.train.heldForAssembly) { if (carriage.train.heldForAssembly) {
player.displayClientMessage(Lang.translate("schedule.train_still_assembling"), true); player.displayClientMessage(Lang.translate("schedule.train_still_assembling"), true);
return false; return false;
} }
@ -260,6 +278,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
train.status.manualControls(); train.status.manualControls();
train.navigation.cancelNavigation(); train.navigation.cancelNavigation();
train.runtime.paused = true; train.runtime.paused = true;
train.navigation.waitingForSignal = null;
return true; return true;
} }
@ -347,7 +366,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
if (lookAhead != null) { if (lookAhead != null) {
if (spaceDown) { if (spaceDown) {
nav.startNavigation(lookAhead, false); nav.startNavigation(lookAhead, -1, false);
navDistanceTotal = nav.distanceToDestination; navDistanceTotal = nav.distanceToDestination;
return true; return true;
} }

View file

@ -25,7 +25,7 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
bogey.couplingAnchors.replace(v -> null); bogey.couplingAnchors.replace(v -> null);
if (!super.shouldRender(entity, clippingHelper, cameraX, cameraY, cameraZ)) if (!super.shouldRender(entity, clippingHelper, cameraX, cameraY, cameraZ))
return false; return false;
return entity.validForRender; return entity.validForRender && !entity.firstPositionUpdate;
} }
@Override @Override

View file

@ -239,8 +239,8 @@ public class CarriageSyncData {
TravellingPoint toApproach = pointsToApproach[index]; TravellingPoint toApproach = pointsToApproach[index];
point.travel(graph, partial * f, point.travel(graph, partial * f,
point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)), point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)), point.ignoreSignals(),
point.ignoreSignals()); point.ignoreTurns());
// could not pathfind to server location // could not pathfind to server location
if (!success.booleanValue()) { if (!success.booleanValue()) {

View file

@ -6,10 +6,14 @@ import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects;
import java.util.PriorityQueue; import java.util.PriorityQueue;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.Create; import com.simibubi.create.Create;
@ -49,6 +53,7 @@ public class Navigation {
private TravellingPoint signalScout; private TravellingPoint signalScout;
public Pair<UUID, Boolean> waitingForSignal; public Pair<UUID, Boolean> waitingForSignal;
public double distanceToSignal; public double distanceToSignal;
public int ticksWaitingForSignal;
public Navigation(Train train) { public Navigation(Train train) {
this.train = train; this.train = train;
@ -84,9 +89,11 @@ public class Navigation {
destination.reserveFor(train); destination.reserveFor(train);
double acceleration = AllConfigs.SERVER.trains.getAccelerationMPTT(); double acceleration = AllConfigs.SERVER.trains.getAccelerationMPTT();
double turnTopSpeed = AllConfigs.SERVER.trains.getTurningTopSpeedMPT();
double brakingDistance = (train.speed * train.speed) / (2 * acceleration); double brakingDistance = (train.speed * train.speed) / (2 * acceleration);
double speedMod = destinationBehindTrain ? -1 : 1; double speedMod = destinationBehindTrain ? -1 : 1;
double preDepartureLookAhead = train.getCurrentStation() != null ? 4.5 : 0; double preDepartureLookAhead = train.getCurrentStation() != null ? 4.5 : 0;
double distanceToNextCurve = -1;
// Signals // Signals
if (train.graph != null) { if (train.graph != null) {
@ -98,10 +105,14 @@ public class Navigation {
: train.carriages.get(train.carriages.size() - 1) : train.carriages.get(train.carriages.size() - 1)
.getTrailingPoint(); .getTrailingPoint();
if (waitingForSignal == null) if (waitingForSignal == null) {
distanceToSignal = Double.MAX_VALUE; distanceToSignal = Double.MAX_VALUE;
ticksWaitingForSignal = 0;
}
if (distanceToSignal > 1 / 16f) { if (distanceToSignal > 1 / 16f) {
MutableDouble curveDistanceTracker = new MutableDouble(-1);
signalScout.node1 = leadingPoint.node1; signalScout.node1 = leadingPoint.node1;
signalScout.node2 = leadingPoint.node2; signalScout.node2 = leadingPoint.node2;
signalScout.edge = leadingPoint.edge; signalScout.edge = leadingPoint.edge;
@ -124,7 +135,15 @@ public class Navigation {
return; return;
} }
signalEdgeGroup.reserved = boundary; signalEdgeGroup.reserved = boundary;
}, (distance, edge) -> {
float current = curveDistanceTracker.floatValue();
if (current == -1 || distance < current)
curveDistanceTracker.setValue(distance);
}); });
distanceToNextCurve = curveDistanceTracker.floatValue();
} else {
ticksWaitingForSignal++;
} }
} }
@ -165,7 +184,17 @@ public class Navigation {
} }
} }
train.targetSpeed = targetDistance > brakingDistance ? topSpeed * speedMod : 0; double targetSpeed = targetDistance > brakingDistance ? topSpeed * speedMod : 0;
if (distanceToNextCurve != -1) {
double slowingDistance = brakingDistance - (turnTopSpeed * turnTopSpeed) / (2 * acceleration);
double targetTurnSpeed =
distanceToNextCurve > slowingDistance ? topSpeed * speedMod : turnTopSpeed * speedMod;
if (Math.abs(targetTurnSpeed) < Math.abs(targetSpeed))
targetSpeed = targetTurnSpeed;
}
train.targetSpeed = targetSpeed;
train.approachTargetSpeed(1); train.approachTargetSpeed(1);
} }
@ -228,16 +257,17 @@ public class Navigation {
train.runtime.transitInterrupted(); train.runtime.transitInterrupted();
} }
public double startNavigation(GlobalStation destination, boolean simulate) { public double startNavigation(GlobalStation destination, double maxCost, boolean simulate) {
Pair<Double, List<Couple<TrackNode>>> pathTo = findPathTo(destination); DiscoveredPath pathTo = findPathTo(destination, maxCost);
boolean noneFound = pathTo.getFirst() == null; boolean noneFound = pathTo == null;
double distance = noneFound ? -1 : Math.abs(pathTo.getFirst()); double distance = noneFound ? -1 : Math.abs(pathTo.distance);
double cost = noneFound ? -1 : pathTo.cost;
if (simulate) if (simulate)
return distance; return cost;
distanceToDestination = distance; distanceToDestination = distance;
currentPath = pathTo.getSecond(); currentPath = pathTo.path;
if (noneFound) { if (noneFound) {
distanceToDestination = 0; distanceToDestination = 0;
@ -246,7 +276,7 @@ public class Navigation {
return -1; return -1;
} }
destinationBehindTrain = pathTo.getFirst() < 0; destinationBehindTrain = pathTo.distance < 0;
if (this.destination == destination) if (this.destination == destination)
return 0; return 0;
@ -274,24 +304,20 @@ public class Navigation {
} }
this.destination = destination; this.destination = destination;
return distanceToDestination; return cost;
} }
private Pair<Double, List<Couple<TrackNode>>> findPathTo(GlobalStation destination) { @Nullable
private DiscoveredPath findPathTo(GlobalStation destination, double maxCost) {
TrackGraph graph = train.graph; TrackGraph graph = train.graph;
List<Couple<TrackNode>> path = new ArrayList<>();
if (graph == null) if (graph == null)
return Pair.of(null, path); return null;
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));
Couple<DiscoveredPath> results = Couple.create(null, null);
for (boolean forward : Iterate.trueAndFalse) { for (boolean forward : Iterate.trueAndFalse) {
if (this.destination == destination && destinationBehindTrain == forward) if (this.destination == destination && destinationBehindTrain == forward)
continue; continue;
List<Couple<TrackNode>> currentPath = new ArrayList<>();
TravellingPoint initialPoint = forward ? train.carriages.get(0) TravellingPoint initialPoint = forward ? train.carriages.get(0)
.getLeadingPoint() .getLeadingPoint()
: train.carriages.get(train.carriages.size() - 1) : train.carriages.get(train.carriages.size() - 1)
@ -300,7 +326,7 @@ public class Navigation {
: graph.getConnectionsFrom(initialPoint.node2) : graph.getConnectionsFrom(initialPoint.node2)
.get(initialPoint.node1); .get(initialPoint.node1);
search(Double.MAX_VALUE, forward, (distance, reachedVia, currentEntry, globalStation) -> { search(Double.MAX_VALUE, maxCost, forward, (distance, cost, reachedVia, currentEntry, globalStation) -> {
if (globalStation != destination) if (globalStation != destination)
return false; return false;
@ -310,6 +336,7 @@ public class Navigation {
TrackNode node2 = currentEntry.getFirst() TrackNode node2 = currentEntry.getFirst()
.getSecond(); .getSecond();
List<Couple<TrackNode>> currentPath = new ArrayList<>();
Pair<Boolean, Couple<TrackNode>> backTrack = reachedVia.get(edge); Pair<Boolean, Couple<TrackNode>> backTrack = reachedVia.get(edge);
Couple<TrackNode> toReach = Couple.create(node1, node2); Couple<TrackNode> toReach = Couple.create(node1, node2);
TrackEdge edgeReached = edge; TrackEdge edgeReached = edge;
@ -325,11 +352,7 @@ public class Navigation {
double position = edge.getLength(node1, node2) - destination.getLocationOn(node1, node2, edge); double position = edge.getLength(node1, node2) - destination.getLocationOn(node1, node2, edge);
double distanceToDestination = distance - position; double distanceToDestination = distance - position;
results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath));
if (forward)
frontResult.setValue(Pair.of(distanceToDestination, currentPath));
else
backResult.setValue(Pair.of(-distanceToDestination, currentPath));
return true; return true;
}); });
@ -337,11 +360,11 @@ public class Navigation {
break; break;
} }
Pair<Double, List<Couple<TrackNode>>> front = frontResult.getValue(); DiscoveredPath front = results.getFirst();
Pair<Double, List<Couple<TrackNode>>> back = backResult.getValue(); DiscoveredPath back = results.getSecond();
boolean frontEmpty = front.getFirst() == null; boolean frontEmpty = front == null;
boolean backEmpty = back.getFirst() == null; boolean backEmpty = back == null;
if (backEmpty) if (backEmpty)
return front; return front;
if (frontEmpty) if (frontEmpty)
@ -354,10 +377,22 @@ public class Navigation {
if (!canDriveForward) if (!canDriveForward)
return back; return back;
boolean frontBetter = -back.getFirst() > front.getFirst(); boolean frontBetter = maxCost == -1 ? -back.distance > front.distance : back.cost > front.cost;
return frontBetter ? front : back; return frontBetter ? front : back;
} }
public class DiscoveredPath {
List<Couple<TrackNode>> path;
double distance;
double cost;
public DiscoveredPath(double distance, double cost, List<Couple<TrackNode>> path) {
this.distance = distance;
this.cost = cost;
this.path = path;
}
}
public GlobalStation findNearestApproachable(boolean forward) { public GlobalStation findNearestApproachable(boolean forward) {
TrackGraph graph = train.graph; TrackGraph graph = train.graph;
if (graph == null) if (graph == null)
@ -368,7 +403,7 @@ public class Navigation {
double minDistance = .75f * (train.speed * train.speed) / (2 * acceleration); double minDistance = .75f * (train.speed * train.speed) / (2 * acceleration);
double maxDistance = Math.max(32, 1.5f * (train.speed * train.speed) / (2 * acceleration)); double maxDistance = Math.max(32, 1.5f * (train.speed * train.speed) / (2 * acceleration));
search(maxDistance, forward, (distance, reachedVia, currentEntry, globalStation) -> { search(maxDistance, forward, (distance, cost, reachedVia, currentEntry, globalStation) -> {
if (distance < minDistance) if (distance < minDistance)
return false; return false;
@ -389,10 +424,36 @@ public class Navigation {
} }
public void search(double maxDistance, boolean forward, StationTest stationTest) { public void search(double maxDistance, boolean forward, StationTest stationTest) {
search(maxDistance, -1, forward, stationTest);
}
public void search(double maxDistance, double maxCost, boolean forward, StationTest stationTest) {
TrackGraph graph = train.graph; TrackGraph graph = train.graph;
if (graph == null) if (graph == null)
return; return;
Map<TrackEdge, Integer> penalties = new IdentityHashMap<>();
boolean costRelevant = maxCost >= 0;
if (costRelevant) {
for (Train otherTrain : Create.RAILWAYS.trains.values()) {
if (otherTrain.graph != graph)
continue;
int navigationPenalty = otherTrain.getNavigationPenalty();
otherTrain.getEndpointEdges()
.forEach(nodes -> {
if (nodes.either(Objects::isNull))
return;
for (boolean flip : Iterate.trueAndFalse) {
TrackEdge e = graph.getConnection(flip ? nodes.swap() : nodes);
if (e == null)
continue;
int existing = penalties.getOrDefault(e, 0);
penalties.put(e, existing + navigationPenalty / 2);
}
});
}
}
TravellingPoint startingPoint = forward ? train.carriages.get(0) TravellingPoint startingPoint = forward ? train.carriages.get(0)
.getLeadingPoint() .getLeadingPoint()
: train.carriages.get(train.carriages.size() - 1) : train.carriages.get(train.carriages.size() - 1)
@ -400,8 +461,7 @@ public class Navigation {
Set<TrackEdge> visited = new HashSet<>(); Set<TrackEdge> visited = new HashSet<>();
Map<TrackEdge, Pair<Boolean, Couple<TrackNode>>> reachedVia = new IdentityHashMap<>(); Map<TrackEdge, Pair<Boolean, Couple<TrackNode>>> reachedVia = new IdentityHashMap<>();
PriorityQueue<Pair<Double, Pair<Couple<TrackNode>, TrackEdge>>> frontier = PriorityQueue<FrontierEntry> frontier = new PriorityQueue<>();
new PriorityQueue<>((p1, p2) -> Double.compare(p1.getFirst(), p2.getFirst()));
TrackNode initialNode1 = forward ? startingPoint.node1 : startingPoint.node2; TrackNode initialNode1 = forward ? startingPoint.node1 : startingPoint.node2;
TrackNode initialNode2 = forward ? startingPoint.node2 : startingPoint.node1; TrackNode initialNode2 = forward ? startingPoint.node2 : startingPoint.node1;
@ -410,20 +470,23 @@ public class Navigation {
double distanceToNode2 = forward ? initialEdge.getLength(initialNode1, initialNode2) - startingPoint.position double distanceToNode2 = forward ? initialEdge.getLength(initialNode1, initialNode2) - startingPoint.position
: startingPoint.position; : startingPoint.position;
frontier.add(Pair.of(distanceToNode2, Pair.of(Couple.create(initialNode1, initialNode2), initialEdge))); frontier.add(new FrontierEntry(distanceToNode2, 0, initialNode1, initialNode2, initialEdge));
Search: while (!frontier.isEmpty()) { Search: while (!frontier.isEmpty()) {
Pair<Double, Pair<Couple<TrackNode>, TrackEdge>> poll = frontier.poll(); FrontierEntry entry = frontier.poll();
double distance = poll.getFirst();
double distance = entry.distance;
int penalty = entry.penalty;
if (distance > maxDistance) if (distance > maxDistance)
continue; continue;
Pair<Couple<TrackNode>, TrackEdge> currentEntry = poll.getSecond(); TrackEdge edge = entry.edge;
TrackEdge edge = currentEntry.getSecond(); TrackNode node1 = entry.node1;
TrackNode node1 = currentEntry.getFirst() TrackNode node2 = entry.node2;
.getFirst();
TrackNode node2 = currentEntry.getFirst() if (costRelevant)
.getSecond(); penalty += penalties.getOrDefault(edge, 0);
EdgeData signalData = edge.getEdgeData(); EdgeData signalData = edge.getEdgeData();
if (signalData.hasPoints()) { if (signalData.hasPoints()) {
@ -431,44 +494,78 @@ public class Navigation {
if (node1 == initialNode1 if (node1 == initialNode1
&& point.getLocationOn(node1, node2, edge) < edge.getLength(node1, node2) - distanceToNode2) && point.getLocationOn(node1, node2, edge) < edge.getLength(node1, node2) - distanceToNode2)
continue; continue;
if (costRelevant && distance + penalty > maxCost)
continue Search;
if (!point.canNavigateVia(node2)) if (!point.canNavigateVia(node2))
continue Search; continue Search;
if (point instanceof GlobalStation station && station.canApproachFrom(node2) if (point instanceof GlobalStation station) {
&& stationTest.test(distance, reachedVia, currentEntry, station)) if (station.getPresentTrain() != null)
return; penalty += Train.Penalties.STATION_WITH_TRAIN;
if (station.canApproachFrom(node2) && stationTest.test(distance, distance + penalty, reachedVia,
Pair.of(Couple.create(node1, node2), edge), station))
return;
penalty += Train.Penalties.STATION;
}
} }
} }
if (costRelevant && distance + penalty > maxCost)
continue;
List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>(); List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>();
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node2); Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node2);
for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) { for (Entry<TrackNode, TrackEdge> connection : connectionsFrom.entrySet()) {
TrackNode newNode = entry.getKey(); TrackNode newNode = connection.getKey();
TrackEdge newEdge = entry.getValue(); TrackEdge newEdge = connection.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false); Vec3 currentDirection = edge.getDirection(node1, node2, false);
Vec3 newDirection = newEdge.getDirection(node2, newNode, true); Vec3 newDirection = newEdge.getDirection(node2, newNode, true);
if (currentDirection.dot(newDirection) < 3 / 4f) if (currentDirection.dot(newDirection) < 3 / 4f)
continue; continue;
if (!visited.add(entry.getValue())) if (!visited.add(connection.getValue()))
continue; continue;
validTargets.add(entry); validTargets.add(connection);
} }
if (validTargets.isEmpty()) if (validTargets.isEmpty())
continue; continue;
for (Entry<TrackNode, TrackEdge> entry : validTargets) { for (Entry<TrackNode, TrackEdge> target : validTargets) {
TrackNode newNode = entry.getKey(); TrackNode newNode = target.getKey();
TrackEdge newEdge = entry.getValue(); TrackEdge newEdge = target.getValue();
double newDistance = newEdge.getLength(node2, newNode) + distance;
int newPenalty = penalty;
reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2)));
frontier.add(Pair.of(newEdge.getLength(node2, newNode) + distance, frontier.add(new FrontierEntry(newDistance, newPenalty, node2, newNode, newEdge));
Pair.of(Couple.create(node2, newNode), newEdge)));
} }
} }
} }
private class FrontierEntry implements Comparable<FrontierEntry> {
double distance;
int penalty;
TrackNode node1;
TrackNode node2;
TrackEdge edge;
public FrontierEntry(double distance, int penalty, TrackNode node1, TrackNode node2, TrackEdge edge) {
this.distance = distance;
this.penalty = penalty;
this.node1 = node1;
this.node2 = node2;
this.edge = edge;
}
@Override
public int compareTo(FrontierEntry o) {
return Double.compare(distance + penalty, o.distance + o.penalty);
}
}
@FunctionalInterface @FunctionalInterface
public interface StationTest { public interface StationTest {
boolean test(double distance, Map<TrackEdge, Pair<Boolean, Couple<TrackNode>>> reachedVia, boolean test(double distance, double cost, Map<TrackEdge, Pair<Boolean, Couple<TrackNode>>> reachedVia,
Pair<Couple<TrackNode>, TrackEdge> current, GlobalStation station); Pair<Couple<TrackNode>, TrackEdge> current, GlobalStation station);
} }
@ -491,6 +588,7 @@ public class Navigation {
tag.putUUID("BlockingSignal", waitingForSignal.getFirst()); tag.putUUID("BlockingSignal", waitingForSignal.getFirst());
tag.putBoolean("BlockingSignalSide", waitingForSignal.getSecond()); tag.putBoolean("BlockingSignalSide", waitingForSignal.getSecond());
tag.putDouble("DistanceToSignal", distanceToSignal); tag.putDouble("DistanceToSignal", distanceToSignal);
tag.putInt("TicksWaitingForSignal", ticksWaitingForSignal);
return tag; return tag;
} }
@ -513,6 +611,7 @@ public class Navigation {
if (waitingForSignal == null) if (waitingForSignal == null)
return; return;
distanceToSignal = tag.getDouble("DistanceToSignal"); distanceToSignal = tag.getDouble("DistanceToSignal");
ticksWaitingForSignal = tag.getInt("TicksWaitingForSignal");
} }
} }

View file

@ -35,6 +35,7 @@ import com.simibubi.create.content.logistics.trains.management.schedule.Schedule
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime.State; import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime.State;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
@ -246,24 +247,34 @@ public class Train {
} }
private void updateNavigationTarget(double distance) { private void updateNavigationTarget(double distance) {
if (navigation.destination != null) { if (navigation.destination == null)
boolean recalculate = navigation.distanceToDestination % 100 > 20; return;
boolean imminentRecalculate = navigation.distanceToDestination > 5;
double toSubstract = navigation.destinationBehindTrain ? -distance : distance; boolean recalculate = navigation.distanceToDestination % 100 > 20;
navigation.distanceToDestination -= toSubstract; boolean imminentRecalculate = navigation.distanceToDestination > 5;
boolean signalMode = navigation.waitingForSignal != null; double toSubstract = navigation.destinationBehindTrain ? -distance : distance;
navigation.distanceToDestination -= toSubstract;
boolean signalMode = navigation.waitingForSignal != null;
boolean navigatingManually = runtime.paused;
if (signalMode) {
navigation.distanceToSignal -= toSubstract;
recalculate = navigation.distanceToSignal % 100 > 20;
}
if (recalculate && (signalMode ? navigation.distanceToSignal : navigation.distanceToDestination) % 100 <= 20
|| imminentRecalculate && navigation.distanceToDestination <= 5) {
if (signalMode) { if (signalMode) {
navigation.distanceToSignal -= toSubstract; navigation.waitingForSignal = null;
recalculate = navigation.distanceToSignal % 100 > 20; return;
} }
if (recalculate && (signalMode ? navigation.distanceToSignal : navigation.distanceToDestination) % 100 <= 20 GlobalStation destination = navigation.destination;
|| imminentRecalculate && navigation.distanceToDestination <= 5) { if (!navigatingManually) {
if (signalMode) { GlobalStation preferredDestination = runtime.findNextStation();
navigation.waitingForSignal = null; if (preferredDestination != null)
return; destination = preferredDestination;
}
navigation.startNavigation(navigation.destination, false);
} }
navigation.startNavigation(destination, navigatingManually ? -1 : Double.MAX_VALUE, false);
} }
} }
@ -680,11 +691,36 @@ public class Train {
return; return;
occupiedSignalBlocks.add(id); occupiedSignalBlocks.add(id);
prevGroup.setValue(id); prevGroup.setValue(id);
})); }), signalScout.ignoreTurns());
}); });
} }
public Couple<Couple<TrackNode>> getEndpointEdges() {
return Couple.create(carriages.get(0)
.getLeadingPoint(),
carriages.get(carriages.size() - 1)
.getTrailingPoint())
.map(tp -> Couple.create(tp.node1, tp.node2));
}
public static class Penalties {
static final int STATION = 200, STATION_WITH_TRAIN = 300;
static final int MANUAL_TRAIN = 200, IDLE_TRAIN = 700, ARRIVING_TRAIN = 50, WAITING_TRAIN = 50, ANY_TRAIN = 25;
}
public int getNavigationPenalty() {
if (manualTick)
return Penalties.MANUAL_TRAIN;
if (runtime.getSchedule() == null || runtime.paused)
return Penalties.IDLE_TRAIN;
if (navigation.waitingForSignal != null && navigation.ticksWaitingForSignal > 0)
return Penalties.WAITING_TRAIN + Math.min(navigation.ticksWaitingForSignal / 20, 1000);
if (navigation.destination != null && navigation.distanceToDestination < 50 || navigation.distanceToSignal < 20)
return Penalties.ARRIVING_TRAIN;
return Penalties.ANY_TRAIN;
}
public CompoundTag write() { public CompoundTag write() {
CompoundTag tag = new CompoundTag(); CompoundTag tag = new CompoundTag();
tag.putUUID("Id", id); tag.putUUID("Id", id);

View file

@ -24,6 +24,7 @@ import com.simibubi.create.content.logistics.trains.TrackGraphHelper;
import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ISignalBoundaryListener; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ISignalBoundaryListener;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITurnListener;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.SteerDirection; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.SteerDirection;
import com.simibubi.create.foundation.item.TooltipHelper; import com.simibubi.create.foundation.item.TooltipHelper;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
@ -105,7 +106,7 @@ public class TrainRelocator {
return null; return null;
BlockPos blockPos = blockhit.getBlockPos(); BlockPos blockPos = blockhit.getBlockPos();
if (simulate && toVisualise != null) { if (simulate && toVisualise != null && lastHoveredResult != null) {
for (int i = 0; i < toVisualise.size() - 1; i++) { for (int i = 0; i < toVisualise.size() - 1; i++) {
Vec3 vec1 = toVisualise.get(i); Vec3 vec1 = toVisualise.get(i);
Vec3 vec2 = toVisualise.get(i + 1); Vec3 vec2 = toVisualise.get(i + 1);
@ -158,6 +159,7 @@ public class TrainRelocator {
TravellingPoint probe = new TravellingPoint(node1, node2, edge, graphLocation.position); TravellingPoint probe = new TravellingPoint(node1, node2, edge, graphLocation.position);
ISignalBoundaryListener ignoreSignals = probe.ignoreSignals(); ISignalBoundaryListener ignoreSignals = probe.ignoreSignals();
ITurnListener ignoreTurns = probe.ignoreTurns();
List<Pair<Couple<TrackNode>, Double>> recordedLocations = new ArrayList<>(); List<Pair<Couple<TrackNode>, Double>> recordedLocations = new ArrayList<>();
List<Vec3> recordedVecs = new ArrayList<>(); List<Vec3> recordedVecs = new ArrayList<>();
Consumer<TravellingPoint> recorder = tp -> { Consumer<TravellingPoint> recorder = tp -> {
@ -171,7 +173,7 @@ public class TrainRelocator {
train.forEachTravellingPointBackwards((tp, d) -> { train.forEachTravellingPointBackwards((tp, d) -> {
if (blocked.booleanValue()) if (blocked.booleanValue())
return; return;
probe.travel(graph, d, steer, ignoreSignals); probe.travel(graph, d, steer, ignoreSignals, ignoreTurns);
recorder.accept(probe); recorder.accept(probe);
if (probe.blocked) { if (probe.blocked) {
blocked.setTrue(); blocked.setTrue();

View file

@ -56,6 +56,9 @@ public class TravellingPoint {
public static interface ISignalBoundaryListener extends BiConsumer<Double, Pair<SignalBoundary, Couple<UUID>>> { public static interface ISignalBoundaryListener extends BiConsumer<Double, Pair<SignalBoundary, Couple<UUID>>> {
}; };
public static interface ITurnListener extends BiConsumer<Double, TrackEdge> {
};
public TravellingPoint() {} public TravellingPoint() {}
public TravellingPoint(TrackNode node1, TrackNode node2, TrackEdge edge, double position) { public TravellingPoint(TrackNode node1, TrackNode node2, TrackEdge edge, double position) {
@ -70,6 +73,11 @@ public class TravellingPoint {
}; };
} }
public ITurnListener ignoreTurns() {
return (d, c) -> {
};
}
public ITrackSelector random() { public ITrackSelector random() {
return (graph, pair) -> pair.getSecond() return (graph, pair) -> pair.getSecond()
.get(Create.RANDOM.nextInt(pair.getSecond() .get(Create.RANDOM.nextInt(pair.getSecond()
@ -170,7 +178,7 @@ public class TravellingPoint {
} }
public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector, public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector,
ISignalBoundaryListener signalListener) { ISignalBoundaryListener signalListener, ITurnListener turnListener) {
blocked = false; blocked = false;
double edgeLength = edge.getLength(node1, node2); double edgeLength = edge.getLength(node1, node2);
if (distance == 0) if (distance == 0)
@ -185,7 +193,7 @@ public class TravellingPoint {
boolean forward = distance > 0; boolean forward = distance > 0;
double collectedDistance = forward ? -prevPos : -edgeLength + prevPos; double collectedDistance = forward ? -prevPos : -edgeLength + prevPos;
edgeTraversedFrom(graph, forward, signalListener, prevPos, collectedDistance); edgeTraversedFrom(graph, forward, signalListener, turnListener, prevPos, collectedDistance);
if (forward) { if (forward) {
// Moving forward // Moving forward
@ -223,7 +231,9 @@ public class TravellingPoint {
position -= edgeLength; position -= edgeLength;
collectedDistance += edgeLength; collectedDistance += edgeLength;
edgeTraversedFrom(graph, forward, signalListener, 0, collectedDistance); if (edge.isTurn())
turnListener.accept(collectedDistance, edge);
edgeTraversedFrom(graph, forward, signalListener, turnListener, 0, collectedDistance);
prevPos = 0; prevPos = 0;
edgeLength = edge.getLength(node1, node2); edgeLength = edge.getLength(node1, node2);
@ -268,7 +278,7 @@ public class TravellingPoint {
edgeLength = edge.getLength(node1, node2); edgeLength = edge.getLength(node1, node2);
position += edgeLength; position += edgeLength;
edgeTraversedFrom(graph, forward, signalListener, edgeLength, collectedDistance); edgeTraversedFrom(graph, forward, signalListener, turnListener, edgeLength, collectedDistance);
} }
} }
@ -277,7 +287,10 @@ public class TravellingPoint {
} }
private void edgeTraversedFrom(TrackGraph graph, boolean forward, ISignalBoundaryListener signalListener, private void edgeTraversedFrom(TrackGraph graph, boolean forward, ISignalBoundaryListener signalListener,
double prevPos, double totalDistance) { ITurnListener turnListener, double prevPos, double totalDistance) {
if (edge.isTurn())
turnListener.accept(Math.max(0, totalDistance), edge);
EdgeData signalsOnEdge = edge.getEdgeData(); EdgeData signalsOnEdge = edge.getEdgeData();
if (!signalsOnEdge.hasSignalBoundaries()) if (!signalsOnEdge.hasSignalBoundaries())
return; return;

View file

@ -97,7 +97,7 @@ public class ScheduleRuntime {
destinationReached(); destinationReached();
return; return;
} }
if (train.navigation.startNavigation(nextStation, false) != -1) if (train.navigation.startNavigation(nextStation, Double.MAX_VALUE, false) != -1)
state = State.IN_TRANSIT; state = State.IN_TRANSIT;
} }
@ -134,7 +134,7 @@ public class ScheduleRuntime {
if (!globalStation.name.matches(regex)) if (!globalStation.name.matches(regex))
continue; continue;
boolean matchesCurrent = train.currentStation != null && train.currentStation.equals(globalStation.id); boolean matchesCurrent = train.currentStation != null && train.currentStation.equals(globalStation.id);
double cost = matchesCurrent ? 0 : train.navigation.startNavigation(globalStation, true); double cost = matchesCurrent ? 0 : train.navigation.startNavigation(globalStation, bestCost, true);
if (cost < 0) if (cost < 0)
continue; continue;
if (cost > bestCost) if (cost > bestCost)

View file

@ -58,9 +58,10 @@ public class TrackBlockItem extends BlockItem {
} }
boolean placing = !(state.getBlock() instanceof ITrackBlock); boolean placing = !(state.getBlock() instanceof ITrackBlock);
if (placing && !state.getMaterial() if (placing) {
.isReplaceable()) { if (!state.getMaterial()
pos = pos.relative(pContext.getClickedFace()); .isReplaceable())
pos = pos.relative(pContext.getClickedFace());
state = getPlacementState(pContext); state = getPlacementState(pContext);
if (state == null) if (state == null)
return InteractionResult.FAIL; return InteractionResult.FAIL;

View file

@ -6,12 +6,11 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.curiosities.girder.GirderBlock; import com.simibubi.create.content.curiosities.girder.GirderBlock;
import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -144,9 +143,9 @@ public class TrackPaver {
int floor = Mth.floor(yValue); int floor = Mth.floor(yValue);
boolean placeSlab = slab && yValue - floor >= .5; boolean placeSlab = slab && yValue - floor >= .5;
BlockPos targetPos = new BlockPos(entry.getKey() BlockPos targetPos = new BlockPos(entry.getKey()
.getKey(), floor, .getFirst(), floor,
entry.getKey() entry.getKey()
.getValue()); .getSecond());
targetPos = targetPos.offset(tePosition) targetPos = targetPos.offset(tePosition)
.above(placeSlab ? 1 : 0); .above(placeSlab ? 1 : 0);
BlockState stateToPlace = BlockState stateToPlace =

View file

@ -159,8 +159,7 @@ public class ClientEvents {
} }
@SubscribeEvent @SubscribeEvent
public static void onRenderSelection(DrawSelectionEvent event) { public static void onRenderSelection(DrawSelectionEvent event) {}
}
@SubscribeEvent @SubscribeEvent
public static void onJoin(ClientPlayerNetworkEvent.LoggedInEvent event) { public static void onJoin(ClientPlayerNetworkEvent.LoggedInEvent event) {
@ -183,12 +182,13 @@ public class ClientEvents {
@SubscribeEvent @SubscribeEvent
public static void onUnloadWorld(WorldEvent.Unload event) { public static void onUnloadWorld(WorldEvent.Unload event) {
if (event.getWorld() if (!event.getWorld()
.isClientSide()) { .isClientSide())
CreateClient.invalidateRenderers(); return;
CreateClient.SOUL_PULSE_EFFECT_HANDLER.refresh(); CreateClient.invalidateRenderers();
AnimationTickHolder.reset(); CreateClient.SOUL_PULSE_EFFECT_HANDLER.refresh();
} AnimationTickHolder.reset();
ControlsHandler.levelUnloaded(event.getWorld());
} }
@SubscribeEvent @SubscribeEvent
@ -281,13 +281,15 @@ public class ClientEvents {
Fluid fluid = fluidstate.getType(); Fluid fluid = fluidstate.getType();
if (AllFluids.CHOCOLATE.get().isSame(fluid)) { if (AllFluids.CHOCOLATE.get()
.isSame(fluid)) {
event.setDensity(5f); event.setDensity(5f);
event.setCanceled(true); event.setCanceled(true);
return; return;
} }
if (AllFluids.HONEY.get().isSame(fluid)) { if (AllFluids.HONEY.get()
.isSame(fluid)) {
event.setDensity(1.5f); event.setDensity(1.5f);
event.setCanceled(true); event.setCanceled(true);
return; return;
@ -307,18 +309,20 @@ public class ClientEvents {
Level level = Minecraft.getInstance().level; Level level = Minecraft.getInstance().level;
BlockPos blockPos = info.getBlockPosition(); BlockPos blockPos = info.getBlockPosition();
FluidState fluidstate = level.getFluidState(blockPos); FluidState fluidstate = level.getFluidState(blockPos);
if (info.getPosition().y > blockPos.getY() + fluidstate.getHeight(level, blockPos)) if (info.getPosition().y > blockPos.getY() + fluidstate.getHeight(level, blockPos))
return; return;
Fluid fluid = fluidstate.getType(); Fluid fluid = fluidstate.getType();
if (AllFluids.CHOCOLATE.get().isSame(fluid)) { if (AllFluids.CHOCOLATE.get()
.isSame(fluid)) {
event.setRed(98 / 256f); event.setRed(98 / 256f);
event.setGreen(32 / 256f); event.setGreen(32 / 256f);
event.setBlue(32 / 256f); event.setBlue(32 / 256f);
} }
if (AllFluids.HONEY.get().isSame(fluid)) { if (AllFluids.HONEY.get()
.isSame(fluid)) {
event.setRed(234 / 256f); event.setRed(234 / 256f);
event.setGreen(174 / 256f); event.setGreen(174 / 256f);
event.setBlue(47 / 256f); event.setBlue(47 / 256f);

View file

@ -49,6 +49,7 @@ import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
import net.minecraftforge.event.entity.player.AttackEntityEvent; import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
import net.minecraftforge.event.server.ServerStoppingEvent; import net.minecraftforge.event.server.ServerStoppingEvent;
import net.minecraftforge.event.world.BiomeLoadingEvent; import net.minecraftforge.event.world.BiomeLoadingEvent;
import net.minecraftforge.event.world.BlockEvent.FluidPlaceBlockEvent; import net.minecraftforge.event.world.BlockEvent.FluidPlaceBlockEvent;
@ -83,6 +84,12 @@ public class CommonEvents {
Create.RAILWAYS.playerLogin(player); Create.RAILWAYS.playerLogin(player);
} }
@SubscribeEvent
public static void playerLoggedOut(PlayerLoggedOutEvent event) {
Player player = event.getPlayer();
Create.RAILWAYS.playerLogout(player);
}
@SubscribeEvent @SubscribeEvent
public static void whenFluidsMeet(FluidPlaceBlockEvent event) { public static void whenFluidsMeet(FluidPlaceBlockEvent event) {
BlockState blockState = event.getOriginalState(); BlockState blockState = event.getOriginalState();
@ -188,7 +195,6 @@ public class CommonEvents {
Create.REDSTONE_LINK_NETWORK_HANDLER.onUnloadWorld(world); Create.REDSTONE_LINK_NETWORK_HANDLER.onUnloadWorld(world);
Create.TORQUE_PROPAGATOR.onUnloadWorld(world); Create.TORQUE_PROPAGATOR.onUnloadWorld(world);
WorldAttached.invalidateWorld(world); WorldAttached.invalidateWorld(world);
Create.RAILWAYS.levelUnloaded(world);
} }
@SubscribeEvent @SubscribeEvent

View file

@ -2,9 +2,10 @@ package com.simibubi.create.foundation.config;
public class CTrains extends ConfigBase { public class CTrains extends ConfigBase {
public final ConfigFloat trainTopSpeed = f(40, 0, "trainTopSpeed", Comments.mps, Comments.trainTopSpeed); public final ConfigFloat trainTopSpeed = f(36, 0, "trainTopSpeed", Comments.mps, Comments.trainTopSpeed);
public final ConfigFloat trainAcceleration = public final ConfigFloat trainTurningTopSpeed =
f(6, 0, "trainAcceleration", Comments.acc, Comments.trainAcceleration); f(18, 0, "trainTurningTopSpeed", Comments.mps, Comments.trainTurningTopSpeed);
public final ConfigFloat trainAcceleration = f(4, 0, "trainAcceleration", Comments.acc, Comments.trainAcceleration);
@Override @Override
public String getName() { public String getName() {
@ -15,6 +16,10 @@ public class CTrains extends ConfigBase {
return trainTopSpeed.getF() / 20; return trainTopSpeed.getF() / 20;
} }
public double getTurningTopSpeedMPT() {
return trainTurningTopSpeed.getF() / 20;
}
public double getAccelerationMPTT() { public double getAccelerationMPTT() {
return trainAcceleration.getF() / 400; return trainAcceleration.getF() / 400;
} }
@ -23,6 +28,7 @@ public class CTrains extends ConfigBase {
static String mps = "[in Blocks/Second]"; static String mps = "[in Blocks/Second]";
static String acc = "[in Blocks/Second²]"; static String acc = "[in Blocks/Second²]";
static String trainTopSpeed = "The top speed of any assembled Train."; static String trainTopSpeed = "The top speed of any assembled Train.";
static String trainTurningTopSpeed = "The top speed of Trains during a turn.";
static String trainAcceleration = "The acceleration of any assembled Train."; static String trainAcceleration = "The acceleration of any assembled Train.";
} }

View file

@ -4,7 +4,6 @@ import java.util.Random;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.jozufozu.flywheel.repack.joml.Math;
import com.mojang.math.Quaternion; import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;