From c3e1e9df3fc40e35c1ca00cc7037ac4a94809d03 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Thu, 3 Feb 2022 02:51:14 +0100 Subject: [PATCH] Graph hopping, Part II - added "/create dumpRailways" command - trains now react to changes of the graph beneath them - trains now notify owner of unexpected issues - filtered navigation wildcard works now - navigation now smarter I guess - navigation cancelled when track path gets disconnected - stations and trains no longer disassociate when graph is edited - schedule no longer skips entry when navigation gets interrupted - trains can now be in a "de-railed" state --- .../com/simibubi/create/AllEntityTypes.java | 2 +- .../content/logistics/trains/TrackGraph.java | 78 ++++++-- .../logistics/trains/entity/Carriage.java | 54 +++--- .../entity/CarriageContraptionEntity.java | 18 ++ .../logistics/trains/entity/Navigation.java | 66 ++++--- .../logistics/trains/entity/Train.java | 182 ++++++++++++++++-- .../trains/entity/TrainMigration.java | 96 +++++++++ .../logistics/trains/entity/TrainStatus.java | 85 ++++++++ ...{MovingPoint.java => TravellingPoint.java} | 39 ++-- .../trains/management/GlobalStation.java | 25 ++- .../trains/management/GraphLocation.java | 13 ++ .../trains/management/ScheduleRuntime.java | 41 +++- .../trains/management/StationEditPacket.java | 8 +- .../trains/management/StationRenderer.java | 2 +- .../trains/management/StationScreen.java | 4 +- .../trains/management/StationTileEntity.java | 26 ++- .../management/TrackTargetingBehaviour.java | 10 +- .../schedule/FilteredDestination.java | 3 + .../schedule/StationPoweredCondition.java | 2 +- .../schedule/StationUnloadedCondition.java | 2 +- .../logistics/trains/track/TrackRenderer.java | 2 +- .../foundation/command/AllCommands.java | 1 + .../command/DumpRailwaysCommand.java | 118 ++++++++++++ .../TileEntityConfigurationPacket.java | 6 +- 24 files changed, 733 insertions(+), 150 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainStatus.java rename src/main/java/com/simibubi/create/content/logistics/trains/entity/{MovingPoint.java => TravellingPoint.java} (80%) create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/management/GraphLocation.java create mode 100644 src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java diff --git a/src/main/java/com/simibubi/create/AllEntityTypes.java b/src/main/java/com/simibubi/create/AllEntityTypes.java index 825acef8d..9af30091d 100644 --- a/src/main/java/com/simibubi/create/AllEntityTypes.java +++ b/src/main/java/com/simibubi/create/AllEntityTypes.java @@ -41,7 +41,7 @@ public class AllEntityTypes { GantryContraptionEntity::new, () -> ContraptionEntityRenderer::new, 10, 40, false); public static final EntityEntry CARRIAGE_CONTRAPTION = contraption("carriage_contraption", CarriageContraptionEntity::new, - () -> CarriageContraptionEntityRenderer::new, 5, 100, true); + () -> CarriageContraptionEntityRenderer::new, 5, 3, true); public static final EntityEntry SUPER_GLUE = register("super_glue", SuperGlueEntity::new, () -> SuperGlueRenderer::new, MobCategory.MISC, 10, diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java index bdb7caa88..8fc03f02f 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java @@ -19,10 +19,10 @@ import javax.annotation.Nullable; import com.simibubi.create.Create; import com.simibubi.create.CreateClient; import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation; +import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.content.logistics.trains.management.GlobalStation; import com.simibubi.create.foundation.utility.Color; import com.simibubi.create.foundation.utility.Couple; -import com.simibubi.create.foundation.utility.Debug; import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.VecHelper; @@ -40,8 +40,8 @@ public class TrackGraph { public static final AtomicInteger netIdGenerator = new AtomicInteger(); - UUID id; - Color color; + public UUID id; + public Color color; Map nodes; Map nodesById; @@ -58,33 +58,37 @@ public class TrackGraph { nodes = new HashMap<>(); nodesById = new HashMap<>(); connectionsByNode = new IdentityHashMap<>(); - color = Color.rainbowColor(new Random().nextInt()); + color = Color.rainbowColor(new Random(graphID.getLeastSignificantBits()).nextInt()); stations = new HashMap<>(); } // - + @Nullable public GlobalStation getStation(UUID id) { return stations.get(id); } - + public Collection getStations() { return stations.values(); } - + public void removeStation(UUID id) { stations.remove(id); markDirty(); } - + public void addStation(GlobalStation station) { stations.put(station.id, station); markDirty(); } - + // - + + public Set getNodes() { + return nodes.keySet(); + } + public TrackNode locateNode(Vec3 position) { return locateNode(new TrackNodeLocation(position)); } @@ -120,19 +124,34 @@ public class TrackGraph { TrackNode removed = nodes.remove(location); if (removed == null) return false; - + if (level != null) { - for (Iterator iterator = stations.keySet().iterator(); iterator.hasNext();) { + for (Iterator iterator = stations.keySet() + .iterator(); iterator.hasNext();) { UUID uuid = iterator.next(); GlobalStation globalStation = stations.get(uuid); Couple loc = globalStation.edgeLocation; - if (loc.getFirst().equals(location) || loc.getSecond().equals(location)) { + if (loc.getFirst() + .equals(location) + || loc.getSecond() + .equals(location)) { globalStation.migrate(level); iterator.remove(); } } } + Map trains = Create.RAILWAYS.trains; + for (Iterator iterator = trains.keySet() + .iterator(); iterator.hasNext();) { + UUID uuid = iterator.next(); + Train train = trains.get(uuid); + if (train.graph != this) + continue; + if (train.isTravellingOn(removed)) + train.detachFromTracks(); + } + nodesById.remove(removed.netId); if (!connectionsByNode.containsKey(removed)) return true; @@ -153,7 +172,7 @@ public class TrackGraph { toOther.nodes.putAll(nodes); toOther.nodesById.putAll(nodesById); toOther.connectionsByNode.putAll(connectionsByNode); - for (GlobalStation globalStation : stations.values()) + for (GlobalStation globalStation : stations.values()) toOther.addStation(globalStation); nodesById.forEach((id, node) -> Create.RAILWAYS.sync.nodeAdded(toOther, node)); @@ -165,6 +184,16 @@ public class TrackGraph { nodes.clear(); nodesById.clear(); connectionsByNode.clear(); + + Map trains = Create.RAILWAYS.trains; + for (Iterator iterator = trains.keySet() + .iterator(); iterator.hasNext();) { + UUID uuid = iterator.next(); + Train train = trains.get(uuid); + if (train.graph != this) + continue; + train.graph = toOther; + } } public Set findDisconnectedGraphs(@Nullable Map preAssignedIds) { @@ -214,12 +243,15 @@ public class TrackGraph { if (!connections.isEmpty()) { target.connectionsByNode.put(node, connections); for (TrackNode entry : connections.keySet()) { - for (Iterator iterator = stations.keySet().iterator(); iterator.hasNext();) { + for (Iterator iterator = stations.keySet() + .iterator(); iterator.hasNext();) { UUID uuid = iterator.next(); GlobalStation globalStation = stations.get(uuid); Couple loc = globalStation.edgeLocation; - if (loc.getFirst().equals(location1) && loc.getSecond().equals(entry.getLocation())) { - Debug.debugChat("Station " + globalStation.name + " migrated directly due to graph split"); + if (loc.getFirst() + .equals(location1) + && loc.getSecond() + .equals(entry.getLocation())) { target.addStation(globalStation); iterator.remove(); } @@ -227,6 +259,18 @@ public class TrackGraph { } } + Map trains = Create.RAILWAYS.trains; + for (Iterator iterator = trains.keySet() + .iterator(); iterator.hasNext();) { + UUID uuid = iterator.next(); + Train train = trains.get(uuid); + if (train.graph != this) + continue; + if (!train.isTravellingOn(node)) + continue; + train.graph = target; + } + nodes.remove(location1); nodesById.remove(node.getNetId()); connectionsByNode.remove(node); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Carriage.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Carriage.java index f5b6338ee..d39a2ac58 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Carriage.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Carriage.java @@ -9,8 +9,10 @@ import javax.annotation.Nullable; import org.apache.commons.lang3.mutable.MutableDouble; import org.apache.commons.lang3.mutable.MutableObject; +import com.simibubi.create.Create; import com.simibubi.create.content.logistics.trains.IBogeyBlock; -import com.simibubi.create.content.logistics.trains.entity.MovingPoint.ITrackSelector; +import com.simibubi.create.content.logistics.trains.TrackGraph; +import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.VecHelper; @@ -31,6 +33,7 @@ public class Carriage { public CarriageContraption contraption; public int bogeySpacing; public int id; + public boolean blocked; WeakReference entity; Couple bogeys; @@ -40,6 +43,10 @@ public class Carriage { this.bogeys = Couple.create(bogey1, bogey2); this.entity = new WeakReference<>(null); this.id = netIdGenerator.incrementAndGet(); + + bogey1.carriage = this; + if (bogey2 != null) + bogey2.carriage = this; } public void setTrain(Train train) { @@ -51,11 +58,13 @@ public class Carriage { contraption.setCarriage(this); } - public double travel(Level level, double distance, @Nullable Function control) { + public double travel(Level level, TrackGraph graph, double distance, + @Nullable Function control) { Vec3 leadingAnchor = leadingBogey().anchorPosition; Vec3 trailingAnchor = trailingBogey().anchorPosition; boolean onTwoBogeys = isOnTwoBogeys(); double stress = onTwoBogeys ? bogeySpacing - leadingAnchor.distanceTo(trailingAnchor) : 0; + blocked = false; // positive stress: points should move apart // negative stress: points should move closer @@ -65,7 +74,7 @@ public class Carriage { double leadingPointModifier = 0.5d; double trailingPointModifier = -0.5d; - MutableObject previous = new MutableObject<>(); + MutableObject previous = new MutableObject<>(); MutableDouble distanceMoved = new MutableDouble(distance); bogeys.forEachWithContext((bogey, firstBogey) -> { @@ -76,15 +85,16 @@ public class Carriage { double bogeyStress = bogey.getStress(); bogey.points.forEachWithContext((point, first) -> { - MovingPoint prevPoint = previous.getValue(); + TravellingPoint prevPoint = previous.getValue(); ITrackSelector trackSelector = prevPoint == null ? control == null ? point.random() : control.apply(point) : point.follow(prevPoint); double correction = bogeyStress * (first ? leadingPointModifier : trailingPointModifier); double toMove = distanceMoved.getValue(); - double moved = point.travel(toMove, trackSelector); - point.travel(correction + bogeyCorrection, trackSelector); + double moved = point.travel(graph, toMove, trackSelector); + point.travel(graph, correction + bogeyCorrection, trackSelector); + blocked |= point.blocked; distanceMoved.setValue(moved); previous.setValue(point); @@ -158,11 +168,11 @@ public class Carriage { entity.discard(); } - public MovingPoint getLeadingPoint() { + public TravellingPoint getLeadingPoint() { return leadingBogey().leading(); } - public MovingPoint getTrailingPoint() { + public TravellingPoint getTrailingPoint() { return trailingBogey().trailing(); } @@ -180,8 +190,9 @@ public class Carriage { public static class CarriageBogey { + Carriage carriage; IBogeyBlock type; - Couple points; + Couple points; Vec3 anchorPosition; LerpedFloat wheelAngle; @@ -190,14 +201,17 @@ public class Carriage { public Vec3 leadingCouplingAnchor; public Vec3 trailingCouplingAnchor; - - public CarriageBogey(IBogeyBlock type, MovingPoint point, MovingPoint point2) { + + int derailAngle; + + public CarriageBogey(IBogeyBlock type, TravellingPoint point, TravellingPoint point2) { this.type = type; points = Couple.create(point, point2); wheelAngle = LerpedFloat.angular(); yaw = LerpedFloat.angular(); pitch = LerpedFloat.angular(); updateAnchorPosition(); + derailAngle = Create.RANDOM.nextInt(90) - 45; } public void updateAngles(double distanceMoved) { @@ -209,16 +223,20 @@ public class Carriage { 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; + wheelAngle.setValue((wheelAngle.getValue() - angleDiff) % 360); pitch.setValue(xRot); yaw.setValue(-yRot); } - public MovingPoint leading() { + public TravellingPoint leading() { return points.getFirst(); } - public MovingPoint trailing() { + public TravellingPoint trailing() { return points.getSecond(); } @@ -240,16 +258,6 @@ public class Carriage { Vec3 thisOffset = type.getConnectorAnchorOffset(); thisOffset = thisOffset.multiply(1, 1, leading ? -1 : 1); -// msr.rotateY(viewYRot + 90) -// .rotateX(-viewXRot) -// .rotateY(180) -// .translate(0, 0, first ? 0 : -bogeySpacing) -// .rotateY(-180) -// .rotateX(viewXRot) -// .rotateY(-viewYRot - 90) -// .rotateY(bogey.yaw.getValue(partialTicks)) -// .rotateX(bogey.pitch.getValue(partialTicks)) - thisOffset = VecHelper.rotate(thisOffset, pitch.getValue(partialTicks), Axis.X); thisOffset = VecHelper.rotate(thisOffset, yaw.getValue(partialTicks), Axis.Y); thisOffset = VecHelper.rotate(thisOffset, -entityYRot - 90, Axis.Y); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java index 8924c0412..a2c637012 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java @@ -3,7 +3,9 @@ package com.simibubi.create.content.logistics.trains.entity; import com.simibubi.create.AllEntityTypes; import com.simibubi.create.Create; import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity; +import com.simibubi.create.foundation.utility.VecHelper; +import net.minecraft.core.particles.ParticleTypes; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; @@ -44,13 +46,29 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { xo = getX(); yo = getY(); zo = getZ(); + carriage.moveEntity(this); + double distanceTo = position().distanceTo(new Vec3(xo, yo, zo)); carriage.bogeys.getFirst() .updateAngles(distanceTo); if (carriage.isOnTwoBogeys()) carriage.bogeys.getSecond() .updateAngles(distanceTo); + + if (carriage.train.derailed) + spawnDerailParticles(carriage); + + } + + Vec3 derailParticleOffset = VecHelper.offsetRandomly(Vec3.ZERO, Create.RANDOM, 1.5f) + .multiply(1, .25f, 1); + + private void spawnDerailParticles(Carriage carriage) { + if (random.nextFloat() < 1 / 20f) { + Vec3 v = position().add(derailParticleOffset); + level.addParticle(ParticleTypes.CAMPFIRE_COSY_SMOKE, v.x, v.y, v.z, 0, .04, 0); + } } @Override diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java index 4ee81da62..3a7382a66 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java @@ -13,7 +13,7 @@ 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.MovingPoint.ITrackSelector; +import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.content.logistics.trains.management.GlobalStation; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Pair; @@ -24,18 +24,14 @@ import net.minecraft.world.phys.Vec3; public class Navigation { - TrackGraph graph; Train train; - public GlobalStation destination; public double distanceToDestination; - - List path; + List currentPath; public Navigation(Train train, TrackGraph graph) { this.train = train; - this.graph = graph; - path = new ArrayList<>(); + currentPath = new ArrayList<>(); } public void tick(Level level) { @@ -47,7 +43,7 @@ public class Navigation { if (distanceToDestination < 1 / 32f) { distanceToDestination = 0; train.speed = 0; - path.clear(); + currentPath.clear(); train.arriveAt(destination); destination = null; return; @@ -83,13 +79,13 @@ public class Navigation { return destination != null; } - public ITrackSelector control(MovingPoint mp) { - return list -> { - if (!path.isEmpty()) { - TrackEdge target = path.get(0); + public ITrackSelector control(TravellingPoint mp) { + return (graph, list) -> { + if (!currentPath.isEmpty()) { + TrackEdge target = currentPath.get(0); for (Entry entry : list) { if (entry.getValue() == target) { - path.remove(0); + currentPath.remove(0); return entry; } } @@ -100,30 +96,50 @@ public class Navigation { public void cancelNavigation() { distanceToDestination = 0; - path.clear(); + currentPath.clear(); if (destination == null) return; destination.cancelReservation(train); + destination = null; + train.runtime.transitInterrupted(); } - public void setDestination(GlobalStation destination) { - findPathTo(destination); - if (distanceToDestination == 0) - return; + public double startNavigation(GlobalStation destination, boolean simulate) { + Pair> pathTo = findPathTo(destination); + + if (simulate) + return pathTo.getFirst(); + + distanceToDestination = pathTo.getFirst(); + currentPath = pathTo.getSecond(); + if (distanceToDestination == -1) { + distanceToDestination = 0; + if (this.destination != null) + cancelNavigation(); + return -1; + } + if (this.destination == destination) - return; + return 0; + train.leave(); this.destination = destination; + return distanceToDestination; } - private void findPathTo(GlobalStation destination) { - path.clear(); - this.distanceToDestination = 0; + private Pair> findPathTo(GlobalStation destination) { + TrackGraph graph = train.graph; + List path = new ArrayList<>(); + double distanceToDestination = 0; + + if (graph == null) + return Pair.of(-1d, path); + Couple target = destination.edgeLocation; PriorityQueue, TrackEdge>>> frontier = new PriorityQueue<>((p1, p2) -> Double.compare(p1.getFirst(), p2.getFirst())); - MovingPoint leadingPoint = train.carriages.get(0) + TravellingPoint leadingPoint = train.carriages.get(0) .getLeadingPoint(); Set visited = new HashSet<>(); Map> reachedVia = new IdentityHashMap<>(); @@ -167,7 +183,7 @@ public class Navigation { else distanceToDestination += train.getTotalLength() + 2; distanceToDestination -= position; - return; + return Pair.of(distanceToDestination, path); } for (Entry entry : graph.getConnectionsFrom(node2) @@ -194,6 +210,8 @@ public class Navigation { Pair.of(Couple.create(node2, newNode), newEdge))); } } + + return Pair.of(-1d, path); } } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java index 963f8fd3e..d1542afad 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java @@ -1,21 +1,37 @@ package com.simibubi.create.content.logistics.trains.entity; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import java.util.UUID; +import java.util.function.Consumer; import java.util.function.Function; +import javax.annotation.Nullable; + +import org.apache.commons.lang3.mutable.MutableBoolean; + import com.simibubi.create.Create; import com.simibubi.create.CreateClient; import com.simibubi.create.content.logistics.trains.TrackGraph; -import com.simibubi.create.content.logistics.trains.entity.MovingPoint.ITrackSelector; +import com.simibubi.create.content.logistics.trains.TrackNode; +import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.content.logistics.trains.management.GlobalStation; +import com.simibubi.create.content.logistics.trains.management.GraphLocation; import com.simibubi.create.content.logistics.trains.management.ScheduleRuntime; +import com.simibubi.create.content.logistics.trains.management.ScheduleRuntime.State; import com.simibubi.create.foundation.utility.Lang; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; @@ -23,33 +39,42 @@ public class Train { public static final double acceleration = 0.005f; public static final double topSpeed = 1.2f; - + public double speed = 0; public double targetSpeed = 0; public UUID id; + public UUID owner; public TrackGraph graph; public Navigation navigation; - public GlobalStation currentStation; public ScheduleRuntime runtime; public TrainIconType icon; public Component name; + public TrainStatus status; + + public UUID currentStation; public boolean heldForAssembly; public boolean doubleEnded; public List carriages; public List carriageSpacing; + List migratingPoints; + public int migrationCooldown; + public boolean derailed; + double[] stress; - public Train(UUID id, TrackGraph graph, List carriages, List carriageSpacing) { + public Train(UUID id, UUID owner, TrackGraph graph, List carriages, List carriageSpacing) { this.id = id; + this.owner = owner; this.graph = graph; this.carriages = carriages; this.carriageSpacing = carriageSpacing; this.icon = TrainIconType.getDefault(); this.stress = new double[carriageSpacing.size()]; this.name = Lang.translate("train.unnamed"); + this.status = new TrainStatus(this); carriages.forEach(c -> { c.setTrain(this); @@ -60,12 +85,28 @@ public class Train { navigation = new Navigation(this, graph); runtime = new ScheduleRuntime(this); heldForAssembly = true; + migratingPoints = new ArrayList<>(); + currentStation = null; } public void tick(Level level) { + status.tick(level); + + if (graph == null) { + if (!migratingPoints.isEmpty()) + reattachToTracks(level); + return; + } + runtime.tick(level); navigation.tick(level); + if (navigation.destination == null && speed > 0) { + speed -= acceleration; + if (speed <= 0) + speed = 0; + } + double distance = speed; Carriage previousCarriage = null; @@ -88,27 +129,38 @@ public class Train { double leadingModifier = approachingStation ? -0.75d : -0.5d; double trailingModifier = approachingStation ? 0d : 0.125d; - MovingPoint previous = null; + TravellingPoint previous = null; + boolean blocked = false; + for (int i = 0; i < carriages.size(); i++) { double leadingStress = i == 0 ? 0 : stress[i - 1] * leadingModifier; double trailingStress = i == stress.length ? 0 : stress[i] * trailingModifier; Carriage carriage = carriages.get(i); - MovingPoint toFollow = previous; - Function control = + TravellingPoint toFollow = previous; + Function control = previous == null ? navigation::control : mp -> mp.follow(toFollow); - double actualDistance = carriage.travel(level, distance + leadingStress + trailingStress, control); + double actualDistance = carriage.travel(level, graph, distance + leadingStress + trailingStress, control); + blocked |= carriage.blocked; if (i == 0) distance = actualDistance; previous = carriage.getTrailingPoint(); } + if (blocked) { + speed = 0; + navigation.cancelNavigation(); + runtime.tick(level); + status.endOfTrack(); + } else if (speed > 0) + status.trackOK(); + if (navigation.destination != null) { - boolean recalculate = navigation.distanceToDestination > 20; + boolean recalculate = navigation.distanceToDestination % 100 > 20; navigation.distanceToDestination -= distance; - if (recalculate && navigation.distanceToDestination <= 20) - navigation.setDestination(navigation.destination); + if (recalculate && navigation.distanceToDestination % 100 <= 20) + navigation.startNavigation(navigation.destination, false); } } @@ -125,12 +177,12 @@ public class Train { int offset = 1; for (int i = 0; i < carriages.size(); i++) { - + Carriage carriage = carriages.get(i); CarriageContraptionEntity entity = carriage.entity.get(); if (entity == null) return false; - + entity.setPos(Vec3.atLowerCornerOf(pos.relative(assemblyDirection, offset))); entity.disassemble(); Create.RAILWAYS.carriageById.remove(carriage.id); @@ -141,14 +193,87 @@ public class Train { offset += carriageSpacing.get(i); } + GlobalStation currentStation = getCurrentStation(); if (currentStation != null) currentStation.cancelReservation(this); Create.RAILWAYS.trains.remove(id); - CreateClient.RAILWAYS.trains.remove(id); + CreateClient.RAILWAYS.trains.remove(id); // TODO Thread breach return true; } + public boolean isTravellingOn(TrackNode node) { + MutableBoolean affected = new MutableBoolean(false); + forEachTravellingPoint(tp -> { + if (tp.node1 == node || tp.node2 == node) + affected.setTrue(); + }); + return affected.booleanValue(); + } + + public void detachFromTracks() { + migratingPoints.clear(); + navigation.cancelNavigation(); + forEachTravellingPoint(tp -> migratingPoints.add(new TrainMigration(tp))); + graph = null; + + } + + private void forEachTravellingPoint(Consumer callback) { + for (Carriage c : carriages) { + c.leadingBogey().points.forEach(callback::accept); + if (c.isOnTwoBogeys()) + c.trailingBogey().points.forEach(callback::accept); + } + } + + public void reattachToTracks(Level level) { + if (migrationCooldown > 0) { + migrationCooldown--; + return; + } + + Set> entrySet = new HashSet<>(Create.RAILWAYS.trackNetworks.entrySet()); + Map> successfulMigrations = new HashMap<>(); + for (TrainMigration md : migratingPoints) { + for (Iterator> iterator = entrySet.iterator(); iterator.hasNext();) { + Entry entry = iterator.next(); + GraphLocation gl = md.tryMigratingTo(entry.getValue()); + if (gl == null) { + iterator.remove(); + continue; + } + successfulMigrations.computeIfAbsent(entry.getKey(), uuid -> new ArrayList<>()) + .add(gl); + } + } + + if (entrySet.isEmpty()) { + migrationCooldown = 40; + status.failedMigration(); + derailed = true; + return; + } + + for (Entry entry : entrySet) { + graph = entry.getValue(); + List locations = successfulMigrations.get(entry.getKey()); + forEachTravellingPoint(tp -> tp.migrateTo(locations)); + migratingPoints.clear(); + if (derailed) + status.successfulMigration(); + derailed = false; + if (runtime.getSchedule() != null) { + if (runtime.state == State.IN_TRANSIT) + runtime.state = State.PRE_TRANSIT; + } + GlobalStation currentStation = getCurrentStation(); + if (currentStation != null) + currentStation.reserveFor(this); + return; + } + } + public int getTotalLength() { int length = 0; for (int i = 0; i < carriages.size(); i++) { @@ -160,13 +285,38 @@ public class Train { } public void leave() { + GlobalStation currentStation = getCurrentStation(); + if (currentStation == null) + return; currentStation.trainDeparted(this); - currentStation = null; + this.currentStation = null; } public void arriveAt(GlobalStation station) { - currentStation = station; + setCurrentStation(station); runtime.destinationReached(); } + public void setCurrentStation(GlobalStation station) { + currentStation = station.id; + } + + public GlobalStation getCurrentStation() { + if (currentStation == null) + return null; + if (graph == null) + return null; + return graph.getStation(currentStation); + } + + @Nullable + public LivingEntity getOwner(Level level) { + try { + UUID uuid = owner; + return uuid == null ? null : level.getPlayerByUUID(uuid); + } catch (IllegalArgumentException illegalargumentexception) { + return null; + } + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java new file mode 100644 index 000000000..db9d84e0b --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java @@ -0,0 +1,96 @@ +package com.simibubi.create.content.logistics.trains.entity; + +import java.util.Map.Entry; + +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.GraphLocation; +import com.simibubi.create.foundation.utility.Couple; +import com.simibubi.create.foundation.utility.VecHelper; + +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; + +class TrainMigration { + Couple locations; + double positionOnOldEdge; + boolean curve; + Vec3 fallback; + + public TrainMigration(TravellingPoint point) { + double t = point.position / point.edge.getLength(point.node1, point.node2); + fallback = point.edge.getPosition(point.node1, point.node2, t); + curve = point.edge.isTurn(); + positionOnOldEdge = point.position; + locations = Couple.create(point.node1.getLocation(), point.node2.getLocation()); + } + + public GraphLocation tryMigratingTo(TrackGraph graph) { + TrackNode node1 = graph.locateNode(locations.getFirst()); + TrackNode node2 = graph.locateNode(locations.getSecond()); + if (node1 != null && node2 != null) { + TrackEdge edge = graph.getConnectionsFrom(node1) + .get(node2); + if (edge != null) { + GraphLocation graphLocation = new GraphLocation(); + graphLocation.graph = graph; + graphLocation.edge = locations; + graphLocation.position = positionOnOldEdge; + return graphLocation; + } + } + + if (curve) + return null; + + Vec3 prevDirection = locations.getSecond() + .getLocation() + .subtract(locations.getFirst() + .getLocation()) + .normalize(); + + for (TrackNodeLocation loc : graph.getNodes()) { + Vec3 nodeVec = loc.getLocation(); + if (nodeVec.distanceToSqr(fallback) > 32 * 32) + continue; + + TrackNode newNode1 = graph.locateNode(loc); + for (Entry entry : graph.getConnectionsFrom(newNode1) + .entrySet()) { + TrackEdge edge = entry.getValue(); + if (edge.isTurn()) + continue; + TrackNode newNode2 = entry.getKey(); + float radius = 1 / 64f; + Vec3 direction = edge.getDirection(newNode1, newNode2, true); + if (!Mth.equal(direction.dot(prevDirection), 1)) + continue; + Vec3 intersectSphere = VecHelper.intersectSphere(nodeVec, direction, fallback, radius); + if (intersectSphere == null) + continue; + if (!Mth.equal(direction.dot(intersectSphere.subtract(nodeVec) + .normalize()), 1)) + continue; + double edgeLength = edge.getLength(newNode1, newNode2); + double position = intersectSphere.distanceTo(nodeVec) - radius; + if (Double.isNaN(position)) + continue; + if (position < 0) + continue; + if (position > edgeLength) + continue; + + GraphLocation graphLocation = new GraphLocation(); + graphLocation.graph = graph; + graphLocation.edge = Couple.create(loc, newNode2.getLocation()); + graphLocation.position = position; + return graphLocation; + } + } + + return null; + } + +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainStatus.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainStatus.java new file mode 100644 index 000000000..7dd8d85fc --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainStatus.java @@ -0,0 +1,85 @@ +package com.simibubi.create.content.logistics.trains.entity; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; + +public class TrainStatus { + + Train train; + + boolean navigation; + boolean track; + + List queued = new ArrayList<>(); + + public TrainStatus(Train train) { + this.train = train; + } + + public void failedNavigation() { + if (navigation) + return; + displayInformation("Unable to find Path to next Scheduled destination", false); + navigation = true; + } + + public void successfulNavigation() { + if (!navigation) + return; + displayInformation("Navigation succeeded", true); + navigation = false; + } + + public void failedMigration() { + if (track) + return; + displayInformation("Tracks are missing beneath the Train", false); + track = true; + } + + public void endOfTrack() { + if (track) + return; + displayInformation("A Carriage has reached the end of its Track.", false); + track = true; + } + + public void successfulMigration() { + if (!track) + return; + displayInformation("Train is back on Track", true); + track = false; + } + + public void trackOK() { + track = false; + } + + public void tick(Level level) { + if (queued.isEmpty()) + return; + LivingEntity owner = train.getOwner(level); + if (owner == null) + return; + if (owner instanceof Player player) { + // TODO change to Lang.translate + player.displayClientMessage(new TextComponent(" Information about Train: ").append(train.name) + .withStyle(ChatFormatting.GOLD), false); + queued.forEach(c -> player.displayClientMessage(c, false)); + } + queued.clear(); + } + + public void displayInformation(String key, boolean itsAGoodThing, Object... args) { + queued.add(new TextComponent(" - ").withStyle(ChatFormatting.GRAY) + .append(new TextComponent(key).withStyle(st -> st.withColor(itsAGoodThing ? 0xD5ECC2 : 0xFFD3B4)))); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/MovingPoint.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java similarity index 80% rename from src/main/java/com/simibubi/create/content/logistics/trains/entity/MovingPoint.java rename to src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java index 3b78157fd..0411e88ba 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/MovingPoint.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java @@ -6,21 +6,22 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.Vector; -import java.util.function.Function; +import java.util.function.BiFunction; import com.simibubi.create.Create; import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackNode; +import com.simibubi.create.content.logistics.trains.management.GraphLocation; import net.minecraft.world.phys.Vec3; -public class MovingPoint { +public class TravellingPoint { - TrackGraph graph; TrackNode node1, node2; TrackEdge edge; double position; + boolean blocked; public static enum SteerDirection { NONE(0), LEFT(-1), RIGHT(1); @@ -33,11 +34,10 @@ public class MovingPoint { } public static interface ITrackSelector - extends Function>, Entry> { + extends BiFunction>, Entry> { }; - public MovingPoint(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge, double position) { - this.graph = graph; + public TravellingPoint(TrackNode node1, TrackNode node2, TrackEdge edge, double position) { this.node1 = node1; this.node2 = node2; this.edge = edge; @@ -45,11 +45,11 @@ public class MovingPoint { } public ITrackSelector random() { - return validTargets -> validTargets.get(Create.RANDOM.nextInt(validTargets.size())); + return (graph, validTargets) -> validTargets.get(Create.RANDOM.nextInt(validTargets.size())); } - public ITrackSelector follow(MovingPoint other) { - return validTargets -> { + public ITrackSelector follow(TravellingPoint other) { + return (graph, validTargets) -> { TrackNode target = other.node1; for (Entry entry : validTargets) @@ -99,7 +99,7 @@ public class MovingPoint { } public ITrackSelector steer(SteerDirection direction, Vec3 upNormal) { - return validTargets -> { + return (graph, validTargets) -> { double closest = Double.MAX_VALUE; Entry best = null; @@ -126,13 +126,13 @@ public class MovingPoint { }; } - public double travel(double distance, ITrackSelector trackSelector) { + public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector) { + blocked = false; double edgeLength = edge.getLength(node1, node2); if (distance == 0) return 0; double traveled = distance; - double currentT = position / edgeLength; double incrementT = edge.incrementT(node1, node2, currentT, distance); position = incrementT * edgeLength; @@ -159,11 +159,12 @@ public class MovingPoint { if (validTargets.isEmpty()) { traveled -= position - edgeLength; position = edgeLength; + blocked = true; break; } Entry entry = - validTargets.size() == 1 ? validTargets.get(0) : trackSelector.apply(validTargets); + validTargets.size() == 1 ? validTargets.get(0) : trackSelector.apply(graph, validTargets); node1 = node2; node2 = entry.getKey(); @@ -175,7 +176,7 @@ public class MovingPoint { return traveled; } - public void reverse() { + public void reverse(TrackGraph graph) { TrackNode n = node1; node1 = node2; node2 = n; @@ -191,4 +192,14 @@ public class MovingPoint { .scale(1)); } + public void migrateTo(List locations) { + GraphLocation location = locations.remove(0); + TrackGraph graph = location.graph; + node1 = graph.locateNode(location.edge.getFirst()); + node2 = graph.locateNode(location.edge.getSecond()); + position = location.position; + edge = graph.getConnectionsFrom(node1) + .get(node2); + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/GlobalStation.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/GlobalStation.java index aecd81690..b35be23f7 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/GlobalStation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/GlobalStation.java @@ -5,12 +5,9 @@ import java.util.UUID; import javax.annotation.Nullable; -import com.simibubi.create.Create; -import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.TrackNodeLocation; import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.foundation.utility.Couple; -import com.simibubi.create.foundation.utility.Debug; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; @@ -49,21 +46,18 @@ public class GlobalStation { public void migrate(LevelAccessor level) { BlockEntity blockEntity = level.getBlockEntity(stationPos); if (blockEntity instanceof StationTileEntity station) { - Debug.debugChat("Migrating Station " + name); station.migrate(this); return; } - Create.LOGGER - .warn("Couldn't migrate Station: " + name + " to changed Graph because associated Tile wasn't loaded."); } - public void setLocation(Couple nodes, double position) { - this.edgeLocation = nodes.map(TrackNode::getLocation); + public void setLocation(Couple nodes, double position) { + this.edgeLocation = nodes; this.position = position; } public void reserveFor(Train train) { - Train nearestTrain = this.nearestTrain.get(); + Train nearestTrain = getNearestTrain(); if (nearestTrain == null || nearestTrain.navigation.distanceToDestination > train.navigation.distanceToDestination) this.nearestTrain = new WeakReference<>(train); @@ -80,18 +74,18 @@ public class GlobalStation { @Nullable public Train getPresentTrain() { - Train nearestTrain = this.nearestTrain.get(); - if (nearestTrain == null || nearestTrain.currentStation != this) + Train nearestTrain = getNearestTrain(); + if (nearestTrain == null || nearestTrain.getCurrentStation() != this) return null; return nearestTrain; } @Nullable public Train getImminentTrain() { - Train nearestTrain = this.nearestTrain.get(); + Train nearestTrain = getNearestTrain(); if (nearestTrain == null) return nearestTrain; - if (nearestTrain.currentStation == this) + if (nearestTrain.getCurrentStation() == this) return nearestTrain; if (!nearestTrain.navigation.isActive()) return null; @@ -100,6 +94,11 @@ public class GlobalStation { return nearestTrain; } + @Nullable + public Train getNearestTrain() { + return this.nearestTrain.get(); + } + public CompoundTag write() { CompoundTag nbt = new CompoundTag(); nbt.putUUID("Id", id); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/GraphLocation.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/GraphLocation.java new file mode 100644 index 000000000..4a3c10592 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/GraphLocation.java @@ -0,0 +1,13 @@ +package com.simibubi.create.content.logistics.trains.management; + +import com.simibubi.create.content.logistics.trains.TrackGraph; +import com.simibubi.create.content.logistics.trains.TrackNodeLocation; +import com.simibubi.create.foundation.utility.Couple; + +public class GraphLocation { + + public TrackGraph graph; + public Couple edge; + public double position; + +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/ScheduleRuntime.java index c22e180da..d9a5b3f5e 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/ScheduleRuntime.java @@ -15,17 +15,17 @@ import net.minecraft.world.level.Level; public class ScheduleRuntime { - enum State { + public enum State { PRE_TRANSIT, IN_TRANSIT, POST_TRANSIT } Train train; Schedule schedule; - boolean paused; boolean isAutoSchedule; - int currentEntry; - State state; + public boolean paused; + public int currentEntry; + public State state; static final int INTERVAL = 40; int cooldown; @@ -51,6 +51,13 @@ public class ScheduleRuntime { } } + public void transitInterrupted() { + if (schedule == null || state != State.IN_TRANSIT) + return; + state = State.PRE_TRANSIT; + cooldown = 0; + } + public void tick(Level level) { if (schedule == null) return; @@ -76,17 +83,18 @@ public class ScheduleRuntime { GlobalStation nextStation = findNextStation(); if (nextStation == null) { + train.status.failedNavigation(); cooldown = INTERVAL; return; } - if (nextStation == train.currentStation) { + train.status.successfulNavigation(); + if (nextStation == train.getCurrentStation()) { state = State.IN_TRANSIT; destinationReached(); return; } - - train.navigation.setDestination(nextStation); - state = State.IN_TRANSIT; + if (train.navigation.startNavigation(nextStation, false) != -1) + state = State.IN_TRANSIT; } public void tickConditions(Level level) { @@ -113,12 +121,25 @@ public class ScheduleRuntime { public GlobalStation findNextStation() { ScheduleEntry entry = schedule.entries.get(currentEntry); ScheduleDestination destination = entry.destination; + if (destination instanceof FilteredDestination filtered) { + String regex = filtered.nameFilter.replace("*", ".*"); + GlobalStation best = null; + double bestCost = Double.MAX_VALUE; for (GlobalStation globalStation : train.graph.getStations()) { - if (globalStation.name.equals(filtered.nameFilter)) - return globalStation; + if (!globalStation.name.matches(regex)) + continue; + double cost = train.navigation.startNavigation(globalStation, true); + if (cost < 0) + continue; + if (cost > bestCost) + continue; + best = globalStation; + bestCost = cost; } + return best; } + return null; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationEditPacket.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationEditPacket.java index d2bef36e3..0c0bf4fed 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationEditPacket.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationEditPacket.java @@ -6,6 +6,7 @@ import com.simibubi.create.foundation.networking.TileEntityConfigurationPacket; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; @@ -66,7 +67,7 @@ public class StationEditPacket extends TileEntityConfigurationPacket { @Override public int getViewDistance() { - return 96; + return 96 * 2; } } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationScreen.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationScreen.java index 00459ea41..a4da7841f 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationScreen.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationScreen.java @@ -167,12 +167,12 @@ public class StationScreen extends AbstractStationScreen { return; } - if (train.navigation.destination != station && train.currentStation != station) { + if (train.navigation.destination != station && train.getCurrentStation() != station) { leavingAnimation = 80; return; } - disassembleTrainButton.active = train.currentStation == station; // TODO te.canAssemble + disassembleTrainButton.active = train.getCurrentStation() == station; // TODO te.canAssemble openScheduleButton.active = train.runtime.schedule != null; float f = (float) (train.navigation.distanceToDestination / 30f); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationTileEntity.java index 109ab810d..1e4befa9e 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/StationTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/StationTileEntity.java @@ -20,12 +20,10 @@ import com.simibubi.create.content.logistics.trains.TrackPropagator; import com.simibubi.create.content.logistics.trains.entity.Carriage; import com.simibubi.create.content.logistics.trains.entity.Carriage.CarriageBogey; import com.simibubi.create.content.logistics.trains.entity.CarriageContraption; -import com.simibubi.create.content.logistics.trains.entity.MovingPoint; import com.simibubi.create.content.logistics.trains.entity.Train; -import com.simibubi.create.content.logistics.trains.management.TrackTargetingBehaviour.GraphLocation; +import com.simibubi.create.content.logistics.trains.entity.TravellingPoint; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; -import com.simibubi.create.foundation.utility.Debug; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Pair; @@ -61,7 +59,7 @@ public class StationTileEntity extends SmartTileEntity { toMigrate = null; failedCarriageIndex = -1; } - + public void migrate(GlobalStation globalStation) { if (toMigrate != null) return; @@ -92,7 +90,7 @@ public class StationTileEntity extends SmartTileEntity { continue; return station; } - + if (level.isClientSide) return null; @@ -107,12 +105,9 @@ public class StationTileEntity extends SmartTileEntity { toMigrate != null ? new GlobalStation(toMigrate) : new GlobalStation(id, worldPosition); globalStation.setLocation(loc.edge, loc.position); loc.graph.addStation(globalStation); - - if (toMigrate != null) - Debug.debugChat("Migrated Station " + globalStation.name); toMigrate = null; setChanged(); - + return globalStation; } @@ -124,6 +119,7 @@ public class StationTileEntity extends SmartTileEntity { super.read(tag, clientPacket); if (tag.contains("ToMigrate")) toMigrate = tag.getCompound("ToMigrate"); + renderBounds = null; } @Override @@ -312,7 +308,7 @@ public class StationTileEntity extends SmartTileEntity { super.setRemovedNotDueToChunkUnload(); } - public void assemble() { + public void assemble(UUID playerUUID) { refreshAssemblyInfo(); if (bogeyLocations[0] != 0) { @@ -349,7 +345,7 @@ public class StationTileEntity extends SmartTileEntity { pointOffsets.add(Double.valueOf(loc + .5 + bogeySize / 2)); } - List points = new ArrayList<>(); + List points = new ArrayList<>(); Vec3 directionVec = Vec3.atLowerCornerOf(assemblyDirection.getNormal()); TrackGraph graph = null; TrackNode secondNode = null; @@ -402,7 +398,7 @@ public class StationTileEntity extends SmartTileEntity { return; } - points.add(new MovingPoint(graph, node, secondNode, edge, positionOnEdge)); + points.add(new TravellingPoint(node, secondNode, edge, positionOnEdge)); } secondNode = node; @@ -483,9 +479,9 @@ public class StationTileEntity extends SmartTileEntity { .forEach(Carriage::discardEntity); Create.RAILWAYS.carriageById.clear(); - Train train = new Train(UUID.randomUUID(), graph, carriages, spacing); + Train train = new Train(UUID.randomUUID(), playerUUID, graph, carriages, spacing); GlobalStation station = getOrCreateGlobalStation(); - train.currentStation = station; + train.setCurrentStation(station); station.reserveFor(train); Create.RAILWAYS.trains.put(train.id, train); @@ -518,7 +514,7 @@ public class StationTileEntity extends SmartTileEntity { if (isAssembling()) return INFINITE_EXTENT_AABB; if (renderBounds == null) - renderBounds = new AABB(worldPosition, getTarget().getGlobalPosition()); + renderBounds = new AABB(worldPosition, getTarget().getGlobalPosition()).inflate(2); return renderBounds; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/TrackTargetingBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/TrackTargetingBehaviour.java index 727933951..4f66ae428 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/TrackTargetingBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/TrackTargetingBehaviour.java @@ -38,7 +38,7 @@ public class TrackTargetingBehaviour extends TileEntityBehaviour { private BlockPos targetTrack; private AxisDirection targetDirection; - + public TrackTargetingBehaviour(SmartTileEntity te) { super(te); targetDirection = AxisDirection.POSITIVE; @@ -84,12 +84,6 @@ public class TrackTargetingBehaviour extends TileEntityBehaviour { return targetDirection; } - static class GraphLocation { - public TrackGraph graph; - public Couple edge; - public double position; - } - public GraphLocation determineGraphLocation() { Level level = getWorld(); BlockPos pos = getGlobalPosition(); @@ -152,7 +146,7 @@ public class TrackTargetingBehaviour extends TileEntityBehaviour { return null; GraphLocation graphLocation = new GraphLocation(); - graphLocation.edge = Couple.create(backNode, frontNode); + graphLocation.edge = Couple.create(backNode.getLocation(), frontNode.getLocation()); graphLocation.position = position; graphLocation.graph = graph; return graphLocation; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/FilteredDestination.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/FilteredDestination.java index 41e3c3101..36136c21e 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/FilteredDestination.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/FilteredDestination.java @@ -3,6 +3,8 @@ package com.simibubi.create.content.logistics.trains.management.schedule; import java.util.List; import java.util.function.BiConsumer; +import org.apache.commons.lang3.StringUtils; + import com.google.common.collect.ImmutableList; import com.simibubi.create.AllBlocks; import com.simibubi.create.Create; @@ -76,6 +78,7 @@ public class FilteredDestination extends ScheduleDestination { editBox.setBordered(false); editBox.setTextColor(0xFFFFFF); editBox.setValue(nameFilter); + editBox.setFilter(s -> StringUtils.countMatches(s, '*') <= 3); editBox.changeFocus(false); editBox.mouseClicked(0, 0, 0); editorSubWidgets diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationPoweredCondition.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationPoweredCondition.java index 795ba7d4a..806b0440d 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationPoweredCondition.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationPoweredCondition.java @@ -21,7 +21,7 @@ public class StationPoweredCondition extends ScheduleWaitCondition { @Override public boolean tickCompletion(Level level, Train train, CompoundTag context) { - GlobalStation currentStation = train.currentStation; + GlobalStation currentStation = train.getCurrentStation(); if (currentStation == null) return false; BlockPos stationPos = currentStation.stationPos; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationUnloadedCondition.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationUnloadedCondition.java index f93756075..b6da20b72 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationUnloadedCondition.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/StationUnloadedCondition.java @@ -21,7 +21,7 @@ public class StationUnloadedCondition extends ScheduleWaitCondition { @Override public boolean tickCompletion(Level level, Train train, CompoundTag context) { - GlobalStation currentStation = train.currentStation; + GlobalStation currentStation = train.getCurrentStation(); if (currentStation == null) return false; if (level instanceof ServerLevel serverLevel) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRenderer.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRenderer.java index 68a0b409d..aa921a315 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRenderer.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRenderer.java @@ -168,7 +168,7 @@ public class TrackRenderer extends SafeTileEntityRenderer { @Override public int getViewDistance() { - return 96; + return 96 * 2; } } diff --git a/src/main/java/com/simibubi/create/foundation/command/AllCommands.java b/src/main/java/com/simibubi/create/foundation/command/AllCommands.java index bf2989b51..ffcebb774 100644 --- a/src/main/java/com/simibubi/create/foundation/command/AllCommands.java +++ b/src/main/java/com/simibubi/create/foundation/command/AllCommands.java @@ -26,6 +26,7 @@ public class AllCommands { .then(new ToggleDebugCommand().register()) .then(FabulousWarningCommand.register()) .then(OverlayConfigCommand.register()) + .then(DumpRailwaysCommand.register()) .then(FixLightingCommand.register()) .then(HighlightCommand.register()) .then(CouplingCommand.register()) diff --git a/src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java b/src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java new file mode 100644 index 000000000..ee718af35 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java @@ -0,0 +1,118 @@ +package com.simibubi.create.foundation.command; + +import java.util.Map.Entry; +import java.util.UUID; +import java.util.function.BiConsumer; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.GlobalRailwayManager; +import com.simibubi.create.content.logistics.trains.TrackGraph; +import com.simibubi.create.content.logistics.trains.entity.Train; +import com.simibubi.create.content.logistics.trains.management.GlobalStation; +import com.simibubi.create.content.logistics.trains.management.ScheduleRuntime; +import com.simibubi.create.content.logistics.trains.management.ScheduleRuntime.State; + +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; + +public class DumpRailwaysCommand { + + static ArgumentBuilder register() { + return Commands.literal("dumpRailways") + .requires(cs -> cs.hasPermission(1)) + .executes(ctx -> { + CommandSourceStack source = ctx.getSource(); + fillReport(source.getLevel(), + (s, f) -> source.sendSuccess(new TextComponent(s).withStyle(st -> st.withColor(f)), false)); + return 1; + }); + } + + static void fillReport(ServerLevel level, BiConsumer chat) { + GlobalRailwayManager railways = Create.RAILWAYS; + int white = ChatFormatting.WHITE.getColor(); + int blue = 0xD3DEDC; + int darkBlue = 0x92A9BD; + int bright = 0xFFEFEF; + int orange = 0xFFAD60; + + for (int i = 0; i < 10; i++) + chat.accept("", white); + chat.accept("-+------<< Railways Summary: >>------+-", white); + chat.accept("Track Networks: " + railways.trackNetworks.size(), blue); + chat.accept("Trains: " + railways.trains.size(), blue); + chat.accept("", white); + + for (Entry entry : railways.trackNetworks.entrySet()) { + TrackGraph graph = entry.getValue(); + UUID id = entry.getKey(); + chat.accept(id.toString() + .substring(0, 5) + ": Track Graph, " + + graph.getNodes() + .size() + + " Nodes", graph.color.getRGB()); + for (GlobalStation globalStation : graph.getStations()) { + BlockPos pos = globalStation.stationPos; + chat.accept(" -> " + globalStation.name + " (" + globalStation.id.toString() + .substring(0, 5) + ") [" + pos.getX() + "," + pos.getY() + "," + pos.getZ() + "]", darkBlue); + if (globalStation.getPresentTrain() != null) { + chat.accept(" > Currently Occupied by " + globalStation.getPresentTrain().name.getString(), blue); + } else { + Train imminentTrain = globalStation.getImminentTrain(); + if (imminentTrain != null) { + chat.accept(" > Reserved by " + imminentTrain.name.getString() + " (" + + Mth.floor(imminentTrain.navigation.distanceToDestination) + "m away)", blue); + } + } + } + chat.accept("", white); + } + + for (Train train : railways.trains.values()) { + chat.accept(train.id.toString() + .substring(0, 5) + ": " + train.name.getString() + ", " + train.carriages.size() + " Wagons", bright); + if (train.graph != null) + chat.accept(" -> On Track: " + train.graph.id.toString() + .substring(0, 5), train.graph.color.getRGB()); + if (train.derailed) + chat.accept(" -> Derailed", orange); + LivingEntity owner = train.getOwner(level); + if (owner != null) + chat.accept(" > Owned by " + owner.getName() + .getString(), blue); + GlobalStation currentStation = train.getCurrentStation(); + if (currentStation != null) { + chat.accept(" > Waiting at: " + currentStation.name, blue); + } else if (train.navigation.destination != null) { + chat.accept(" > Travelling to " + train.navigation.destination.name + " (" + + Mth.floor(train.navigation.distanceToDestination) + "m away)", darkBlue); + } + ScheduleRuntime runtime = train.runtime; + if (runtime.getSchedule() != null) { + chat.accept(" > Schedule, Entry " + runtime.currentEntry + ", " + + (runtime.paused ? "Paused" + : runtime.state.name() + .replaceAll("_", " ")), + runtime.paused ? darkBlue : blue); + if (!runtime.paused && runtime.state != State.POST_TRANSIT) { + for (Component component : runtime.getSchedule().entries.get(runtime.currentEntry).destination + .getTitleAs("destination")) { + chat.accept(" - " + component.getString(), blue); + } + } + } else + chat.accept(" > Idle, No Schedule", darkBlue); + chat.accept("", white); + } + chat.accept("-+--------------------------------+-", white); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/networking/TileEntityConfigurationPacket.java b/src/main/java/com/simibubi/create/foundation/networking/TileEntityConfigurationPacket.java index e5648dd42..b841b852a 100644 --- a/src/main/java/com/simibubi/create/foundation/networking/TileEntityConfigurationPacket.java +++ b/src/main/java/com/simibubi/create/foundation/networking/TileEntityConfigurationPacket.java @@ -46,7 +46,7 @@ public abstract class TileEntityConfigurationPacket return; BlockEntity tileEntity = world.getBlockEntity(pos); if (tileEntity instanceof SyncedTileEntity) { - applySettings((TE) tileEntity); + applySettings(player, (TE) tileEntity); ((SyncedTileEntity) tileEntity).sendData(); tileEntity.setChanged(); } @@ -60,6 +60,10 @@ public abstract class TileEntityConfigurationPacket protected abstract void readSettings(FriendlyByteBuf buffer); + protected void applySettings(ServerPlayer player, TE te) { + applySettings(te); + } + protected abstract void applySettings(TE te); }