diff --git a/src/main/java/com/simibubi/create/compat/computercraft/CreateLuaTable.java b/src/main/java/com/simibubi/create/compat/computercraft/CreateLuaTable.java index ac46bdc76..46ca3addb 100644 --- a/src/main/java/com/simibubi/create/compat/computercraft/CreateLuaTable.java +++ b/src/main/java/com/simibubi/create/compat/computercraft/CreateLuaTable.java @@ -3,20 +3,30 @@ package com.simibubi.create.compat.computercraft; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; 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; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class CreateLuaTable extends ObjectLuaTable { +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.LuaTable; +import dan200.computercraft.api.lua.LuaValues; + +public class CreateLuaTable implements LuaTable { + + private final Map map; + + public CreateLuaTable() { + this.map = new HashMap<>(); + } public CreateLuaTable(Map map) { - super(map); + this.map = new HashMap<>(map); } public boolean getBoolean(String key) throws LuaException { @@ -86,4 +96,77 @@ public class CreateLuaTable extends ObjectLuaTable { return Collections.unmodifiableList(tables); } + public Map getMap() { + return map; + } + + @Nullable + @Override + public Object put(Object key, Object value) { + return map.put(key, value); + } + + public void putBoolean(String key, boolean value) { + map.put(key, value); + } + + public void putDouble(String key, double value) { + map.put(key, value); + } + + public void putString(String key, String value) { + map.put(key, value); + } + + public void putTable(String key, CreateLuaTable value) { + map.put(key, value); + } + + public void putTable(int i, CreateLuaTable value) { + map.put(i, value); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean containsKey(Object o) { + return map.containsKey(o); + } + + @Override + public boolean containsValue(Object o) { + return map.containsValue(o); + } + + @Override + public Object get(Object o) { + return map.get(o); + } + + @NotNull + @Override + public Set keySet() { + return map.keySet(); + } + + @NotNull + @Override + public Collection values() { + return map.values(); + } + + @NotNull + @Override + public Set> entrySet() { + return map.entrySet(); + } + } diff --git a/src/main/java/com/simibubi/create/compat/computercraft/peripherals/StationPeripheral.java b/src/main/java/com/simibubi/create/compat/computercraft/peripherals/StationPeripheral.java index 309058f5e..8bf555a93 100644 --- a/src/main/java/com/simibubi/create/compat/computercraft/peripherals/StationPeripheral.java +++ b/src/main/java/com/simibubi/create/compat/computercraft/peripherals/StationPeripheral.java @@ -1,8 +1,10 @@ package com.simibubi.create.compat.computercraft.peripherals; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; +import java.util.Map; + +import javax.annotation.Nullable; + +import org.jetbrains.annotations.NotNull; import com.simibubi.create.compat.computercraft.CreateLuaTable; import com.simibubi.create.content.logistics.trains.entity.Train; @@ -10,20 +12,21 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.station 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.ByteTag; import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; - +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.IntTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; import net.minecraftforge.network.PacketDistributor; public class StationPeripheral extends SyncedPeripheral { @@ -127,6 +130,23 @@ public class StationPeripheral extends SyncedPeripheral { AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainEditPacket.TrainEditReturnPacket(train.id, name, train.icon.getId())); } + @LuaFunction + public final CreateLuaTable getSchedule() 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 = train.runtime.getSchedule(); + if (schedule == null) + throw new LuaException("train doesn't have a schedule"); + + return fromCompoundTag(schedule.write()); + } + @LuaFunction(mainThread = true) public final void setSchedule(IArguments arguments) throws LuaException { GlobalStation station = tile.getStation(); @@ -137,87 +157,100 @@ public class StationPeripheral extends SyncedPeripheral { if (train == null) throw new LuaException("there is no train present"); - Schedule schedule = parseSchedule(arguments); - train.runtime.setSchedule(schedule, true); + Schedule schedule = Schedule.fromTag(toCompoundTag(new CreateLuaTable(arguments.getTable(0)))); + boolean autoSchedule = train.runtime.getSchedule() == null || train.runtime.isAutoSchedule; + train.runtime.setSchedule(schedule, autoSchedule); } - private static Schedule parseSchedule(IArguments arguments) throws LuaException { - CreateLuaTable scheduleTable = new CreateLuaTable(arguments.getTable(0)); - Schedule schedule = new Schedule(); + private static @NotNull CreateLuaTable fromCompoundTag(CompoundTag tag) throws LuaException { + return (CreateLuaTable) fromNBTTag(null, tag); + } - schedule.cyclic = scheduleTable.getOptBoolean("cyclic").orElse(true); - CreateLuaTable entriesTable = scheduleTable.getTable("entries"); + private static @NotNull Object fromNBTTag(@Nullable String key, Tag tag) throws LuaException { + byte type = tag.getId(); - for (CreateLuaTable entryTable : entriesTable.tableValues()) { - ScheduleEntry entry = new ScheduleEntry(); + if (type == Tag.TAG_BYTE && key != null && key.equals("Count")) + return ((NumericTag) tag).getAsByte(); + else if (type == Tag.TAG_BYTE) + return ((NumericTag) tag).getAsByte() != 0; + else if (type == Tag.TAG_INT || type == Tag.TAG_LONG) + return ((NumericTag) tag).getAsLong(); + else if (type == Tag.TAG_FLOAT || type == Tag.TAG_DOUBLE) + return ((NumericTag) tag).getAsDouble(); + else if (type == Tag.TAG_STRING) + return tag.getAsString(); + else if (type == Tag.TAG_LIST) { + CreateLuaTable list = new CreateLuaTable(); + ListTag listTag = (ListTag) tag; - 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); - } + for (int i = 0; i < listTag.size(); i++) { + list.put(i + 1, fromNBTTag(null, listTag.get(i))); } - schedule.entries.add(entry); + return list; + + } else if (type == Tag.TAG_COMPOUND) { + CreateLuaTable table = new CreateLuaTable(); + CompoundTag compoundTag = (CompoundTag) tag; + + for (String compoundKey : compoundTag.getAllKeys()) { + table.put( + StringHelper.camelCaseToSnakeCase(compoundKey), + fromNBTTag(compoundKey, compoundTag.get(compoundKey)) + ); + } + + return table; } - return schedule; + throw new LuaException("unknown tag type " + tag.getType().getName()); } - 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 @NotNull CompoundTag toCompoundTag(CreateLuaTable table) throws LuaException { + return (CompoundTag) toNBTTag(null, table.getMap()); } - 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); + private static @NotNull Tag toNBTTag(@Nullable String key, Object value) throws LuaException { + if (value instanceof Boolean v) + return ByteTag.valueOf(v); + else if (value instanceof Byte || (key != null && key.equals("count"))) + return ByteTag.valueOf(((Number) value).byteValue()); + else if (value instanceof Number v) { + // If number is numerical integer + if (v.intValue() == v.doubleValue()) + return IntTag.valueOf(v.intValue()); else - throw new LuaException(""); + return DoubleTag.valueOf(v.doubleValue()); + + } else if (value instanceof String v) + return StringTag.valueOf(v); + else if (value instanceof Map v && v.containsKey(1.0)) { // List + ListTag list = new ListTag(); + for (Object o : v.values()) { + list.add(toNBTTag(null, o)); + } + + return list; + + } else if (value instanceof Map v) { // Table/Map + CompoundTag compound = new CompoundTag(); + for (Object objectKey : v.keySet()) { + if (!(objectKey instanceof String compoundKey)) + throw new LuaException("table key is not of type string"); + + compound.put( + // Items serialize their resource location as "id" and not as "Id". + // This check is needed to see if the 'i' should be left lowercase or not. + // Items store "count" in the same compound tag, so we can check for its presence to see if this is a serialized item + compoundKey.equals("id") && v.containsKey("count") ? "id" : StringHelper.snakeCaseToCamelCase(compoundKey), + toNBTTag(compoundKey, v.get(compoundKey)) + ); + } + + return compound; } - return tag; + throw new LuaException("unknown object type " + value.getClass().getName()); } @NotNull 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 df81a447d..c2c7144c7 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 @@ -18,6 +18,7 @@ public abstract class ScheduleDataEntry implements IScheduleInput { @Override public void setData(CompoundTag data) { this.data = data; + readAdditional(data); } protected void writeAdditional(CompoundTag tag) {}; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/FluidThresholdCondition.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/FluidThresholdCondition.java index 0c3f25f55..f9ab12a18 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/FluidThresholdCondition.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/FluidThresholdCondition.java @@ -68,7 +68,8 @@ public class FluidThresholdCondition extends CargoThresholdCondition { @Override protected void readAdditional(CompoundTag tag) { super.readAdditional(tag); - compareStack = ItemStack.of(tag.getCompound("Bucket")); + if (tag.contains("Bucket")) + compareStack = ItemStack.of(tag.getCompound("Bucket")); } @Override @@ -139,4 +140,4 @@ public class FluidThresholdCondition extends CargoThresholdCondition { Math.max(0, getThreshold() + offset), Lang.translateDirect("schedule.condition.threshold.buckets")); } -} \ No newline at end of file +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/ItemThresholdCondition.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/ItemThresholdCondition.java index 78905d201..81122ad73 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/ItemThresholdCondition.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/ItemThresholdCondition.java @@ -69,7 +69,8 @@ public class ItemThresholdCondition extends CargoThresholdCondition { @Override protected void readAdditional(CompoundTag tag) { super.readAdditional(tag); - stack = ItemStack.of(tag.getCompound("Item")); + if (tag.contains("Item")) + stack = ItemStack.of(tag.getCompound("Item")); } @Override @@ -131,4 +132,4 @@ public class ItemThresholdCondition extends CargoThresholdCondition { Math.max(0, getThreshold() + offset), Lang.translateDirect("schedule.condition.threshold." + (inStacks() ? "stacks" : "items"))); } -} \ No newline at end of file +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/RedstoneLinkCondition.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/RedstoneLinkCondition.java index 9bb8f4190..896c9a23f 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/RedstoneLinkCondition.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/RedstoneLinkCondition.java @@ -107,7 +107,8 @@ public class RedstoneLinkCondition extends ScheduleWaitCondition { @Override protected void readAdditional(CompoundTag tag) { - freq = Couple.deserializeEach(tag.getList("Frequency", Tag.TAG_COMPOUND), c -> Frequency.of(ItemStack.of(c))); + if (tag.contains("Frequency")) + freq = Couple.deserializeEach(tag.getList("Frequency", Tag.TAG_COMPOUND), c -> Frequency.of(ItemStack.of(c))); } @Override @@ -118,7 +119,7 @@ public class RedstoneLinkCondition extends ScheduleWaitCondition { .titled(Lang.translateDirect("schedule.condition.redstone_link.frequency_state")), "Inverted"); } - + @Override public MutableComponent getWaitingStatus(Level level, Train train, CompoundTag tag) { return Lang.translateDirect("schedule.condition.redstone_link.status"); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/ScheduleWaitCondition.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/ScheduleWaitCondition.java index a8e307d6a..7da150cce 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/ScheduleWaitCondition.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/condition/ScheduleWaitCondition.java @@ -16,16 +16,17 @@ import net.minecraft.world.level.Level; public abstract class ScheduleWaitCondition extends ScheduleDataEntry { public abstract boolean tickCompletion(Level level, Train train, CompoundTag context); - + protected void requestStatusToUpdate(CompoundTag context) { context.putInt("StatusVersion", context.getInt("StatusVersion") + 1); } - + public final CompoundTag write() { CompoundTag tag = new CompoundTag(); + CompoundTag dataCopy = data.copy(); + writeAdditional(dataCopy); tag.putString("Id", getId().toString()); - tag.put("Data", data.copy()); - writeAdditional(tag); + tag.put("Data", dataCopy); return tag; } @@ -43,11 +44,14 @@ public abstract class ScheduleWaitCondition extends ScheduleDataEntry { } ScheduleWaitCondition condition = supplier.get(); - condition.data = tag.getCompound("Data"); + // Left around for migration purposes. Data added in writeAdditional has moved into the "Data" tag condition.readAdditional(tag); + CompoundTag data = tag.getCompound("Data"); + condition.readAdditional(data); + condition.data = data; return condition; } public abstract MutableComponent getWaitingStatus(Level level, Train train, CompoundTag tag); -} \ No newline at end of file +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/destination/ScheduleInstruction.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/destination/ScheduleInstruction.java index cc3554094..6e1e56354 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/destination/ScheduleInstruction.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/destination/ScheduleInstruction.java @@ -16,9 +16,10 @@ public abstract class ScheduleInstruction extends ScheduleDataEntry { public final CompoundTag write() { CompoundTag tag = new CompoundTag(); + CompoundTag dataCopy = data.copy(); + writeAdditional(dataCopy); tag.putString("Id", getId().toString()); - tag.put("Data", data.copy()); - writeAdditional(tag); + tag.put("Data", dataCopy); return tag; } @@ -36,9 +37,12 @@ public abstract class ScheduleInstruction extends ScheduleDataEntry { } ScheduleInstruction scheduleDestination = supplier.get(); - scheduleDestination.data = tag.getCompound("Data"); + // Left around for migration purposes. Data added in writeAdditional has moved into the "Data" tag scheduleDestination.readAdditional(tag); + CompoundTag data = tag.getCompound("Data"); + scheduleDestination.readAdditional(data); + scheduleDestination.data = data; return scheduleDestination; } -} \ No newline at end of file +} diff --git a/src/main/java/com/simibubi/create/foundation/utility/StringHelper.java b/src/main/java/com/simibubi/create/foundation/utility/StringHelper.java index 863569651..78521b4e5 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/StringHelper.java +++ b/src/main/java/com/simibubi/create/foundation/utility/StringHelper.java @@ -25,4 +25,22 @@ public class StringHelper { return builder.toString(); } + public static String camelCaseToSnakeCase(String text) { + StringBuilder builder = new StringBuilder(); + + for (char c : text.toCharArray()) { + if (Character.isUpperCase(c)) { + builder.append('_'); + builder.append(Character.toLowerCase(c)); + } else { + builder.append(c); + } + } + + if (builder.length() > 0 && builder.charAt(0) == '_') + builder.deleteCharAt(0); + + return builder.toString(); + } + }