From ab18034b985fe9cecf4de5add48557371789d756 Mon Sep 17 00:00:00 2001 From: caelwarner Date: Wed, 19 Oct 2022 15:58:56 -0700 Subject: [PATCH] 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 --- .../compat/computercraft/CreateLuaTable.java | 89 ++++++++++++ .../computercraft/StationPeripheral.java | 130 ++++++++++++++++++ .../edgePoint/station/StationTileEntity.java | 36 ++++- .../management/schedule/IScheduleInput.java | 6 +- .../schedule/ScheduleDataEntry.java | 21 +-- .../foundation/utility/StringHelper.java | 28 ++++ 6 files changed, 297 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/simibubi/create/compat/computercraft/CreateLuaTable.java create mode 100644 src/main/java/com/simibubi/create/compat/computercraft/StationPeripheral.java create mode 100644 src/main/java/com/simibubi/create/foundation/utility/StringHelper.java diff --git a/src/main/java/com/simibubi/create/compat/computercraft/CreateLuaTable.java b/src/main/java/com/simibubi/create/compat/computercraft/CreateLuaTable.java new file mode 100644 index 000000000..ac46bdc76 --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/computercraft/CreateLuaTable.java @@ -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 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 stringKeySet() throws LuaException { + Set 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 tableValues() throws LuaException { + List 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); + } + +} diff --git a/src/main/java/com/simibubi/create/compat/computercraft/StationPeripheral.java b/src/main/java/com/simibubi/create/compat/computercraft/StationPeripheral.java new file mode 100644 index 000000000..0e05c2180 --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/computercraft/StationPeripheral.java @@ -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 { + + 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 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> 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> 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"; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java index 1c9edc2bd..544984651 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java @@ -12,10 +12,14 @@ import java.util.UUID; import javax.annotation.Nullable; +import org.jetbrains.annotations.NotNull; + import com.simibubi.create.AllBlocks; import com.simibubi.create.AllItems; import com.simibubi.create.AllSoundEvents; 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.ITransformableTE; 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.Chaser; +import dan200.computercraft.api.peripheral.IPeripheral; import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.core.Direction; @@ -77,7 +82,7 @@ import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.network.PacketDistributor; -public class StationTileEntity extends SmartTileEntity implements ITransformableTE { +public class StationTileEntity extends SmartTileEntity implements ITransformableTE, ComputerControllable { public TrackTargetingBehaviour edgePoint; public LerpedFloat flag; @@ -98,6 +103,7 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable boolean flagFlipped; public Component lastDisassembledTrainName; + private LazyOptional peripheral; public StationTileEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); @@ -699,10 +705,19 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable } @Override - public LazyOptional getCapability(Capability cap, Direction side) { + public @NotNull LazyOptional getCapability(@NotNull Capability cap, Direction side) { + LazyOptional peripheralCap = getPeripheralCapability(cap); + if (isItemHandlerCap(cap)) 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() { @@ -766,4 +781,19 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable edgePoint.transform(transform); } + @Override + public IPeripheral createPeripheral() { + return new StationPeripheral(this); + } + + @Override + public void setPeripheral(LazyOptional peripheral) { + this.peripheral = peripheral; + } + + @Override + public LazyOptional getPeripheral() { + return peripheral; + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/IScheduleInput.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/IScheduleInput.java index 4858e0974..41392f608 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/IScheduleInput.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/IScheduleInput.java @@ -25,6 +25,8 @@ public interface IScheduleInput { public abstract CompoundTag getData(); + public abstract void setData(CompoundTag data); + public default int slotsTargeted() { return 0; } @@ -40,7 +42,7 @@ public interface IScheduleInput { } public default void setItem(int slot, ItemStack stack) {} - + public default ItemStack getItem(int slot) { return ItemStack.EMPTY; } @@ -58,4 +60,4 @@ public interface IScheduleInput { return false; } -} \ No newline at end of file +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleDataEntry.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleDataEntry.java index 58b2eae7a..df81a447d 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleDataEntry.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleDataEntry.java @@ -3,33 +3,38 @@ package com.simibubi.create.content.logistics.trains.management.schedule; import net.minecraft.nbt.CompoundTag; public abstract class ScheduleDataEntry implements IScheduleInput { - + protected CompoundTag data; - + public ScheduleDataEntry() { data = new CompoundTag(); } - + @Override public CompoundTag getData() { return data; } - + + @Override + public void setData(CompoundTag data) { + this.data = data; + } + protected void writeAdditional(CompoundTag tag) {}; protected void readAdditional(CompoundTag tag) {}; - + protected T enumData(String key, Class enumClass) { T[] enumConstants = enumClass.getEnumConstants(); return enumConstants[data.getInt(key) % enumConstants.length]; } - + protected String textData(String key) { return data.getString(key); } - + protected int intData(String key) { return data.getInt(key); } - + } diff --git a/src/main/java/com/simibubi/create/foundation/utility/StringHelper.java b/src/main/java/com/simibubi/create/foundation/utility/StringHelper.java new file mode 100644 index 000000000..863569651 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/utility/StringHelper.java @@ -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(); + } + +}