From a030c9f6e3a62d6ec5352f97619adc3c7a9e4415 Mon Sep 17 00:00:00 2001 From: Daniel Deptula Date: Thu, 24 Jul 2025 17:55:26 -0400 Subject: [PATCH 1/2] Add deliver and retrieve package conditions Slightly hacky fix for trains eating all packages in a post box, by requiring a condition be met to get the package or deliver the package. --- .../content/trains/schedule/Schedule.java | 4 + .../condition/DeliverPackageCondition.java | 69 ++++++++ .../condition/RetrievePackageCondition.java | 88 ++++++++++ .../content/trains/station/GlobalStation.java | 166 ++++++++++++++++++ .../assets/create/lang/default/interface.json | 10 +- .../resources/assets/create/lang/en_gb.json | 17 +- 6 files changed, 344 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/trains/schedule/condition/DeliverPackageCondition.java create mode 100644 src/main/java/com/simibubi/create/content/trains/schedule/condition/RetrievePackageCondition.java diff --git a/src/main/java/com/simibubi/create/content/trains/schedule/Schedule.java b/src/main/java/com/simibubi/create/content/trains/schedule/Schedule.java index 910e70829b..45e4cafab3 100644 --- a/src/main/java/com/simibubi/create/content/trains/schedule/Schedule.java +++ b/src/main/java/com/simibubi/create/content/trains/schedule/Schedule.java @@ -5,9 +5,11 @@ import java.util.function.Supplier; import com.simibubi.create.Create; +import com.simibubi.create.content.trains.schedule.condition.DeliverPackageCondition; import com.simibubi.create.content.trains.schedule.condition.FluidThresholdCondition; import com.simibubi.create.content.trains.schedule.condition.IdleCargoCondition; import com.simibubi.create.content.trains.schedule.condition.ItemThresholdCondition; +import com.simibubi.create.content.trains.schedule.condition.RetrievePackageCondition; import com.simibubi.create.content.trains.schedule.condition.PlayerPassengerCondition; import com.simibubi.create.content.trains.schedule.condition.RedstoneLinkCondition; import com.simibubi.create.content.trains.schedule.condition.ScheduleWaitCondition; @@ -63,6 +65,8 @@ public class Schedule { registerCondition("idle", IdleCargoCondition::new); registerCondition("unloaded", StationUnloadedCondition::new); registerCondition("powered", StationPoweredCondition::new); + registerCondition("retrieve_package", RetrievePackageCondition::new); + registerCondition("deliver_package", DeliverPackageCondition::new); } private static void registerInstruction(String name, Supplier factory) { diff --git a/src/main/java/com/simibubi/create/content/trains/schedule/condition/DeliverPackageCondition.java b/src/main/java/com/simibubi/create/content/trains/schedule/condition/DeliverPackageCondition.java new file mode 100644 index 0000000000..50041bc310 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/trains/schedule/condition/DeliverPackageCondition.java @@ -0,0 +1,69 @@ +package com.simibubi.create.content.trains.schedule.condition; + +import com.google.common.collect.ImmutableList; +import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.box.PackageItem; +import com.simibubi.create.content.logistics.box.PackageStyles; +import com.simibubi.create.content.logistics.packagePort.postbox.PostboxBlockEntity; +import com.simibubi.create.content.trains.entity.Carriage; +import com.simibubi.create.content.trains.entity.CarriageContraptionEntity; +import com.simibubi.create.content.trains.entity.Train; +import com.simibubi.create.content.trains.station.GlobalStation.GlobalPackagePort; +import com.simibubi.create.foundation.gui.ModularGuiLineBuilder; +import com.simibubi.create.foundation.utility.CreateLang; + +import net.createmod.catnip.data.Pair; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.IItemHandlerModifiable; +import net.neoforged.neoforge.items.ItemHandlerHelper; + +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.Map.Entry; + +public class DeliverPackageCondition extends ScheduleWaitCondition { + + public DeliverPackageCondition() { + } + + @Override + public boolean tickCompletion(Level level, Train train, CompoundTag context) { + return true; + } + + @Override + public MutableComponent getWaitingStatus(Level level, Train train, CompoundTag tag) { + return CreateLang.translateDirect("schedule.condition.deliver_package.status"); + } + + @Override + public Pair getSummary() { + return Pair.of(PackageStyles.getDefaultBox(), CreateLang.translateDirect("schedule.condition.deliver_package.summary")); + } + + @Override + public List getTitleAs(String type) { + return ImmutableList.of( + Component.translatable(getId().getNamespace() + ".schedule." + type + "." + getId().getPath())); + } + + @Override + public ResourceLocation getId() { + return Create.asResource("deliver_package"); + } + +} diff --git a/src/main/java/com/simibubi/create/content/trains/schedule/condition/RetrievePackageCondition.java b/src/main/java/com/simibubi/create/content/trains/schedule/condition/RetrievePackageCondition.java new file mode 100644 index 0000000000..8f35bc3cad --- /dev/null +++ b/src/main/java/com/simibubi/create/content/trains/schedule/condition/RetrievePackageCondition.java @@ -0,0 +1,88 @@ +package com.simibubi.create.content.trains.schedule.condition; + +import com.google.common.collect.ImmutableList; +import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.box.PackageItem; +import com.simibubi.create.content.logistics.box.PackageStyles; +import com.simibubi.create.content.logistics.packagePort.postbox.PostboxBlockEntity; +import com.simibubi.create.content.trains.entity.Carriage; +import com.simibubi.create.content.trains.entity.CarriageContraptionEntity; +import com.simibubi.create.content.trains.entity.Train; + +import com.simibubi.create.content.trains.station.GlobalStation.GlobalPackagePort; +import com.simibubi.create.foundation.gui.ModularGuiLineBuilder; +import com.simibubi.create.foundation.utility.CreateLang; + +import net.createmod.catnip.data.Pair; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; + +import net.neoforged.neoforge.items.IItemHandlerModifiable; + +import net.neoforged.neoforge.items.ItemHandlerHelper; + +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.Map.Entry; + +public class RetrievePackageCondition extends ScheduleWaitCondition { + + public RetrievePackageCondition() { + data.putString("Filter", ""); + } + + @Override + public boolean tickCompletion(Level level, Train train, CompoundTag context) { + return true; + } + + @Override + public MutableComponent getWaitingStatus(Level level, Train train, CompoundTag tag) { + return CreateLang.translateDirect("schedule.condition.retrieve_package.status", getFilter()); + } + + @Override + public Pair getSummary() { + return Pair.of(PackageStyles.getDefaultBox(), CreateLang.translateDirect("schedule.condition.retrieve_package.summary")); + } + + @Override + public List getTitleAs(String type) { + return ImmutableList.of( + Component.translatable(getId().getNamespace() + ".schedule." + type + "." + getId().getPath()), + CreateLang.translateDirect("schedule.condition.retrieve_package.addressed", getFilter()) + .withStyle(ChatFormatting.DARK_AQUA)); + } + + @Override + public ResourceLocation getId() { + return Create.asResource("retrieve_package"); + } + + @OnlyIn(Dist.CLIENT) + protected void modifyEditBox(EditBox box) { + box.setFilter(s -> StringUtils.countMatches(s, '*') <= 3); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void initConfigurationWidgets(ModularGuiLineBuilder builder) { + builder.addTextInput(0, 121, (e, t) -> modifyEditBox(e), "Filter"); + } + + public String getFilter() { + return textData("Filter"); + } +} diff --git a/src/main/java/com/simibubi/create/content/trains/station/GlobalStation.java b/src/main/java/com/simibubi/create/content/trains/station/GlobalStation.java index adf1ffd470..63617e36c7 100644 --- a/src/main/java/com/simibubi/create/content/trains/station/GlobalStation.java +++ b/src/main/java/com/simibubi/create/content/trains/station/GlobalStation.java @@ -1,7 +1,9 @@ package com.simibubi.create.content.trains.station; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -15,6 +17,13 @@ import com.simibubi.create.content.trains.entity.Train; import com.simibubi.create.content.trains.graph.DimensionPalette; import com.simibubi.create.content.trains.graph.TrackNode; +import com.simibubi.create.content.trains.schedule.ScheduleEntry; +import com.simibubi.create.content.trains.schedule.condition.DeliverPackageCondition; +import com.simibubi.create.content.trains.schedule.condition.RetrievePackageCondition; +import com.simibubi.create.content.trains.schedule.condition.ScheduleWaitCondition; +import com.simibubi.create.content.trains.schedule.destination.DeliverPackagesInstruction; +import com.simibubi.create.content.trains.schedule.destination.DestinationInstruction; +import com.simibubi.create.content.trains.schedule.destination.FetchPackagesInstruction; import com.simibubi.create.content.trains.signal.SingleBlockEntityEdgePoint; import net.createmod.catnip.nbt.NBTHelper; @@ -170,12 +179,169 @@ public static class GlobalPackagePort { public boolean primed = false; } + private List getRetrievePackageFilters() { + Train train = getNearestTrain(); + if (train == null || train.runtime.schedule == null || train.runtime.schedule.entries.isEmpty()) new ArrayList(); + ScheduleEntry entry = train.runtime.schedule.entries.get(train.runtime.currentEntry); + List filters = new ArrayList<>(); + + if (entry.instruction instanceof FetchPackagesInstruction fetchPackagesInstruction) { + filters.add(fetchPackagesInstruction.getFilter()); + return filters; + } + + for (var i = 0; i < entry.conditions.size(); i++) { + List entryConditions = entry.conditions.get(i); + for (var entryCondition : entryConditions) { + if (entryCondition instanceof RetrievePackageCondition retrieveCondition) { + filters.add(retrieveCondition.getFilter()); + } + } + } + return filters; + } + + private boolean hasDeliverPackageCondition() { + Train train = getNearestTrain(); + if (train == null || train.runtime.schedule == null || train.runtime.schedule.entries.isEmpty()) return false; + ScheduleEntry entry = train.runtime.schedule.entries.get(train.runtime.currentEntry); + for (var i = 0; i < entry.conditions.size(); i++) { + List entryConditions = entry.conditions.get(i); + for (var entryCondition : entryConditions) { + if (entryCondition instanceof DeliverPackageCondition) { + return true; + } + } + } + return false; + } + + private boolean isDeliverPackageInstruction() { + Train train = getNearestTrain(); + if (train == null || train.runtime.schedule == null || train.runtime.schedule.entries.isEmpty()) return false; + ScheduleEntry entry = train.runtime.schedule.entries.get(train.runtime.currentEntry); + if (entry.instruction instanceof DeliverPackagesInstruction) { + return true; + } + return false; + } + + private boolean shouldDeliverPackages() { + return hasDeliverPackageCondition() || isDeliverPackageInstruction(); + } + + public void runMailTransferScheduled() { + Train train = getPresentTrain(); + if (train == null || connectedPorts.isEmpty()) + return; + Level level = null; + + if (train.runtime.schedule == null) return; + + + for (Carriage carriage : train.carriages) { + if (level == null) { + CarriageContraptionEntity entity = carriage.anyAvailableEntity(); + if (entity != null && entity.level() instanceof ServerLevel sl) + level = sl.getServer() + .getLevel(getBlockEntityDimension()); + } + + IItemHandlerModifiable carriageInventory = carriage.storage.getAllItems(); + if (carriageInventory == null) + continue; + + // Import from station + var retrievePackageFilters = getRetrievePackageFilters(); + if (retrievePackageFilters != null && !retrievePackageFilters.isEmpty()) { + for (Entry entry : connectedPorts.entrySet()) { + GlobalPackagePort port = entry.getValue(); + BlockPos pos = entry.getKey(); + PostboxBlockEntity box = null; + + IItemHandlerModifiable postboxInventory = port.offlineBuffer; + if (level != null && level.isLoaded(pos) + && level.getBlockEntity(pos) instanceof PostboxBlockEntity ppbe) { + postboxInventory = ppbe.inventory; + box = ppbe; + } + + for (int slot = 0; slot < postboxInventory.getSlots(); slot++) { + ItemStack stack = postboxInventory.getStackInSlot(slot); + if (!PackageItem.isPackage(stack)) + continue; + if (PackageItem.matchAddress(stack, port.address)) + continue; + + // Only import if we have matching conditions + for (var filter : retrievePackageFilters) { + if (PackageItem.matchAddress(stack, filter)) { + ItemStack result = ItemHandlerHelper.insertItemStacked(carriageInventory, stack, false); + if (!result.isEmpty()) + break; + + postboxInventory.setStackInSlot(slot, ItemStack.EMPTY); + Create.RAILWAYS.markTracksDirty(); + if (box != null) + box.spawnParticles(); + + break; + } + } + } + } + } + + // Only export if we have the condition to + if (!shouldDeliverPackages()) return; + + // Export to station + for (int slot = 0; slot < carriageInventory.getSlots(); slot++) { + ItemStack stack = carriageInventory.getStackInSlot(slot); + if (!PackageItem.isPackage(stack)) + continue; + + for (Entry entry : connectedPorts.entrySet()) { + GlobalPackagePort port = entry.getValue(); + BlockPos pos = entry.getKey(); + PostboxBlockEntity box = null; + + if (!PackageItem.matchAddress(stack, port.address)) + continue; + + IItemHandler postboxInventory = port.offlineBuffer; + if (level != null && level.isLoaded(pos) + && level.getBlockEntity(pos) instanceof PostboxBlockEntity ppbe) { + postboxInventory = ppbe.inventory; + box = ppbe; + } + + ItemStack result = ItemHandlerHelper.insertItemStacked(postboxInventory, stack, false); + if (!result.isEmpty()) + continue; + + Create.RAILWAYS.markTracksDirty(); + carriageInventory.setStackInSlot(slot, ItemStack.EMPTY); + if (box != null) + box.spawnParticles(); + + break; + } + } + } + } + public void runMailTransfer() { Train train = getPresentTrain(); if (train == null || connectedPorts.isEmpty()) return; Level level = null; + if (train.runtime.schedule != null) { + runMailTransferScheduled(); + return; + } + for (Carriage carriage : train.carriages) { if (level == null) { CarriageContraptionEntity entity = carriage.anyAvailableEntity(); diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json index dc0242aeb4..5573682dd1 100644 --- a/src/main/resources/assets/create/lang/default/interface.json +++ b/src/main/resources/assets/create/lang/default/interface.json @@ -801,7 +801,7 @@ "create.schedule.instruction.address_filter_edit_box_2": "Train may navigate anywhere to collect it", "create.schedule.instruction.address_filter_edit_box_3": "Add another instruction to make it return", - "create.schedule.condition_type": "Continue if/after:", + "create.schedule.condition_type": "Continue if/after:", "create.schedule.condition.editor": "Condition Editor", "create.schedule.condition.delay": "Scheduled Delay", @@ -856,6 +856,14 @@ "create.schedule.condition.redstone_link.frequency_state": "Frequency state:", "create.schedule.condition.redstone_link.frequency_powered": "Frequency powered:", "create.schedule.condition.redstone_link.frequency_unpowered": "Frequency not powered:", + "create.schedule.condition.retrieve_package": "Retrieve Packages", + "create.schedule.condition.retrieve_package.summary": "Retrieve packages", + "create.schedule.condition.retrieve_package.status": "Retrieve packages: %1$s", + "create.schedule.condition.retrieve_package.addressed": "with address %1$s", + "create.schedule.condition.deliver_package": "Deliver Packages", + "create.schedule.condition.deliver_package.summary": "Deliver packages", + "create.schedule.condition.deliver_package.status": "Delivering packages", + "create.schedule.condition.player_count": "Players Seated", "create.schedule.condition.player_count.summary": "%1$s Player", "create.schedule.condition.player_count.summary_plural": "%1$s Players", diff --git a/src/main/resources/assets/create/lang/en_gb.json b/src/main/resources/assets/create/lang/en_gb.json index b4b253b563..ee79623451 100644 --- a/src/main/resources/assets/create/lang/en_gb.json +++ b/src/main/resources/assets/create/lang/en_gb.json @@ -2723,6 +2723,13 @@ "create.schedule.condition.redstone_link.unpowered": "Not powered", "create.schedule.condition.redstone_link_off": "Link Off", "create.schedule.condition.redstone_link_on": "Link On", + "create.schedule.condition.retrieve_package": "Retrieve Packages", + "create.schedule.condition.retrieve_package.summary": "Retrieve packages", + "create.schedule.condition.retrieve_package.status": "Retrieve packages: %1$s", + "create.schedule.condition.retrieve_package.addressed": "with address %1$s", + "create.schedule.condition.deliver_package": "Deliver Packages", + "create.schedule.condition.deliver_package.summary": "Deliver packages", + "create.schedule.condition.deliver_package.status": "Delivering packages", "create.schedule.condition.threshold.anything": "Anything", "create.schedule.condition.threshold.buckets": "Buckets", "create.schedule.condition.threshold.equal": "exactly", @@ -2771,14 +2778,6 @@ "create.schedule.instruction.name_edit_box": "Scheduled Title", "create.schedule.instruction.name_edit_box_1": "Affects text shown on displays", "create.schedule.instruction.name_edit_box_2": "Defaults to next destination's name", - "create.schedule.instruction.package_delivery": "Deliver Package", - "create.schedule.instruction.package_delivery.summary": "Deliver first package in cargo", - "create.schedule.instruction.package_delivery.summary_1": "Drives to the station with", - "create.schedule.instruction.package_delivery.summary_2": "the correct postbox", - "create.schedule.instruction.package_retrieval": "Retrieve Package", - "create.schedule.instruction.package_retrieval.summary": "Fetch a package addressed to:", - "create.schedule.instruction.package_retrieval.summary_1": "Drives to a postbox with", - "create.schedule.instruction.package_retrieval.summary_2": "an undelivered package", "create.schedule.instruction.rename": "Update Schedule Title", "create.schedule.instruction.rename.summary": "New Title:", "create.schedule.instruction.throttle": "Limit Max Speed", @@ -3359,4 +3358,4 @@ "item.create.zinc_nugget": "Zinc Nugget", "itemGroup.create.base": "Create", "itemGroup.create.palettes": "Create's Building Blocks" -} \ No newline at end of file +} From 40853b0f48bc3153bbbf00dae91a1c1649d00388 Mon Sep 17 00:00:00 2001 From: Daniel Deptula Date: Thu, 24 Jul 2025 21:01:11 -0400 Subject: [PATCH 2/2] Fix leaving mailTransfer early for additional carriages due to bad delivery check. Fixed leaving mail transfer early when checking if delivering packages. Minor optimization via storing the results of needing to deliver and package filters so we aren't checking for every carriage. --- .../content/trains/station/GlobalStation.java | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/station/GlobalStation.java b/src/main/java/com/simibubi/create/content/trains/station/GlobalStation.java index 63617e36c7..dd58276d2f 100644 --- a/src/main/java/com/simibubi/create/content/trains/station/GlobalStation.java +++ b/src/main/java/com/simibubi/create/content/trains/station/GlobalStation.java @@ -239,6 +239,8 @@ public void runMailTransferScheduled() { if (train.runtime.schedule == null) return; + var shouldDeliverPackages = shouldDeliverPackages(); + var retrievePackageFilters = getRetrievePackageFilters(); for (Carriage carriage : train.carriages) { if (level == null) { CarriageContraptionEntity entity = carriage.anyAvailableEntity(); @@ -252,7 +254,6 @@ public void runMailTransferScheduled() { continue; // Import from station - var retrievePackageFilters = getRetrievePackageFilters(); if (retrievePackageFilters != null && !retrievePackageFilters.isEmpty()) { for (Entry entry : connectedPorts.entrySet()) { GlobalPackagePort port = entry.getValue(); @@ -293,39 +294,39 @@ public void runMailTransferScheduled() { } // Only export if we have the condition to - if (!shouldDeliverPackages()) return; - - // Export to station - for (int slot = 0; slot < carriageInventory.getSlots(); slot++) { - ItemStack stack = carriageInventory.getStackInSlot(slot); - if (!PackageItem.isPackage(stack)) - continue; + if (shouldDeliverPackages) { + // Export to station + for (int slot = 0; slot < carriageInventory.getSlots(); slot++) { + ItemStack stack = carriageInventory.getStackInSlot(slot); + if (!PackageItem.isPackage(stack)) + continue; - for (Entry entry : connectedPorts.entrySet()) { - GlobalPackagePort port = entry.getValue(); - BlockPos pos = entry.getKey(); - PostboxBlockEntity box = null; + for (Entry entry : connectedPorts.entrySet()) { + GlobalPackagePort port = entry.getValue(); + BlockPos pos = entry.getKey(); + PostboxBlockEntity box = null; - if (!PackageItem.matchAddress(stack, port.address)) - continue; + if (!PackageItem.matchAddress(stack, port.address)) + continue; - IItemHandler postboxInventory = port.offlineBuffer; - if (level != null && level.isLoaded(pos) - && level.getBlockEntity(pos) instanceof PostboxBlockEntity ppbe) { - postboxInventory = ppbe.inventory; - box = ppbe; - } + IItemHandler postboxInventory = port.offlineBuffer; + if (level != null && level.isLoaded(pos) + && level.getBlockEntity(pos) instanceof PostboxBlockEntity ppbe) { + postboxInventory = ppbe.inventory; + box = ppbe; + } - ItemStack result = ItemHandlerHelper.insertItemStacked(postboxInventory, stack, false); - if (!result.isEmpty()) - continue; + ItemStack result = ItemHandlerHelper.insertItemStacked(postboxInventory, stack, false); + if (!result.isEmpty()) + continue; - Create.RAILWAYS.markTracksDirty(); - carriageInventory.setStackInSlot(slot, ItemStack.EMPTY); - if (box != null) - box.spawnParticles(); + Create.RAILWAYS.markTracksDirty(); + carriageInventory.setStackInSlot(slot, ItemStack.EMPTY); + if (box != null) + box.spawnParticles(); - break; + break; + } } } }