Train Admin

- Clean up /dumpRailways
- Trains can now be removed via a command
This commit is contained in:
simibubi 2022-06-29 21:02:50 +02:00
parent 3806ab04d7
commit 1d1cf8e289
7 changed files with 184 additions and 89 deletions

View file

@ -223,13 +223,14 @@ public class GlobalRailwayManager {
for (Iterator<Train> 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<Train> 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() {

View file

@ -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);
}

View file

@ -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();

View file

@ -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<Boolean, Integer> 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;
}
}

View file

@ -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())

View file

@ -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<CommandSourceStack, ?> 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<String, Integer> chat) {
static void fillReport(ServerLevel level, Vec3 location, BiConsumer<String, Integer> chat,
Consumer<Component> 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<UUID, TrackGraph> 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<SignalBoundary> 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<TrackGraph> 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<SignalBoundary> signals = graph.getPoints(EdgePointType.SIGNAL);
if (!signals.isEmpty())
chat.accept(" -> " + signals.size() + " Signals", blue);
Collection<GlobalStation> 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<Train> 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());
}));
}
}

View file

@ -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<CommandSourceStack, ?> 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);
}
}