Added Train Station as peripheral

- Train station can set a new auto-schedule for the train currently at the station
- Added CreateLuaTable to add helper functions for working with lua tables
- Added StringHelper util class to convert snake case to camel case
This commit is contained in:
caelwarner 2022-10-19 15:58:56 -07:00
parent 1091f3227c
commit ab18034b98
No known key found for this signature in database
GPG Key ID: 514BEF5EADE889FF
6 changed files with 297 additions and 13 deletions

View File

@ -0,0 +1,89 @@
package com.simibubi.create.compat.computercraft;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaValues;
import dan200.computercraft.api.lua.ObjectLuaTable;
public class CreateLuaTable extends ObjectLuaTable {
public CreateLuaTable(Map<?, ?> map) {
super(map);
}
public boolean getBoolean(String key) throws LuaException {
Object value = get(key);
if (!(value instanceof Boolean))
throw LuaValues.badField(key, "boolean", LuaValues.getType(value));
return (Boolean) value;
}
public String getString(String key) throws LuaException {
Object value = get(key);
if (!(value instanceof String))
throw LuaValues.badField(key, "string", LuaValues.getType(value));
return (String) value;
}
public CreateLuaTable getTable(String key) throws LuaException {
Object value = get(key);
if (!(value instanceof Map<?, ?>))
throw LuaValues.badField(key, "table", LuaValues.getType(value));
return new CreateLuaTable((Map<?, ?>) value);
}
public Optional<Boolean> getOptBoolean(String key) throws LuaException {
Object value = get(key);
if (value == null)
return Optional.empty();
if (!(value instanceof Boolean))
throw LuaValues.badField(key, "boolean", LuaValues.getType(value));
return Optional.of((Boolean) value);
}
public Set<String> stringKeySet() throws LuaException {
Set<String> stringSet = new HashSet<>();
for (Object key : keySet()) {
if (!(key instanceof String))
throw new LuaException("key " + key + " is not string (got " + LuaValues.getType(key) + ")");
stringSet.add((String) key);
}
return Collections.unmodifiableSet(stringSet);
}
public Collection<CreateLuaTable> tableValues() throws LuaException {
List<CreateLuaTable> tables = new ArrayList<>();
for (int i = 1; i <= size(); i++) {
Object value = get((double) i);
if (!(value instanceof Map<?, ?>))
throw new LuaException("value " + value + " is not table (got " + LuaValues.getType(value) + ")");
tables.add(new CreateLuaTable((Map<?, ?>) value));
}
return Collections.unmodifiableList(tables);
}
}

View File

@ -0,0 +1,130 @@
package com.simibubi.create.compat.computercraft;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.jetbrains.annotations.NotNull;
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.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.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;
public class StationPeripheral extends PeripheralBase<StationTileEntity> {
public StationPeripheral(StationTileEntity tile) {
super(tile);
}
@LuaFunction(mainThread = true)
public void setSchedule(IArguments arguments) 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");
Schedule schedule = parseSchedule(arguments);
train.runtime.setSchedule(schedule, true);
}
private static Schedule parseSchedule(IArguments arguments) throws LuaException {
CreateLuaTable scheduleTable = new CreateLuaTable(arguments.getTable(0));
Schedule schedule = new Schedule();
schedule.cyclic = scheduleTable.getOptBoolean("cyclic").orElse(true);
CreateLuaTable entriesTable = scheduleTable.getTable("entries");
for (CreateLuaTable entryTable : entriesTable.tableValues()) {
ScheduleEntry entry = new ScheduleEntry();
entry.instruction = getInstruction(entryTable);
// Add conditions
if (entry.instruction.supportsConditions()) {
for (CreateLuaTable conditionsListTable : entryTable.getTable("conditions").tableValues()) {
List<ScheduleWaitCondition> conditionsList = new ArrayList<>();
for (CreateLuaTable conditionTable : conditionsListTable.tableValues()) {
conditionsList.add(getCondition(conditionTable));
}
entry.conditions.add(conditionsList);
}
}
schedule.entries.add(entry);
}
return schedule;
}
private static ScheduleInstruction getInstruction(CreateLuaTable entry) throws LuaException {
ResourceLocation location = new ResourceLocation(entry.getString("instruction"));
for (Pair<ResourceLocation, Supplier<? extends ScheduleInstruction>> pair : Schedule.INSTRUCTION_TYPES)
if (pair.getFirst().equals(location)) {
ScheduleInstruction instruction = pair.getSecond().get();
instruction.setData(getEntryData(entry.getTable("data")));
return instruction;
}
throw new LuaException("instruction " + location + " is not a valid instruction type");
}
private static ScheduleWaitCondition getCondition(CreateLuaTable entry) throws LuaException {
ResourceLocation location = new ResourceLocation(entry.getString("condition"));
for (Pair<ResourceLocation, Supplier<? extends ScheduleWaitCondition>> pair : Schedule.CONDITION_TYPES)
if (pair.getFirst().equals(location)) {
ScheduleWaitCondition condition = pair.getSecond().get();
condition.setData(getEntryData(entry.getTable("data")));
return condition;
}
throw new LuaException("condition " + location + " is not a valid condition type");
}
private static CompoundTag getEntryData(CreateLuaTable data) throws LuaException {
CompoundTag tag = new CompoundTag();
for (String key : data.stringKeySet()) {
String tagKey = StringHelper.snakeCaseToCamelCase(key);
Object value = data.get(key);
if (value instanceof Boolean)
tag.putBoolean(tagKey, (Boolean) value);
else if (value instanceof Number)
tag.putDouble(tagKey, ((Number) value).doubleValue());
else if (value instanceof String)
tag.putString(tagKey, (String) value);
else
throw new LuaException("");
}
return tag;
}
@NotNull
@Override
public String getType() {
return "Create_Station";
}
}

