Extended train station peripheral API

- Train station peripherals can now assemble and disassemble trains, check if the station is in assembly mode, set the assembly mode of the station, get and change the station name, check if a train is present at the station and get and change the currently present train name.
- Refactored StationEditPacket. Moved most of the logic that was previously in StationEditPacket to StationTileEntity. This allows us to call this logic without having to send a packet.
- Made Train#owner nullable. This is needed so that computers can assemble trains. All Train#owner is currently used for is to display the train status to the correct play.
This commit is contained in:
caelwarner 2023-03-08 18:22:23 -08:00
parent 574cd93a89
commit 31ad3aa671
Failed to generate hash of commit
5 changed files with 231 additions and 108 deletions

View file

@ -4,24 +4,27 @@ import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.compat.computercraft.CreateLuaTable;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationTileEntity;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.TrainEditPacket;
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleEntry;
import com.simibubi.create.content.logistics.trains.management.schedule.condition.ScheduleWaitCondition;
import com.simibubi.create.content.logistics.trains.management.schedule.destination.ScheduleInstruction;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.StringHelper;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import net.minecraftforge.network.PacketDistributor;
public class StationPeripheral extends SyncedPeripheral<StationTileEntity> {
@ -29,6 +32,101 @@ public class StationPeripheral extends SyncedPeripheral<StationTileEntity> {
super(tile);
}
@LuaFunction(mainThread = true)
public final void assemble() throws LuaException {
if (!tile.isAssembling())
throw new LuaException("station must be in assembly mode");
tile.assemble(null);
if (tile.getStation() == null || tile.getStation().getPresentTrain() == null)
throw new LuaException("failed to assemble train");
if (!tile.exitAssemblyMode())
throw new LuaException("failed to exit assembly mode");
}
@LuaFunction(mainThread = true)
public final void disassemble() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("train station does not exist");
Train train = station.getPresentTrain();
if (train == null)
throw new LuaException("there is no train present");
if (!tile.enterAssemblyMode(null))
throw new LuaException("could not disassemble train");
}
@LuaFunction(mainThread = true)
public final void setAssemblyMode(boolean assemblyMode) throws LuaException {
if (assemblyMode) {
if (!tile.enterAssemblyMode(null))
throw new LuaException("failed to enter assembly mode");
} else {
if (!tile.exitAssemblyMode())
throw new LuaException("failed to exit assembly mode");
}
}
@LuaFunction
public final boolean inAssemblyMode() {
return tile.isAssembling();
}
@LuaFunction
public final String getStationName() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("train station does not exist");
return station.name;
}
@LuaFunction(mainThread = true)
public final void setStationName(String name) throws LuaException {
if (!tile.updateName(name))
throw new LuaException("could not set station name");
}
@LuaFunction
public final boolean isTrainPresent() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("train station does not exist");
return station.getPresentTrain() != null;
}
@LuaFunction
public final String getTrainName() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("train station does not exist");
Train train = station.getPresentTrain();
if (train == null)
throw new LuaException("there is no train present");
return train.name.getString();
}
@LuaFunction
public final void setTrainName(String name) throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("train station does not exist");
Train train = station.getPresentTrain();
if (train == null)
throw new LuaException("there is no train present");
train.name = Components.literal(name);
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainEditPacket.TrainEditReturnPacket(train.id, name, train.icon.getId()));
}
@LuaFunction(mainThread = true)
public final void setSchedule(IArguments arguments) throws LuaException {
GlobalStation station = tile.getStation();

View file

@ -15,8 +15,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableObject;
@ -51,7 +49,7 @@ import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
@ -67,6 +65,7 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Explosion.BlockInteraction;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
@ -86,6 +85,7 @@ public class Train {
public boolean honk = false;
public UUID id;
@Nullable
public UUID owner;
public TrackGraph graph;
public Navigation navigation;
@ -124,7 +124,7 @@ public class Train {
public int honkPitch;
public float accumulatedSteamRelease;
int tickOffset;
double[] stress;
@ -277,7 +277,7 @@ public class Train {
int carriageCount = carriages.size();
boolean stalled = false;
double maxStress = 0;
if (carriageWaitingForChunks != -1)
distance = 0;
@ -317,7 +317,7 @@ public class Train {
entries++;
}
}
if (entries > 0)
actual = total / entries;
@ -369,7 +369,7 @@ public class Train {
.getLeadingPoint();
double totalStress = derailed ? 0 : leadingStress + trailingStress;
boolean first = i == 0;
boolean last = i == carriageCount - 1;
int carriageType = first ? last ? Carriage.BOTH : Carriage.FIRST : last ? Carriage.LAST : Carriage.MIDDLE;
@ -1087,7 +1087,8 @@ public class Train {
public CompoundTag write(DimensionPalette dimensions) {
CompoundTag tag = new CompoundTag();
tag.putUUID("Id", id);
tag.putUUID("Owner", owner);
if (owner != null)
tag.putUUID("Owner", owner);
if (graph != null)
tag.putUUID("Graph", graph.id);
tag.put("Carriages", NBTHelper.writeCompoundList(carriages, c -> c.write(dimensions)));
@ -1133,7 +1134,7 @@ public class Train {
public static Train read(CompoundTag tag, Map<UUID, TrackGraph> trackNetworks, DimensionPalette dimensions) {
UUID id = tag.getUUID("Id");
UUID owner = tag.getUUID("Owner");
UUID owner = tag.contains("Owner") ? tag.getUUID("Owner") : null;
UUID graphId = tag.contains("Graph") ? tag.getUUID("Graph") : null;
TrackGraph graph = graphId == null ? null : trackNetworks.get(graphId);
List<Carriage> carriages = new ArrayList<>();

View file

@ -12,7 +12,6 @@ import com.simibubi.create.foundation.networking.SimplePacketBase;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.RegisteredObjects;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.world.level.block.Block;
@ -37,7 +36,10 @@ public class TrainPacket extends SimplePacketBase {
if (!add)
return;
UUID owner = buffer.readUUID();
UUID owner = null;
if (buffer.readBoolean())
owner = buffer.readUUID();
List<Carriage> carriages = new ArrayList<>();
List<Integer> carriageSpacing = new ArrayList<>();
@ -73,7 +75,9 @@ public class TrainPacket extends SimplePacketBase {
if (!add)
return;
buffer.writeUUID(train.owner);
buffer.writeBoolean(train.owner != null);
if (train.owner != null)
buffer.writeUUID(train.owner);
buffer.writeVarInt(train.carriages.size());
for (Carriage carriage : train.carriages) {

View file

@ -1,19 +1,11 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint.station;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.GraphLocation;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.foundation.networking.TileEntityConfigurationPacket;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
public class StationEditPacket extends TileEntityConfigurationPacket<StationTileEntity> {
@ -92,18 +84,12 @@ public class StationEditPacket extends TileEntityConfigurationPacket<StationTile
BlockState blockState = level.getBlockState(blockPos);
if (dropSchedule) {
scheduleDropRequested(player, te);
te.dropSchedule(player);
return;
}
if (!name.isBlank()) {
GlobalStation station = te.getStation();
GraphLocation graphLocation = te.edgePoint.determineGraphLocation();
if (station != null && graphLocation != null) {
station.name = name;
Create.RAILWAYS.sync.pointAdded(graphLocation.graph, station);
Create.RAILWAYS.markTracksDirty();
}
te.updateName(name);
}
if (!(blockState.getBlock() instanceof StationBlock))
@ -120,89 +106,17 @@ public class StationEditPacket extends TileEntityConfigurationPacket<StationTile
assemblyComplete = te.getStation() != null && te.getStation()
.getPresentTrain() != null;
} else {
if (disassembleAndEnterMode(player, te))
if (te.tryDisassembleTrain(player) && te.tryEnterAssemblyMode())
te.refreshAssemblyInfo();
}
if (!assemblyComplete)
return;
}
if (isAssemblyMode == assemblyMode)
return;
BlockState newState = blockState.cycle(StationBlock.ASSEMBLING);
Boolean nowAssembling = newState.getValue(StationBlock.ASSEMBLING);
if (nowAssembling) {
if (!disassembleAndEnterMode(player, te))
return;
} else {
te.cancelAssembly();
}
level.setBlock(blockPos, newState, 3);
te.refreshBlockState();
if (nowAssembling)
te.refreshAssemblyInfo();
GlobalStation station = te.getStation();
GraphLocation graphLocation = te.edgePoint.determineGraphLocation();
if (station != null && graphLocation != null) {
station.assembling = nowAssembling;
Create.RAILWAYS.sync.pointAdded(graphLocation.graph, station);
Create.RAILWAYS.markTracksDirty();
if (nowAssembling)
for (Train train : Create.RAILWAYS.sided(level).trains.values()) {
if (train.navigation.destination != station)
continue;
GlobalStation preferredDestination = train.runtime.startCurrentInstruction();
if (preferredDestination != null)
train.navigation.startNavigation(preferredDestination, Double.MAX_VALUE, false);
else
train.navigation.startNavigation(station, Double.MAX_VALUE, false);
}
}
}
private void scheduleDropRequested(ServerPlayer sender, StationTileEntity te) {
GlobalStation station = te.getStation();
if (station == null)
return;
Train train = station.getPresentTrain();
if (train == null)
return;
ItemStack schedule = train.runtime.returnSchedule();
dropSchedule(sender, te, schedule);
}
private boolean disassembleAndEnterMode(ServerPlayer sender, StationTileEntity te) {
GlobalStation station = te.getStation();
if (station != null) {
Train train = station.getPresentTrain();
BlockPos trackPosition = te.edgePoint.getGlobalPosition();
ItemStack schedule = train == null ? ItemStack.EMPTY : train.runtime.returnSchedule();
if (train != null && !train.disassemble(te.getAssemblyDirection(), trackPosition.above()))
return false;
dropSchedule(sender, te, schedule);
}
return te.tryEnterAssemblyMode();
}
private void dropSchedule(ServerPlayer sender, StationTileEntity te, ItemStack schedule) {
if (schedule.isEmpty())
return;
if (sender.getMainHandItem()
.isEmpty()) {
sender.getInventory()
.placeItemBackInInventory(schedule);
return;
}
Vec3 v = VecHelper.getCenterOf(te.getBlockPos());
ItemEntity itemEntity = new ItemEntity(te.getLevel(), v.x, v.y, v.z, schedule);
itemEntity.setDeltaMovement(Vec3.ZERO);
te.getLevel()
.addFreshEntity(itemEntity);
if (assemblyMode)
te.enterAssemblyMode(player);
else
te.exitAssemblyMode();
}
@Override

View file

@ -9,6 +9,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Consumer;
import javax.annotation.Nullable;
@ -25,6 +26,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.ITr
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.logistics.block.depot.DepotBehaviour;
import com.simibubi.create.content.logistics.block.display.DisplayLinkBlock;
import com.simibubi.create.content.logistics.trains.GraphLocation;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.TrackEdge;
@ -51,6 +53,7 @@ import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.WorldAttached;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
@ -64,9 +67,11 @@ import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.SoundType;
@ -320,6 +325,63 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
return true;
}
public boolean enterAssemblyMode(@Nullable ServerPlayer sender) {
if (isAssembling())
return false;
tryDisassembleTrain(sender);
if (!tryEnterAssemblyMode())
return false;
BlockState newState = getBlockState().setValue(StationBlock.ASSEMBLING, true);
level.setBlock(getBlockPos(), newState, 3);
refreshBlockState();
refreshAssemblyInfo();
updateStationState(station -> station.assembling = true);
GlobalStation station = getStation();
if (station != null) {
for (Train train : Create.RAILWAYS.sided(level).trains.values()) {
if (train.navigation.destination != station)
continue;
GlobalStation preferredDestination = train.runtime.startCurrentInstruction();
train.navigation.startNavigation(preferredDestination != null ? preferredDestination : station, Double.MAX_VALUE, false);
}
}
return true;
}
public boolean exitAssemblyMode() {
if (!isAssembling())
return false;
cancelAssembly();
BlockState newState = getBlockState().setValue(StationBlock.ASSEMBLING, false);
level.setBlock(getBlockPos(), newState, 3);
refreshBlockState();
return updateStationState(station -> station.assembling = false);
}
public boolean tryDisassembleTrain(@Nullable ServerPlayer sender) {
GlobalStation station = getStation();
if (station == null)
return false;
Train train = station.getPresentTrain();
if (train == null)
return false;
BlockPos trackPosition = edgePoint.getGlobalPosition();
if (!train.disassemble(getAssemblyDirection(), trackPosition.above()))
return false;
dropSchedule(sender);
return true;
}
public boolean isAssembling() {
BlockState state = getBlockState();
return state.hasProperty(StationBlock.ASSEMBLING) && state.getValue(StationBlock.ASSEMBLING);
@ -347,6 +409,42 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
return true;
}
public void dropSchedule(@Nullable ServerPlayer sender) {
GlobalStation station = getStation();
if (station == null)
return;
Train train = station.getPresentTrain();
if (train == null)
return;
ItemStack schedule = train.runtime.returnSchedule();
if (schedule.isEmpty())
return;
if (sender != null && sender.getMainHandItem().isEmpty()) {
sender.getInventory()
.placeItemBackInInventory(schedule);
return;
}
Vec3 v = VecHelper.getCenterOf(getBlockPos());
ItemEntity itemEntity = new ItemEntity(getLevel(), v.x, v.y, v.z, schedule);
itemEntity.setDeltaMovement(Vec3.ZERO);
getLevel().addFreshEntity(itemEntity);
}
private boolean updateStationState(Consumer<GlobalStation> updateState) {
GlobalStation station = getStation();
GraphLocation graphLocation = edgePoint.determineGraphLocation();
if (station == null || graphLocation == null)
return false;
updateState.accept(station);
Create.RAILWAYS.sync.pointAdded(graphLocation.graph, station);
Create.RAILWAYS.markTracksDirty();
return true;
}
public void refreshAssemblyInfo() {
if (!edgePoint.hasValidTrack())
return;
@ -415,6 +513,14 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
map.put(worldPosition, BoundingBox.fromCorners(startPosition, trackEnd));
}
public boolean updateName(String name) {
if (!updateStationState(station -> station.name = name))
return false;
notifyUpdate();
return true;
}
public boolean isValidBogeyOffset(int i) {
if ((i < 3 || bogeyCount == 0) && i != 0)
return false;