diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java b/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java index 6930a4f80..994611bf4 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java @@ -223,13 +223,14 @@ public class GlobalRailwayManager { for (Iterator iterator = waitingTrains.iterator(); iterator.hasNext();) { Train train = iterator.next(); - + if (train.invalid) { iterator.remove(); trains.remove(train.id); + AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainPacket(train, false)); continue; } - + if (train.navigation.waitingForSignal != null) continue; movingTrains.add(train); @@ -238,20 +239,20 @@ public class GlobalRailwayManager { for (Iterator iterator = movingTrains.iterator(); iterator.hasNext();) { Train train = iterator.next(); - + if (train.invalid) { iterator.remove(); trains.remove(train.id); + AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainPacket(train, false)); continue; } - + if (train.navigation.waitingForSignal == null) continue; waitingTrains.add(train); iterator.remove(); } - - + } public void tickSignalOverlay() { 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 3a6b4fb95..897713e94 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 @@ -9,6 +9,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.UUID; @@ -465,6 +466,17 @@ public class TrackGraph { return connections.put(node2, edge) == null; } + public float distanceToLocationSqr(Level level, Vec3 location) { + float nearest = Float.MAX_VALUE; + for (TrackNodeLocation tnl : nodes.keySet()) { + if (!Objects.equals(tnl.dimension, level.dimension())) + continue; + nearest = Math.min(nearest, (float) tnl.getLocation() + .distanceToSqr(location)); + } + return nearest; + } + public void deferIntersectionUpdate(TrackEdge edge) { deferredIntersectionUpdates.add(edge); } 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 484771ba6..8886ab3b1 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 @@ -157,7 +157,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { } public boolean isLocalCoordWithin(BlockPos localPos, int min, int max) { - if (!(getContraption() instanceof CarriageContraption cc)) + if (!(getContraption()instanceof CarriageContraption cc)) return false; Direction facing = cc.getAssemblyDirection(); Axis axis = facing.getClockWise() @@ -266,6 +266,10 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { carriageData.approach(this, carriage, 1f / getType().updateInterval()); + if (!Create.RAILWAYS.sided(null).trains.containsKey(carriage.train.id)) { + discard(); + return; + } if (!carriage.train.derailed) carriage.updateContraptionAnchors(); 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 223006626..ed91b5e55 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 @@ -413,7 +413,7 @@ public class Train { public IEdgePointListener frontSignalListener() { return (distance, couple) -> { - if (couple.getFirst() instanceof GlobalStation station) { + if (couple.getFirst()instanceof GlobalStation station) { if (!station.canApproachFrom(couple.getSecond() .getSecond()) || navigation.destination != station) return false; @@ -425,12 +425,12 @@ public class Train { return true; } - if (couple.getFirst() instanceof TrackObserver observer) { + if (couple.getFirst()instanceof TrackObserver observer) { occupiedObservers.add(observer.getId()); return false; } - if (!(couple.getFirst() instanceof SignalBoundary signal)) + if (!(couple.getFirst()instanceof SignalBoundary signal)) return false; if (navigation.waitingForSignal != null && navigation.waitingForSignal.getFirst() .equals(signal.getId())) { @@ -480,12 +480,12 @@ public class Train { public IEdgePointListener backSignalListener() { return (distance, couple) -> { - if (couple.getFirst() instanceof TrackObserver observer) { + if (couple.getFirst()instanceof TrackObserver observer) { occupiedObservers.remove(observer.getId()); cachedObserverFiltering.remove(observer.getId()); return false; } - if (!(couple.getFirst() instanceof SignalBoundary signal)) + if (!(couple.getFirst()instanceof SignalBoundary signal)) return false; UUID groupId = signal.getGroup(couple.getSecond() .getFirst()); @@ -716,7 +716,7 @@ public class Train { return false; level = entity.level; - if (entity.getContraption() instanceof CarriageContraption cc) + if (entity.getContraption()instanceof CarriageContraption cc) cc.returnStorageForDisassembly(carriage.storage); entity.setPos(Vec3 .atLowerCornerOf(pos.relative(assemblyDirection, backwards ? offset + carriage.bogeySpacing : offset))); @@ -732,7 +732,7 @@ public class Train { if (currentStation != null) { currentStation.cancelReservation(this); BlockPos tilePos = currentStation.getTilePos(); - if (level.getBlockEntity(tilePos) instanceof StationTileEntity ste) + if (level.getBlockEntity(tilePos)instanceof StationTileEntity ste) ste.lastDisassembledTrainName = name.copy(); } @@ -982,11 +982,11 @@ public class Train { forEachTravellingPointBackwards((tp, d) -> { signalScout.travel(graph, d, signalScout.follow(tp), (distance, couple) -> { - if (couple.getFirst() instanceof TrackObserver observer) { + if (couple.getFirst()instanceof TrackObserver observer) { occupiedObservers.add(observer.getId()); return false; } - if (!(couple.getFirst() instanceof SignalBoundary signal)) + if (!(couple.getFirst()instanceof SignalBoundary signal)) return false; couple.getSecond() .map(signal::getGroup) @@ -1194,7 +1194,7 @@ public class Train { if (dimensional == null) return; CarriageContraptionEntity entity = dimensional.entity.get(); - if (entity == null || !(entity.getContraption() instanceof CarriageContraption otherCC)) + if (entity == null || !(entity.getContraption()instanceof CarriageContraption otherCC)) break; Pair first = otherCC.soundQueue.getFirstWhistle(entity); if (first != null) { @@ -1204,4 +1204,15 @@ public class Train { } } + public float distanceToLocationSqr(Level level, Vec3 location) { + float distance = Float.MAX_VALUE; + for (Carriage carriage : carriages) { + DimensionalCarriageEntity dce = carriage.getDimensionalIfPresent(level.dimension()); + if (dce == null || dce.positionAnchor == null) + continue; + distance = Math.min(distance, (float) dce.positionAnchor.distanceToSqr(location)); + } + return distance; + } + } 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 bb46cca08..70e4c87a9 100644 --- a/src/main/java/com/simibubi/create/foundation/command/AllCommands.java +++ b/src/main/java/com/simibubi/create/foundation/command/AllCommands.java @@ -29,6 +29,7 @@ public class AllCommands { .then(DumpRailwaysCommand.register()) .then(FixLightingCommand.register()) .then(HighlightCommand.register()) + .then(KillTrainCommand.register()) .then(CouplingCommand.register()) .then(ConfigCommand.register()) .then(PonderCommand.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 index 2587a1ea4..d51655d52 100644 --- a/src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java +++ b/src/main/java/com/simibubi/create/foundation/command/DumpRailwaysCommand.java @@ -1,9 +1,9 @@ package com.simibubi.create.foundation.command; import java.util.Collection; -import java.util.Map.Entry; -import java.util.UUID; +import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; import com.mojang.brigadier.builder.ArgumentBuilder; import com.simibubi.create.Create; @@ -14,32 +14,36 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePoi import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation; import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime; -import com.simibubi.create.content.logistics.trains.management.schedule.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.ClickEvent; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentUtils; +import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.TextComponent; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.phys.Vec3; public class DumpRailwaysCommand { static ArgumentBuilder register() { return Commands.literal("dumpRailways") - .requires(cs -> cs.hasPermission(1)) + .requires(cs -> cs.hasPermission(2)) .executes(ctx -> { CommandSourceStack source = ctx.getSource(); - fillReport(source.getLevel(), - (s, f) -> source.sendSuccess(new TextComponent(s).withStyle(st -> st.withColor(f)), false)); + fillReport(source.getLevel(), source.getPosition(), + (s, f) -> source.sendSuccess(new TextComponent(s).withStyle(st -> st.withColor(f)), false), + (c) -> source.sendSuccess(c, false)); return 1; }); } - static void fillReport(ServerLevel level, BiConsumer chat) { + static void fillReport(ServerLevel level, Vec3 location, BiConsumer chat, + Consumer chatRaw) { GlobalRailwayManager railways = Create.RAILWAYS; int white = ChatFormatting.WHITE.getColor(); int blue = 0xD3DEDC; @@ -47,81 +51,103 @@ public class DumpRailwaysCommand { int bright = 0xFFEFEF; int orange = 0xFFAD60; - for (int i = 0; i < 10; i++) - chat.accept("", white); + chat.accept("", white); chat.accept("-+------<< Railways Summary: >>------+-", white); - chat.accept("Track Networks: " + railways.trackNetworks.size(), blue); + int graphCount = railways.trackNetworks.size(); + chat.accept("Track Networks: " + graphCount, blue); chat.accept("Signal Groups: " + railways.signalEdgeGroups.size(), blue); - chat.accept("Trains: " + railways.trains.size(), blue); + int trainCount = railways.trains.size(); + chat.accept("Trains: " + trainCount, 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()); - Collection signals = graph.getPoints(EdgePointType.SIGNAL); - if (!signals.isEmpty()) - chat.accept(" -> " + signals.size() + " registered Signals", blue); - for (GlobalStation globalStation : graph.getPoints(EdgePointType.STATION)) { - BlockPos pos = globalStation.getTilePos(); - if (pos == null) - pos = BlockPos.ZERO; - chat.accept(" -> " + globalStation.name + " (" + globalStation.id.toString() - .substring(0, 5) + ") [" + pos.getX() + "," + pos.getY() + "," + pos.getZ() + "]", darkBlue); - if (globalStation.getPresentTrain() != null) { - 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); - } - } + List nearest = railways.trackNetworks.values() + .stream() + .sorted((tg1, tg2) -> Float.compare(tg1.distanceToLocationSqr(level, location), + tg2.distanceToLocationSqr(level, location))) + .limit(5) + .toList(); + + if (graphCount > 0) { + chat.accept("Nearest Graphs: ", orange); + chat.accept("", white); + for (TrackGraph graph : nearest) { + chat.accept(graph.id.toString() + .substring(0, 5) + " with " + + graph.getNodes() + .size() + + " Nodes", white); + Collection signals = graph.getPoints(EdgePointType.SIGNAL); + if (!signals.isEmpty()) + chat.accept(" -> " + signals.size() + " Signals", blue); + Collection stations = graph.getPoints(EdgePointType.STATION); + if (!stations.isEmpty()) + chat.accept(" -> " + stations.size() + " Stations", blue); } chat.accept("", white); + if (graphCount > 5) { + chat.accept("[...]", white); + 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).instruction - .getTitleAs("destination")) { - chat.accept(" - " + component.getString(), blue); - } - } - } else - chat.accept(" > Idle, No Schedule", darkBlue); + List nearestTrains = railways.trains.values() + .stream() + .sorted((t1, t2) -> Float.compare(t1.distanceToLocationSqr(level, location), + t2.distanceToLocationSqr(level, location))) + .limit(5) + .toList(); + + if (trainCount > 0 && !nearestTrains.isEmpty()) { + chat.accept("Nearest Trains: ", orange); chat.accept("", white); + for (Train train : nearestTrains) { + chat.accept(train.id.toString() + .substring(0, 5) + ": " + train.name.getString() + ", " + train.carriages.size() + " Wagons", + bright); + if (train.derailed) + chat.accept(" -> Derailed", orange); + else if (train.graph != null) + chat.accept(" -> On Track: " + train.graph.id.toString() + .substring(0, 5), blue); + 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); + } else + chat.accept(" -> Idle, No Schedule", darkBlue); + chatRaw.accept(createDeleteButton(train)); + chat.accept("", white); + } + if (trainCount > 5) { + chat.accept("[...]", white); + chat.accept("", white); + } } + chat.accept("-+--------------------------------+-", white); } + private static Component createDeleteButton(Train train) { + return ComponentUtils.wrapInSquareBrackets((new TextComponent("Remove")).withStyle((p_180514_) -> { + return p_180514_.withColor(0xFFAD60) + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/c killTrain " + train.id.toString())) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new TextComponent("Click to remove ").append(train.name))) + .withInsertion("/c killTrain " + train.id.toString()); + })); + } + } diff --git a/src/main/java/com/simibubi/create/foundation/command/KillTrainCommand.java b/src/main/java/com/simibubi/create/foundation/command/KillTrainCommand.java new file mode 100644 index 000000000..c2194446c --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/command/KillTrainCommand.java @@ -0,0 +1,40 @@ +package com.simibubi.create.foundation.command; + +import java.util.UUID; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.entity.Train; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.UuidArgument; +import net.minecraft.network.chat.TextComponent; + +public class KillTrainCommand { + + static ArgumentBuilder register() { + return Commands.literal("killTrain") + .requires(cs -> cs.hasPermission(2)) + .then(Commands.argument("train", UuidArgument.uuid()) + .executes(ctx -> { + CommandSourceStack source = ctx.getSource(); + run(source, UuidArgument.getUuid(ctx, "train")); + return 1; + })); + } + + private static void run(CommandSourceStack source, UUID argument) { + Train train = Create.RAILWAYS.trains.get(argument); + if (train == null) { + source.sendFailure(new TextComponent("No Train with id " + argument.toString() + .substring(0, 5) + "[...] was found")); + return; + } + + train.invalid = true; + source.sendSuccess(new TextComponent("Train '").append(train.name) + .append("' removed successfully"), true); + } + +}