View File

@ -12,10 +12,14 @@ import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.AllSoundEvents; import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.compat.computercraft.ComputerControllable;
import com.simibubi.create.compat.computercraft.StationPeripheral;
import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException; import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableTE; import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableTE;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform; import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
@ -51,6 +55,7 @@ import com.simibubi.create.foundation.utility.WorldAttached;
import com.simibubi.create.foundation.utility.animation.LerpedFloat; import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser; import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -77,7 +82,7 @@ import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.PacketDistributor;
public class StationTileEntity extends SmartTileEntity implements ITransformableTE { public class StationTileEntity extends SmartTileEntity implements ITransformableTE, ComputerControllable {
public TrackTargetingBehaviour<GlobalStation> edgePoint; public TrackTargetingBehaviour<GlobalStation> edgePoint;
public LerpedFloat flag; public LerpedFloat flag;
@ -98,6 +103,7 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
boolean flagFlipped; boolean flagFlipped;
public Component lastDisassembledTrainName; public Component lastDisassembledTrainName;
private LazyOptional<IPeripheral> peripheral;
public StationTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) { public StationTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state); super(type, pos, state);
@ -699,10 +705,19 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
} }
@Override @Override
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) { public <T> @NotNull LazyOptional<T> getCapability(@NotNull Capability<T> cap, Direction side) {
LazyOptional<T> peripheralCap = getPeripheralCapability(cap);
if (isItemHandlerCap(cap)) if (isItemHandlerCap(cap))
return depotBehaviour.getItemCapability(cap, side); return depotBehaviour.getItemCapability(cap, side);
return super.getCapability(cap, side);
return peripheralCap.isPresent() ? peripheralCap : super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
removePeripheral();
} }
private void applyAutoSchedule() { private void applyAutoSchedule() {
@ -766,4 +781,19 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
edgePoint.transform(transform); edgePoint.transform(transform);
} }
@Override
public IPeripheral createPeripheral() {
return new StationPeripheral(this);
}
@Override
public void setPeripheral(LazyOptional<IPeripheral> peripheral) {
this.peripheral = peripheral;
}
@Override
public LazyOptional<IPeripheral> getPeripheral() {
return peripheral;
}
} }

View File

@ -25,6 +25,8 @@ public interface IScheduleInput {
public abstract CompoundTag getData(); public abstract CompoundTag getData();
public abstract void setData(CompoundTag data);
public default int slotsTargeted() { public default int slotsTargeted() {
return 0; return 0;
} }
@ -40,7 +42,7 @@ public interface IScheduleInput {
} }
public default void setItem(int slot, ItemStack stack) {} public default void setItem(int slot, ItemStack stack) {}
public default ItemStack getItem(int slot) { public default ItemStack getItem(int slot) {
return ItemStack.EMPTY; return ItemStack.EMPTY;
} }
@ -58,4 +60,4 @@ public interface IScheduleInput {
return false; return false;
} }
} }

View File

@ -3,33 +3,38 @@ package com.simibubi.create.content.logistics.trains.management.schedule;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
public abstract class ScheduleDataEntry implements IScheduleInput { public abstract class ScheduleDataEntry implements IScheduleInput {
protected CompoundTag data; protected CompoundTag data;
public ScheduleDataEntry() { public ScheduleDataEntry() {
data = new CompoundTag(); data = new CompoundTag();
} }
@Override @Override
public CompoundTag getData() { public CompoundTag getData() {
return data; return data;
} }
@Override
public void setData(CompoundTag data) {
this.data = data;
}
protected void writeAdditional(CompoundTag tag) {}; protected void writeAdditional(CompoundTag tag) {};
protected void readAdditional(CompoundTag tag) {}; protected void readAdditional(CompoundTag tag) {};
protected <T> T enumData(String key, Class<T> enumClass) { protected <T> T enumData(String key, Class<T> enumClass) {
T[] enumConstants = enumClass.getEnumConstants(); T[] enumConstants = enumClass.getEnumConstants();
return enumConstants[data.getInt(key) % enumConstants.length]; return enumConstants[data.getInt(key) % enumConstants.length];
} }
protected String textData(String key) { protected String textData(String key) {
return data.getString(key); return data.getString(key);
} }
protected int intData(String key) { protected int intData(String key) {
return data.getInt(key); return data.getInt(key);
} }
} }

View File

@ -0,0 +1,28 @@
package com.simibubi.create.foundation.utility;
import java.util.Locale;
public class StringHelper {
public static String snakeCaseToCamelCase(String text) {
StringBuilder builder = new StringBuilder();
builder.append(text.substring(0, 1).toUpperCase(Locale.ROOT));
for (int i = 1; i < text.length(); i++) {
int j = text.indexOf('_', i);
if (j == -1) {
builder.append(text.substring(i));
break;
}
builder.append(text.substring(i, j).toLowerCase(Locale.ROOT));
builder.append(text.substring(j + 1, j + 2).toUpperCase(Locale.ROOT));
i = j + 1;
}
return builder.toString();
}
}