From 2a3a14993213fea7af7f9816e2a7246a76034d92 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Fri, 12 Sep 2025 19:44:14 +0800 Subject: [PATCH 01/15] feat: add basic stock tag system --- .../registry/DefinitionManager.java | 17 ++- .../EntityRollingStockDefinition.java | 9 +- .../immersiverailroading/util/BiMultiMap.java | 113 ++++++++++++++++++ 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 src/main/java/cam72cam/immersiverailroading/util/BiMultiMap.java diff --git a/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java b/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java index 05dc91ccd..b3478d5bc 100644 --- a/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java +++ b/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java @@ -2,6 +2,7 @@ import cam72cam.immersiverailroading.Config.ConfigPerformance; import cam72cam.immersiverailroading.ImmersiveRailroading; +import cam72cam.immersiverailroading.util.BiMultiMap; import cam72cam.immersiverailroading.util.CAML; import cam72cam.immersiverailroading.util.DataBlock; import cam72cam.immersiverailroading.library.Gauge; @@ -23,6 +24,7 @@ public class DefinitionManager { private static Map definitions; + private static BiMultiMap stockTags; private static Map tracks; private static final Map stockLoaders; @@ -240,6 +242,7 @@ private static void initModels() throws IOException { System.out.println("GC"); System.gc(); } + stockDefinition.tags.forEach(tag -> stockTags.put(tag, stockDefinition)); return Pair.of(stockDefinition.defID, stockDefinition); } catch (Exception e) { @@ -255,6 +258,7 @@ private static void initModels() throws IOException { }).filter(Objects::nonNull).collect(Collectors.toMap(Pair::getKey, Pair::getValue)); definitions = new LinkedHashMap<>(); + stockTags = new BiMultiMap<>(); definitionIDMap.keySet().stream().filter(loaded::containsKey).forEach(x -> definitions.put(x, loaded.get(x))); Progress.pop(bar); @@ -372,6 +376,18 @@ public static Set getDefinitionNames() { return definitions.keySet(); } + public static Set getTaggedStocks (String tag) { + return stockTags.getValues(tag); + } + + public static Set getStockTags (EntityRollingStockDefinition def) { + return stockTags.getKeys(def); + } + + public static boolean isTaggedWith (EntityRollingStockDefinition def, String tag) { + return stockTags.containsEntry(tag, def); + } + public static List getTracks() { return new ArrayList<>(tracks.values()); } @@ -396,5 +412,4 @@ public static TrackDefinition getTrack(String track) { private interface StockLoader { EntityRollingStockDefinition apply(String defID, DataBlock data) throws Exception; } - } diff --git a/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java b/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java index b6132e2f8..d0f2b78d3 100644 --- a/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java +++ b/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java @@ -62,6 +62,7 @@ public abstract class EntityRollingStockDefinition { private String name; private String modelerName; private String packName; + protected Set tags; private ValveGearConfig valveGear; public float darken; public Identifier modelLoc; @@ -398,7 +399,13 @@ private DataBlock transformData(DataBlock data) throws IOException { public void loadData(DataBlock data) throws Exception { name = data.getValue("name").asString(); modelerName = data.getValue("modeler").asString(); - packName = data.getValue("pack").asString(); + packName = data.getValue("pack").asString();tags = new HashSet<>(); + tags = new HashSet<>(); + List tagValues = data.getValues("tags"); + if (tagValues != null) { + tagValues.forEach(v -> tags.add(v.asString())); + } + darken = data.getValue("darken_model").asFloat(); internal_model_scale = 1; internal_inv_scale = 1; diff --git a/src/main/java/cam72cam/immersiverailroading/util/BiMultiMap.java b/src/main/java/cam72cam/immersiverailroading/util/BiMultiMap.java new file mode 100644 index 000000000..326538a60 --- /dev/null +++ b/src/main/java/cam72cam/immersiverailroading/util/BiMultiMap.java @@ -0,0 +1,113 @@ +package cam72cam.immersiverailroading.util; + +import java.util.*; +import java.util.Collections; + +public class BiMultiMap { + private final Map> keyToValues = new HashMap<>(); + private final Map> valueToKeys = new HashMap<>(); + + public void put(K key, V value) { + keyToValues.computeIfAbsent(key, k -> new HashSet<>()).add(value); + valueToKeys.computeIfAbsent(value, v -> new HashSet<>()).add(key); + } + + public Set getValues(K key) { + Set values = keyToValues.get(key); + return values != null ? Collections.unmodifiableSet(values) : Collections.emptySet(); + } + + public Set getKeys(V value) { + Set keys = valueToKeys.get(value); + return keys != null ? Collections.unmodifiableSet(keys) : Collections.emptySet(); + } + + public boolean containsKey(K key) { + return keyToValues.containsKey(key); + } + + public boolean containsValue(V value) { + return valueToKeys.containsKey(value); + } + + public boolean containsEntry(K key, V value) { + Set values = keyToValues.get(key); + return values != null && values.contains(value); + } + + public void remove(K key, V value) { + // Remove from key->values mapping + Set values = keyToValues.get(key); + if (values != null) { + values.remove(value); + if (values.isEmpty()) { + keyToValues.remove(key); + } + } + + // Remove from value->keys mapping + Set keys = valueToKeys.get(value); + if (keys != null) { + keys.remove(key); + if (keys.isEmpty()) { + valueToKeys.remove(value); + } + } + } + + public void removeKey(K key) { + Set values = keyToValues.remove(key); + if (values != null) { + for (V value : values) { + Set keys = valueToKeys.get(value); + if (keys != null) { + keys.remove(key); + if (keys.isEmpty()) { + valueToKeys.remove(value); + } + } + } + } + } + + public void removeValue(V value) { + Set keys = valueToKeys.remove(value); + if (keys != null) { + for (K key : keys) { + Set values = keyToValues.get(key); + if (values != null) { + values.remove(value); + if (values.isEmpty()) { + keyToValues.remove(key); + } + } + } + } + } + + public Set keySet() { + return Collections.unmodifiableSet(keyToValues.keySet()); + } + + public Set valueSet() { + return Collections.unmodifiableSet(valueToKeys.keySet()); + } + + public int size() { + return keyToValues.size(); + } + + public boolean isEmpty() { + return keyToValues.isEmpty(); + } + + public void clear() { + keyToValues.clear(); + valueToKeys.clear(); + } + + @Override + public String toString() { + return keyToValues.toString(); + } +} \ No newline at end of file From 4f0b776df1ad52c04a47eb0751eb41e674752023 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sat, 13 Sep 2025 15:07:27 +0800 Subject: [PATCH 02/15] feat: add augment gui --- .../ImmersiveRailroading.java | 2 + .../gui/AugmentFilterGUI.java | 76 +++++++++++++++++++ .../items/ItemRollingStock.java | 3 +- .../library/GuiTypes.java | 2 + .../registry/DefinitionManager.java | 6 +- .../tile/TileRailBase.java | 68 ++++++++++++++--- 6 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java diff --git a/src/main/java/cam72cam/immersiverailroading/ImmersiveRailroading.java b/src/main/java/cam72cam/immersiverailroading/ImmersiveRailroading.java index fe120fa0a..e6d8e6f78 100644 --- a/src/main/java/cam72cam/immersiverailroading/ImmersiveRailroading.java +++ b/src/main/java/cam72cam/immersiverailroading/ImmersiveRailroading.java @@ -2,6 +2,7 @@ import cam72cam.immersiverailroading.entity.*; import cam72cam.immersiverailroading.entity.physics.chrono.ServerChronoState; +import cam72cam.immersiverailroading.gui.AugmentFilterGUI; import cam72cam.immersiverailroading.gui.overlay.GuiBuilder; import cam72cam.immersiverailroading.items.ItemPaintBrush; import cam72cam.immersiverailroading.library.GuiTypes; @@ -86,6 +87,7 @@ public void commonEvent(ModEvent event) { Packet.register(ClientPartDragging.SeatPacket::new, PacketDirection.ClientToServer); Packet.register(GuiBuilder.ControlChangePacket::new, PacketDirection.ClientToServer); Packet.register(ItemPaintBrush.PaintBrushPacket::new, PacketDirection.ClientToServer); + Packet.register(AugmentFilterGUI.AugmentFilterChangePacket::new, PacketDirection.ClientToServer); ServerChronoState.register(); diff --git a/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java new file mode 100644 index 000000000..e946f3b0f --- /dev/null +++ b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java @@ -0,0 +1,76 @@ +package cam72cam.immersiverailroading.gui; + +import cam72cam.immersiverailroading.tile.TileRailBase; +import cam72cam.mod.gui.helpers.GUIHelpers; +import cam72cam.mod.gui.screen.IScreen; +import cam72cam.mod.gui.screen.IScreenBuilder; +import cam72cam.mod.gui.screen.TextField; +import cam72cam.mod.math.Vec3i; +import cam72cam.mod.net.Packet; +import cam72cam.mod.render.opengl.RenderState; +import cam72cam.mod.serialization.TagField; + +public class AugmentFilterGUI implements IScreen { + private final Vec3i pos; + private TextField textField; + private String filter; + + public AugmentFilterGUI(TileRailBase tileRailBase) { + this.pos = tileRailBase.getPos(); + this.filter = tileRailBase.getAugmentFilter(); + } + + @Override + public void init(IScreenBuilder screen) { + textField = new TextField(screen, -GUIHelpers.getScreenWidth() / 4, -(GUIHelpers.getScreenHeight() / 8), + 200 - 1, 20); + textField.setText(filter); + textField.setFocused(true); + textField.setValidator(s -> { + filter = s; + return true; + }); + } + + @Override + public void onEnterKey(IScreenBuilder builder) { + builder.close(); + } + + @Override + public void onClose() { + String s = textField.getText(); + new AugmentFilterChangePacket(pos, s).sendToServer(); + } + + @Override + public void draw(IScreenBuilder builder, RenderState state) { + IScreen.super.draw(builder, state); + textField.setText(filter); + GUIHelpers.drawRect(0, 0, GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight(), 0x88000000); + } + + public static class AugmentFilterChangePacket extends Packet { + @TagField + Vec3i pos; + + @TagField + String filter; + + public AugmentFilterChangePacket() { + } + + public AugmentFilterChangePacket(Vec3i pos, String filter) { + this.pos = pos; + this.filter = filter; + } + + @Override + protected void handle() { + TileRailBase railBase = this.getWorld().getBlockEntity(pos, TileRailBase.class); + if (railBase != null && railBase.getAugment() != null) { + railBase.setAugmentFilter(filter); + } + } + } +} diff --git a/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java b/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java index 2011b4ac9..99977560b 100644 --- a/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java +++ b/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java @@ -150,7 +150,8 @@ public ClickResult onClickBlock(Player player, World world, Vec3i pos, Player.Ha case ITEM_UNLOADER: if (world.isServer) { Data data = new Data(player.getHeldItem(hand)); - boolean set = te.setAugmentFilter(data.def != null ? data.def.defID : null); + //TODO + boolean set = te.setAugmentFilter(data.def != null ? data.def.defID : ""); if (set) { player.sendMessage(ChatText.SET_AUGMENT_FILTER.getMessage(data.def != null ? data.def.name() : "Unknown")); } else { diff --git a/src/main/java/cam72cam/immersiverailroading/library/GuiTypes.java b/src/main/java/cam72cam/immersiverailroading/library/GuiTypes.java index a9f2a0e0e..2ad837396 100644 --- a/src/main/java/cam72cam/immersiverailroading/library/GuiTypes.java +++ b/src/main/java/cam72cam/immersiverailroading/library/GuiTypes.java @@ -7,6 +7,7 @@ import cam72cam.immersiverailroading.multiblock.CastingMultiblock; import cam72cam.immersiverailroading.multiblock.PlateRollerMultiblock; import cam72cam.immersiverailroading.tile.TileMultiblock; +import cam72cam.immersiverailroading.tile.TileRailBase; import cam72cam.immersiverailroading.tile.TileRailPreview; import cam72cam.mod.config.ConfigGui; import cam72cam.mod.gui.GuiRegistry; @@ -25,6 +26,7 @@ public class GuiTypes { public static final GUI RAIL = GuiRegistry.register(new Identifier(ImmersiveRailroading.MODID, "RAIL"), TrackGui::new); public static final BlockGUI RAIL_PREVIEW = GuiRegistry.registerBlock(TileRailPreview.class, TrackGui::new); + public static final BlockGUI RAIL_AUGMENT = GuiRegistry.registerBlock(TileRailBase.class, AugmentFilterGUI::new); public static final GUI TRACK_EXCHANGER = GuiRegistry.register(new Identifier(ImmersiveRailroading.MODID, "TRACK_EXCHANGER"), TrackExchangerGui::new); public static final GUI PAINT_BRUSH = GuiRegistry.register(new Identifier(ImmersiveRailroading.MODID, "PAINT_BRUSH"), PaintBrushPicker::new); diff --git a/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java b/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java index b3478d5bc..5147114e9 100644 --- a/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java +++ b/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java @@ -376,15 +376,15 @@ public static Set getDefinitionNames() { return definitions.keySet(); } - public static Set getTaggedStocks (String tag) { + public static Set getTaggedStocks(String tag) { return stockTags.getValues(tag); } - public static Set getStockTags (EntityRollingStockDefinition def) { + public static Set getStockTags(EntityRollingStockDefinition def) { return stockTags.getKeys(def); } - public static boolean isTaggedWith (EntityRollingStockDefinition def, String tag) { + public static boolean isTaggedWith(EntityRollingStockDefinition def, String tag) { return stockTags.containsEntry(tag, def); } diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index ae616156c..22c32ed4f 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -14,6 +14,7 @@ import cam72cam.immersiverailroading.library.*; import cam72cam.immersiverailroading.model.part.Door; import cam72cam.immersiverailroading.physics.MovementTrack; +import cam72cam.immersiverailroading.registry.DefinitionManager; import cam72cam.immersiverailroading.thirdparty.trackapi.BlockEntityTrackTickable; import cam72cam.immersiverailroading.util.*; import cam72cam.mod.block.IRedstoneProvider; @@ -35,7 +36,9 @@ import cam72cam.mod.util.SingleCache; import org.apache.commons.lang3.ArrayUtils; +import javax.annotation.Nonnull; import java.util.*; +import java.util.function.Predicate; public class TileRailBase extends BlockEntityTrackTickable implements IRedstoneProvider { @TagField("parent") @@ -46,8 +49,9 @@ public class TileRailBase extends BlockEntityTrackTickable implements IRedstoneP private float railHeight = 0; @TagField("augment") private Augment augment; - @TagField("augmentFilterID") - private String augmentFilterID; + @TagField("augment_filter") + private String filter; + private final List> compiledFilter = new ArrayList<>(); @TagField("snowLayers") private int snowLayers = 0; @TagField("flexible") @@ -123,18 +127,53 @@ public void setAugment(Augment augment) { } } } - setAugmentFilter(null); + setAugmentFilter(""); redstoneMode = RedstoneMode.ENABLED; this.markDirty(); } - public boolean setAugmentFilter(String definitionID) { - if (definitionID != null && !definitionID.equals(augmentFilterID)) { - this.augmentFilterID = definitionID; - } else { - this.augmentFilterID = null; - } + + //TODO + public boolean setAugmentFilter(@Nonnull String input) { + this.filter = input; + this.markDirty(); - return this.augmentFilterID != null; + return !this.compiledFilter.isEmpty(); + } + + public void compileFilter() { + compiledFilter.clear(); + + String[] filters = filter.split(" "); + for (String str : filters) { + str = str.trim(); + if (str.startsWith("type:")) { + switch (str.substring(5)) { + case "diesel": + compiledFilter.add(s -> s instanceof LocomotiveDiesel); + break; + case "steam": + compiledFilter.add(s -> s instanceof LocomotiveSteam); + break; + case "handcar": + compiledFilter.add(s -> s instanceof HandCar); + break; + case "passenger": + compiledFilter.add(s -> s instanceof CarPassenger); + break; + case "tender": + compiledFilter.add(s -> s instanceof Tender); + break; + case "tank": + compiledFilter.add(s -> s instanceof CarTank); + break; + case "freight": + compiledFilter.add(s -> s instanceof CarFreight); + } + } else if (str.startsWith("tag:")) { + String tag = str.substring(4); + compiledFilter.add(s -> DefinitionManager.isTaggedWith(s.getDefinition(), tag)); + } + } } public PlayerMessage nextAugmentRedstoneMode(boolean isPiston) { @@ -172,6 +211,9 @@ public PlayerMessage nextAugmentRedstoneMode(boolean isPiston) { public Augment getAugment() { return this.augment; } + public String getAugmentFilter() { + return filter; + } public int getSnowLayers() { return this.snowLayers; } @@ -460,7 +502,7 @@ public T getStockNearBy(Class type){ if (overhead == null) { return null; } - if (augmentFilterID != null && !augmentFilterID.equals(overhead.getDefinitionID())) { + if (!compiledFilter.isEmpty() && compiledFilter.stream().noneMatch(p -> p.test(overhead))) { return null; } if (stockTag != null && !stockTag.equals(overhead.tag)) { @@ -888,6 +930,10 @@ public void onBreak() { @Override public boolean onClick(Player player, Player.Hand hand, Facing facing, Vec3d hit) { + if (this.augment != null) { + GuiTypes.RAIL_AUGMENT.open(player, this.getPos()); + return true; + } ItemStack stack = player.getHeldItem(hand); if (stack.is(IRItems.ITEM_TRACK_EXCHANGER) && player.hasPermission(Permissions.EXCHANGE_TRACK)) { TileRail tileRail = this.getParentTile(); From 74e1c31c5e742cbe2df0dba42295624e80cc07ad Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 14 Sep 2025 10:41:52 +0800 Subject: [PATCH 03/15] feat: rework augment filter --- .../gui/AugmentFilterGUI.java | 148 ++++++++++-- .../items/ItemRollingStock.java | 31 +-- .../immersiverailroading/library/Augment.java | 76 ++++++ .../EntityRollingStockDefinition.java | 8 +- .../registry/LocomotiveDefinition.java | 2 +- .../tile/TileRailBase.java | 222 +++++++++++------- 6 files changed, 349 insertions(+), 138 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java index e946f3b0f..5bc7103b2 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java @@ -1,35 +1,124 @@ package cam72cam.immersiverailroading.gui; +import cam72cam.immersiverailroading.library.*; import cam72cam.immersiverailroading.tile.TileRailBase; +import cam72cam.mod.entity.Player; import cam72cam.mod.gui.helpers.GUIHelpers; -import cam72cam.mod.gui.screen.IScreen; -import cam72cam.mod.gui.screen.IScreenBuilder; -import cam72cam.mod.gui.screen.TextField; +import cam72cam.mod.gui.screen.*; import cam72cam.mod.math.Vec3i; import cam72cam.mod.net.Packet; import cam72cam.mod.render.opengl.RenderState; import cam72cam.mod.serialization.TagField; +import cam72cam.mod.text.TextUtil; + +import java.util.function.Function; + +import static cam72cam.immersiverailroading.gui.ClickListHelper.next; public class AugmentFilterGUI implements IScreen { private final Vec3i pos; - private TextField textField; - private String filter; + private final Augment augment; + private final Augment.Properties properties; + private TextField includeTags; + private TextField excludeTags; + private Button stockDetectorMode; + private Button redstoneMode; + private CheckBox pushpull; + private Button couplerMode; + private Button locoControlMode; public AugmentFilterGUI(TileRailBase tileRailBase) { this.pos = tileRailBase.getPos(); - this.filter = tileRailBase.getAugmentFilter(); + this.augment = tileRailBase.getAugment(); + this.properties = tileRailBase.getAugmentProperties() == null + ? Augment.Properties.EMPTY + : tileRailBase.getAugmentProperties(); } @Override public void init(IScreenBuilder screen) { - textField = new TextField(screen, -GUIHelpers.getScreenWidth() / 4, -(GUIHelpers.getScreenHeight() / 8), - 200 - 1, 20); - textField.setText(filter); - textField.setFocused(true); - textField.setValidator(s -> { - filter = s; + int xtop = -GUIHelpers.getScreenWidth() / 2; + int ytop = -GUIHelpers.getScreenHeight() / 4; + + int xOffset = 0; + int yOffset = 40; + + includeTags = new TextField(screen, xtop + xOffset, ytop + yOffset, + 200-1, 20); + includeTags.setText(properties.positiveFilter); + includeTags.setValidator(s -> { + properties.positiveFilter = s; + return true; + }); + yOffset += 40; + + excludeTags = new TextField(screen, xtop + xOffset, ytop + yOffset, + 200-1, 20); + excludeTags.setText(properties.negativeFilter); + excludeTags.setValidator(s -> { + properties.negativeFilter = s; return true; }); + yOffset += 25; + + Function, String> translate = e -> TextUtil.translate(e.toString()); + + stockDetectorMode = new Button(screen, xtop + xOffset, ytop + yOffset, "Stock Detect Mode: " + translate.apply(properties.stockDetectorMode)) { + @Override + public void onClick(Player.Hand hand) { + properties.stockDetectorMode = next(properties.stockDetectorMode, Player.Hand.PRIMARY); + stockDetectorMode.setText("Detecting: " + translate.apply(properties.stockDetectorMode)); + } + }; + stockDetectorMode.setEnabled(this.augment == Augment.DETECTOR); + yOffset += 25; + + redstoneMode = new Button(screen, xtop + xOffset, ytop + yOffset, "Redstone Mode: " + translate.apply(properties.redstoneMode)) { + @Override + public void onClick(Player.Hand hand) { + properties.redstoneMode = next(properties.redstoneMode, Player.Hand.PRIMARY); + redstoneMode.setText("Redstone Mode: " + translate.apply(properties.redstoneMode)); + } + }; + redstoneMode.setEnabled(this.augment == Augment.COUPLER + || this.augment == Augment.ITEM_LOADER + || this.augment == Augment.ITEM_UNLOADER + || this.augment == Augment.FLUID_LOADER + || this.augment == Augment.FLUID_UNLOADER); + yOffset += 25; + + pushpull = new CheckBox(screen, xtop + xOffset, ytop + yOffset, "Enable Pushpull", properties.pushpull) { + @Override + public void onClick(Player.Hand hand) { + properties.pushpull = !properties.pushpull; + pushpull.setChecked(properties.pushpull); + } + }; + pushpull.setEnabled(this.augment == Augment.COUPLER + || this.augment == Augment.ITEM_LOADER + || this.augment == Augment.ITEM_UNLOADER + || this.augment == Augment.FLUID_LOADER + || this.augment == Augment.FLUID_UNLOADER); + yOffset += 25; + + couplerMode = new Button(screen, xtop + xOffset, ytop + yOffset, "Coupler Mode: " + translate.apply(properties.couplerAugmentMode)) { + @Override + public void onClick(Player.Hand hand) { + properties.couplerAugmentMode = next(properties.couplerAugmentMode, Player.Hand.PRIMARY); + couplerMode.setText("Coupler Mode: " + translate.apply(properties.couplerAugmentMode)); + } + }; + couplerMode.setEnabled(this.augment == Augment.COUPLER); + yOffset += 25; + + locoControlMode = new Button(screen, xtop + xOffset, ytop + yOffset, "Locomotive Control Mode: " + translate.apply(properties.locoControlMode)) { + @Override + public void onClick(Player.Hand hand) { + properties.locoControlMode = next(properties.locoControlMode, Player.Hand.PRIMARY); + locoControlMode.setText("Locomotive Control Mode: " + translate.apply(properties.locoControlMode)); + } + }; + locoControlMode.setEnabled(this.augment == Augment.LOCO_CONTROL); } @Override @@ -39,15 +128,36 @@ public void onEnterKey(IScreenBuilder builder) { @Override public void onClose() { - String s = textField.getText(); - new AugmentFilterChangePacket(pos, s).sendToServer(); + if(properties.positiveFilter == null) { + properties.positiveFilter = ""; + } + if (properties.negativeFilter == null) { + properties.negativeFilter = ""; + } + new AugmentFilterChangePacket(pos, properties).sendToServer(); } @Override public void draw(IScreenBuilder builder, RenderState state) { IScreen.super.draw(builder, state); - textField.setText(filter); + GUIHelpers.drawRect(0, 0, GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight(), 0x88000000); + + GUIHelpers.drawRect(0, 0, 200, GUIHelpers.getScreenHeight(), 0xCC000000); + + int xtop = -GUIHelpers.getScreenWidth() / 2; + int ytop = -GUIHelpers.getScreenHeight() / 4; + + int xOffset = 100; + int yOffset = 30; + GUIHelpers.drawCenteredString("Current Augment: " + TextUtil.translate("item.immersiverailroading:item_augment." + this.augment.toString() + ".name"), xOffset, 10, 0xFFFFFFFF); + + GUIHelpers.drawCenteredString("Included Tags", xOffset, yOffset, 0xFFFFFFFF); + includeTags.setText(properties.positiveFilter); + yOffset+=40; + + GUIHelpers.drawCenteredString("Excluded Tags", xOffset, yOffset, 0xFFFFFFFF); + excludeTags.setText(properties.negativeFilter); } public static class AugmentFilterChangePacket extends Packet { @@ -55,21 +165,21 @@ public static class AugmentFilterChangePacket extends Packet { Vec3i pos; @TagField - String filter; + Augment.Properties properties; public AugmentFilterChangePacket() { } - public AugmentFilterChangePacket(Vec3i pos, String filter) { + public AugmentFilterChangePacket(Vec3i pos, Augment.Properties filter) { this.pos = pos; - this.filter = filter; + this.properties = filter; } @Override protected void handle() { TileRailBase railBase = this.getWorld().getBlockEntity(pos, TileRailBase.class); if (railBase != null && railBase.getAugment() != null) { - railBase.setAugmentFilter(filter); + railBase.setAugmentProperties(properties); } } } diff --git a/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java b/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java index 99977560b..9ad07607a 100644 --- a/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java +++ b/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java @@ -138,32 +138,15 @@ public List getTooltip(ItemStack stack) @Override public ClickResult onClickBlock(Player player, World world, Vec3i pos, Player.Hand hand, Facing facing, Vec3d hit) { - if (BlockUtil.isIRRail(world, pos)) { - TileRailBase te = world.getBlockEntity(pos, TileRailBase.class); - if (te.getAugment() != null) { - switch(te.getAugment()) { - case DETECTOR: - case LOCO_CONTROL: - case FLUID_LOADER: - case FLUID_UNLOADER: - case ITEM_LOADER: - case ITEM_UNLOADER: - if (world.isServer) { - Data data = new Data(player.getHeldItem(hand)); - //TODO - boolean set = te.setAugmentFilter(data.def != null ? data.def.defID : ""); - if (set) { - player.sendMessage(ChatText.SET_AUGMENT_FILTER.getMessage(data.def != null ? data.def.name() : "Unknown")); - } else { - player.sendMessage(ChatText.RESET_AUGMENT_FILTER.getMessage()); - } - } - return ClickResult.ACCEPTED; - default: - break; - } + if(world.isServer && BlockUtil.isIRRail(world, pos)) { + TileRailBase base = world.getBlockEntity(pos, TileRailBase.class); + //TODO + if (base.getAugment() != null) { + Augment.Properties properties = base.getAugmentProperties(); +// properties.positiveFilter += } } + return tryPlaceStock(player, world, pos, hand, null); } diff --git a/src/main/java/cam72cam/immersiverailroading/library/Augment.java b/src/main/java/cam72cam/immersiverailroading/library/Augment.java index edd68abe2..9d91bc9b1 100644 --- a/src/main/java/cam72cam/immersiverailroading/library/Augment.java +++ b/src/main/java/cam72cam/immersiverailroading/library/Augment.java @@ -1,6 +1,10 @@ package cam72cam.immersiverailroading.library; import cam72cam.mod.render.Color; +import cam72cam.mod.serialization.SerializationException; +import cam72cam.mod.serialization.TagCompound; +import cam72cam.mod.serialization.TagField; +import cam72cam.mod.serialization.TagMapped; public enum Augment { SPEED_RETARDER, @@ -40,4 +44,76 @@ public Color color() { } return Color.WHITE; } + + @TagMapped(PropertyMapper.class) + public static class Properties { + public static final Properties EMPTY = new Properties("", "", + CouplerAugmentMode.ENGAGED, + LocoControlMode.THROTTLE, + RedstoneMode.ENABLED, + true, + StockDetectorMode.SIMPLE); + + public String positiveFilter; + public String negativeFilter; + public CouplerAugmentMode couplerAugmentMode; + public LocoControlMode locoControlMode; + public RedstoneMode redstoneMode; + public boolean pushpull; + public StockDetectorMode stockDetectorMode; + + public Properties() { + } + + public Properties(String positiveFilter, String negativeFilter, CouplerAugmentMode couplerAugmentMode, + LocoControlMode locoControlMode, RedstoneMode redstoneMode, boolean pushpull, StockDetectorMode stockDetectorMode) { + this.positiveFilter = positiveFilter; + this.negativeFilter = negativeFilter; + this.couplerAugmentMode = couplerAugmentMode; + this.locoControlMode = locoControlMode; + this.redstoneMode = redstoneMode; + this.pushpull = pushpull; + this.stockDetectorMode = stockDetectorMode; + } + + public TagCompound toNBT() { + TagCompound compound = new TagCompound(); + compound.setString("positive", positiveFilter); + compound.setString("negative", negativeFilter); + compound.setString("coupler", couplerAugmentMode.name()); + compound.setString("loco", locoControlMode.name()); + compound.setString("redstone", redstoneMode.name()); + compound.setBoolean("pushpull", pushpull); + compound.setString("detector", stockDetectorMode.name()); + return compound; + } + + public static Properties fromNBT(TagCompound compound) { + Properties properties = new Properties( + compound.getString("positive"), + compound.getString("negative"), + CouplerAugmentMode.valueOf(compound.getString("coupler")), + LocoControlMode.valueOf(compound.getString("loco")), + RedstoneMode.valueOf(compound.getString("redstone")), + compound.getBoolean("pushpull"), + StockDetectorMode.valueOf(compound.getString("detector"))); + return properties; + } + } + + public static class PropertyMapper implements cam72cam.mod.serialization.TagMapper { + @Override + public TagAccessor apply(Class type, String fieldName, TagField tag) throws SerializationException { + return new TagAccessor<>( + (t, p) -> { + if (p == null) { + t.remove(fieldName); + } else { + t.set(fieldName, p.toNBT()); + } + }, + t -> t.hasKey(fieldName) ? Properties.fromNBT(t.get(fieldName)) : Properties.EMPTY + ); + } + } } diff --git a/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java b/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java index d0f2b78d3..4294081a1 100644 --- a/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java +++ b/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java @@ -59,9 +59,9 @@ public abstract class EntityRollingStockDefinition { public Identifier collision_sound; public double flange_min_yaw; double internal_inv_scale; - private String name; - private String modelerName; - private String packName; + public String name; + public String modelerName; + public String packName; protected Set tags; private ValveGearConfig valveGear; public float darken; @@ -399,7 +399,7 @@ private DataBlock transformData(DataBlock data) throws IOException { public void loadData(DataBlock data) throws Exception { name = data.getValue("name").asString(); modelerName = data.getValue("modeler").asString(); - packName = data.getValue("pack").asString();tags = new HashSet<>(); + packName = data.getValue("pack").asString(); tags = new HashSet<>(); List tagValues = data.getValues("tags"); if (tagValues != null) { diff --git a/src/main/java/cam72cam/immersiverailroading/registry/LocomotiveDefinition.java b/src/main/java/cam72cam/immersiverailroading/registry/LocomotiveDefinition.java index f7ef650ad..d976d135c 100644 --- a/src/main/java/cam72cam/immersiverailroading/registry/LocomotiveDefinition.java +++ b/src/main/java/cam72cam/immersiverailroading/registry/LocomotiveDefinition.java @@ -15,7 +15,7 @@ public abstract class LocomotiveDefinition extends FreightDefinition { public boolean toggleBell; public SoundDefinition bell; - private String works; + public String works; private double power; private double traction; private Speed maxSpeed; diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index 22c32ed4f..9582972c5 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -29,7 +29,6 @@ import cam72cam.mod.sound.Audio; import cam72cam.mod.sound.SoundCategory; import cam72cam.mod.sound.StandardSound; -import cam72cam.mod.text.PlayerMessage; import cam72cam.mod.util.Facing; import cam72cam.mod.serialization.TagCompound; import cam72cam.immersiverailroading.thirdparty.trackapi.ITrack; @@ -49,9 +48,11 @@ public class TileRailBase extends BlockEntityTrackTickable implements IRedstoneP private float railHeight = 0; @TagField("augment") private Augment augment; - @TagField("augment_filter") - private String filter; - private final List> compiledFilter = new ArrayList<>(); + @TagField("positive_filter") + private String positive; + @TagField("negative_filter") + private String negative; + private List> compiledFilter = new LinkedList<>(); @TagField("snowLayers") private int snowLayers = 0; @TagField("flexible") @@ -65,13 +66,13 @@ public class TileRailBase extends BlockEntityTrackTickable implements IRedstoneP private final IInventory emptyInventory = new ItemStackHandler(0); private int redstoneLevel = 0; @TagField("redstoneMode") - private StockDetectorMode detectorMode = StockDetectorMode.SIMPLE; + private StockDetectorMode detectorMode; @TagField("controlMode") - private LocoControlMode controlMode = LocoControlMode.THROTTLE; + private LocoControlMode controlMode; @TagField("couplerMod") - private CouplerAugmentMode couplerMode = CouplerAugmentMode.ENGAGED; + private CouplerAugmentMode couplerMode; @TagField("redstoneSensitivity") - private RedstoneMode redstoneMode = RedstoneMode.ENABLED; + private RedstoneMode redstoneMode; private int ticksExisted; public boolean blockUpdate; private Gauge augmentGauge; @@ -114,105 +115,151 @@ public float getRailHeight() { public void setAugment(Augment augment) { this.augment = augment; + Augment.Properties properties = new Augment.Properties("", "", + CouplerAugmentMode.ENGAGED, + LocoControlMode.THROTTLE, + RedstoneMode.ENABLED, + true, + StockDetectorMode.SIMPLE); if (getParentTile() != null) { augmentGauge = getParentTile().info.settings.gauge; if (ConfigDebug.defaultAugmentComputer && augment != null) { switch (augment) { case DETECTOR: - detectorMode = StockDetectorMode.COMPUTER; + properties.stockDetectorMode = StockDetectorMode.COMPUTER; break; case LOCO_CONTROL: - controlMode = LocoControlMode.COMPUTER; + properties.locoControlMode = LocoControlMode.COMPUTER; break; } } } - setAugmentFilter(""); - redstoneMode = RedstoneMode.ENABLED; + properties.redstoneMode = RedstoneMode.ENABLED; + setAugmentProperties(properties); this.markDirty(); } - //TODO - public boolean setAugmentFilter(@Nonnull String input) { - this.filter = input; + public void setAugmentProperties(@Nonnull Augment.Properties properties) { + this.positive = properties.positiveFilter; + this.negative = properties.negativeFilter; + this.couplerMode = properties.couplerAugmentMode; + this.controlMode = properties.locoControlMode; + this.redstoneMode = properties.redstoneMode; + this.pushPull = properties.pushpull; + this.detectorMode = properties.stockDetectorMode; + this.compileFilter(); this.markDirty(); - return !this.compiledFilter.isEmpty(); } public void compileFilter() { - compiledFilter.clear(); - - String[] filters = filter.split(" "); - for (String str : filters) { - str = str.trim(); - if (str.startsWith("type:")) { - switch (str.substring(5)) { - case "diesel": - compiledFilter.add(s -> s instanceof LocomotiveDiesel); - break; - case "steam": - compiledFilter.add(s -> s instanceof LocomotiveSteam); - break; - case "handcar": - compiledFilter.add(s -> s instanceof HandCar); - break; - case "passenger": - compiledFilter.add(s -> s instanceof CarPassenger); - break; - case "tender": - compiledFilter.add(s -> s instanceof Tender); - break; - case "tank": - compiledFilter.add(s -> s instanceof CarTank); - break; - case "freight": - compiledFilter.add(s -> s instanceof CarFreight); + List> list = new LinkedList<>(); + + if (positive != null && !positive.isEmpty()) { + String[] positiveFilters = positive.split(","); + for (String str : positiveFilters) { + str = str.trim(); + if (str.startsWith("type:")) { + switch (str.substring(5)) { + case "diesel": + list.add(s -> s instanceof LocomotiveDiesel); + break; + case "steam": + list.add(s -> s instanceof LocomotiveSteam); + break; + case "handcar": + list.add(s -> s instanceof HandCar); + break; + case "passenger": + list.add(s -> s instanceof CarPassenger); + break; + case "tender": + list.add(s -> s instanceof Tender); + break; + case "tank": + list.add(s -> s instanceof CarTank); + break; + case "freight": + list.add(s -> s instanceof CarFreight); + } + } else if (str.startsWith("tag:")) { + String tag = str.substring(4); + list.add(s -> DefinitionManager.isTaggedWith(s.getDefinition(), tag)); + } else if (str.startsWith("stock:")) { + String defID = str.substring(6); + list.add(s -> s.getDefinitionID().split("/")[2] + .replace(".json", "").replace(".caml", "").equals(defID)); + } else if (str.startsWith("works:")) { + String works = str.substring(6); + list.add(s -> s instanceof Locomotive && ((Locomotive) s).getDefinition().works.equals(works)); + } else if (str.startsWith("author:")) { + String author = str.substring(7); + list.add(s -> s.getDefinition().modelerName.equals(author)); + } else if (str.startsWith("pack:")) { + String pack = str.substring(5); + list.add(s -> s.getDefinition().packName.equals(pack)); } - } else if (str.startsWith("tag:")) { - String tag = str.substring(4); - compiledFilter.add(s -> DefinitionManager.isTaggedWith(s.getDefinition(), tag)); } + } else { + list.add(s -> true); } - } - public PlayerMessage nextAugmentRedstoneMode(boolean isPiston) { - if (this.augment == null) { - return null; - } - switch (this.augment) { - case DETECTOR: - detectorMode = StockDetectorMode.values()[((detectorMode.ordinal() + 1) % (StockDetectorMode.values().length))]; - return PlayerMessage.translate(detectorMode.toString()); - case LOCO_CONTROL: - controlMode = LocoControlMode.values()[((controlMode.ordinal() + 1) % (LocoControlMode.values().length))]; - return PlayerMessage.translate(controlMode.toString()); - case COUPLER: - if (isPiston) { - couplerMode = CouplerAugmentMode.values()[((couplerMode.ordinal() + 1) % (CouplerAugmentMode.values().length))]; - return PlayerMessage.translate(couplerMode.toString()); - } - // Fall through to redstone control setting - case ITEM_LOADER: - case ITEM_UNLOADER: - case FLUID_LOADER: - case FLUID_UNLOADER: - if (isPiston) { - this.pushPull = !this.pushPull; - return PlayerMessage.translate("immersiverailroading:augment.pushpull." + (this.pushPull ? "enabled" : "disabled")); - } else { - this.redstoneMode = RedstoneMode.values()[(redstoneMode.ordinal() + 1) % RedstoneMode.values().length]; - return PlayerMessage.translate(redstoneMode.toString()); + if (negative != null && !negative.isEmpty()) { + String[] negativeFilters = negative.split(","); + for (String str : negativeFilters) { + str = str.trim(); + if (str.startsWith("type:")) { + switch (str.substring(5)) { + case "diesel": + list.add(s -> !(s instanceof LocomotiveDiesel)); + break; + case "steam": + list.add(s -> !(s instanceof LocomotiveSteam)); + break; + case "handcar": + list.add(s -> !(s instanceof HandCar)); + break; + case "passenger": + list.add(s -> !(s instanceof CarPassenger)); + break; + case "tender": + list.add(s -> !(s instanceof Tender)); + break; + case "tank": + list.add(s -> !(s instanceof CarTank)); + break; + case "freight": + list.add(s -> !(s instanceof CarFreight)); + } + } else if (str.startsWith("tag:")) { + String tag = str.substring(4); + list.add(s -> !DefinitionManager.isTaggedWith(s.getDefinition(), tag)); + } else if (str.startsWith("stock:")) { + String defID = str.substring(6); + list.add(s -> !s.getDefinitionID().split("/")[2] + .replace(".json", "").replace(".caml", "").equals(defID)); + } else if (str.startsWith("works:")) { + String works = str.substring(6); + list.add(s -> !(s instanceof Locomotive && ((Locomotive) s).getDefinition().works.equals(works))); + } else if (str.startsWith("author:")) { + String author = str.substring(7); + list.add(s -> !s.getDefinition().modelerName.equals(author)); + } else if (str.startsWith("pack:")) { + String pack = str.substring(5); + list.add(s -> !s.getDefinition().packName.equals(pack)); } - default: - return null; + } + } else { + list.add(s -> true); } + + compiledFilter = list; } public Augment getAugment() { return this.augment; } - public String getAugmentFilter() { - return filter; + public Augment.Properties getAugmentProperties() { + return new Augment.Properties(positive, negative, couplerMode, controlMode, redstoneMode, pushPull, detectorMode); } public int getSnowLayers() { return this.snowLayers; @@ -311,6 +358,7 @@ public void load(TagCompound nbt) { } } } + this.compileFilter(); } @Override public void save(TagCompound nbt) { @@ -502,7 +550,7 @@ public T getStockNearBy(Class type){ if (overhead == null) { return null; } - if (!compiledFilter.isEmpty() && compiledFilter.stream().noneMatch(p -> p.test(overhead))) { + if (!compiledFilter.isEmpty() && !compiledFilter.stream().allMatch(p -> p.test(overhead))) { return null; } if (stockTag != null && !stockTag.equals(overhead.tag)) { @@ -587,7 +635,7 @@ public void update() { if (ticksExisted > 5 && blockUpdate || (ticksExisted % (20 * 5) == 0 && ticksExisted > (20 * 20))) { // Double check every 5 seconds that the master is not gone - // Wont fire on first due to incr above + // Won't fire on first due to incr above blockUpdate = false; if (this.getParent() == null || !getWorld().isBlockLoaded(this.getParent())) { @@ -930,10 +978,11 @@ public void onBreak() { @Override public boolean onClick(Player player, Player.Hand hand, Facing facing, Vec3d hit) { - if (this.augment != null) { + if (this.augment != null && player.hasPermission(Permissions.AUGMENT_TRACK)) { GuiTypes.RAIL_AUGMENT.open(player, this.getPos()); return true; } + ItemStack stack = player.getHeldItem(hand); if (stack.is(IRItems.ITEM_TRACK_EXCHANGER) && player.hasPermission(Permissions.EXCHANGE_TRACK)) { TileRail tileRail = this.getParentTile(); @@ -969,6 +1018,7 @@ public boolean onClick(Player player, Player.Hand hand, Facing facing, Vec3d hit } return true; } + if (stack.is(Fuzzy.NAME_TAG) && player.hasPermission(Permissions.AUGMENT_TRACK)) { if (getWorld().isServer) { if (player.isCrouching()) { @@ -981,15 +1031,7 @@ public boolean onClick(Player player, Player.Hand hand, Facing facing, Vec3d hit } return true; } - if (player.hasPermission(Permissions.AUGMENT_TRACK) && (stack.is(Fuzzy.REDSTONE_TORCH) || stack.is(Fuzzy.REDSTONE_DUST) || stack.is(Fuzzy.PISTON))) { - PlayerMessage next = this.nextAugmentRedstoneMode(stack.is(Fuzzy.PISTON)); - if (next != null) { - if (this.getWorld().isServer) { - player.sendMessage(next); - } - return true; - } - } + if (stack.is(Fuzzy.SNOW_LAYER)) { if (this.getWorld().isServer) { this.handleSnowTick(); From 32700e33b923023741f71a18507a6c8798ac3adc Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 14 Sep 2025 11:22:33 +0800 Subject: [PATCH 04/15] fix: add filter for speed retarder --- .../entity/EntityMoveableRollingStock.java | 2 +- .../immersiverailroading/gui/AugmentFilterGUI.java | 2 +- .../immersiverailroading/tile/TileRailBase.java | 10 ++++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/entity/EntityMoveableRollingStock.java b/src/main/java/cam72cam/immersiverailroading/entity/EntityMoveableRollingStock.java index 667bd7252..02d14887a 100644 --- a/src/main/java/cam72cam/immersiverailroading/entity/EntityMoveableRollingStock.java +++ b/src/main/java/cam72cam/immersiverailroading/entity/EntityMoveableRollingStock.java @@ -483,7 +483,7 @@ public double getDirectFrictionNewtons(List track) { for (Vec3i bp : track) { TileRailBase te = getWorld().getBlockEntity(bp, TileRailBase.class); if (te != null) { - if (te.getAugment() == Augment.SPEED_RETARDER) { + if (te.getAugment() == Augment.SPEED_RETARDER && te.canInteractWith(this)) { double red = getWorld().getRedstone(bp); retardedNewtons += red / 15f / track.size() * newtons; } diff --git a/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java index 5bc7103b2..06c044e01 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java @@ -99,7 +99,7 @@ public void onClick(Player.Hand hand) { || this.augment == Augment.ITEM_UNLOADER || this.augment == Augment.FLUID_LOADER || this.augment == Augment.FLUID_UNLOADER); - yOffset += 25; + yOffset += 15; couplerMode = new Button(screen, xtop + xOffset, ytop + yOffset, "Coupler Mode: " + translate.apply(properties.couplerAugmentMode)) { @Override diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index 9582972c5..dc147657b 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -550,7 +550,7 @@ public T getStockNearBy(Class type){ if (overhead == null) { return null; } - if (!compiledFilter.isEmpty() && !compiledFilter.stream().allMatch(p -> p.test(overhead))) { + if(!canInteractWith(overhead)) { return null; } if (stockTag != null && !stockTag.equals(overhead.tag)) { @@ -560,6 +560,10 @@ public T getStockNearBy(Class type){ return overhead.as(type); } + public boolean canInteractWith(EntityRollingStock stock) { + return compiledFilter == null || compiledFilter.isEmpty() || compiledFilter.stream().allMatch(p -> p.test(stock)); + } + private boolean canOperate() { switch (this.redstoneMode) { case ENABLED: @@ -978,7 +982,9 @@ public void onBreak() { @Override public boolean onClick(Player player, Player.Hand hand, Facing facing, Vec3d hit) { - if (this.augment != null && player.hasPermission(Permissions.AUGMENT_TRACK)) { + if (this.augment != null + && player.hasPermission(Permissions.AUGMENT_TRACK) + && !player.getHeldItem(Player.Hand.PRIMARY).is(IRItems.ITEM_ROLLING_STOCK)) { GuiTypes.RAIL_AUGMENT.open(player, this.getPos()); return true; } From 1c9f65b18007d6fc93125cffd8cc419ef73c8508 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 14 Sep 2025 12:11:02 +0800 Subject: [PATCH 05/15] feat: migrate stock existing `tag` --- .../items/ItemRollingStock.java | 16 +++++------ .../tile/TileRailBase.java | 28 ++++++++----------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java b/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java index 9ad07607a..41ef3f144 100644 --- a/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java +++ b/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java @@ -138,14 +138,14 @@ public List getTooltip(ItemStack stack) @Override public ClickResult onClickBlock(Player player, World world, Vec3i pos, Player.Hand hand, Facing facing, Vec3d hit) { - if(world.isServer && BlockUtil.isIRRail(world, pos)) { - TileRailBase base = world.getBlockEntity(pos, TileRailBase.class); - //TODO - if (base.getAugment() != null) { - Augment.Properties properties = base.getAugmentProperties(); -// properties.positiveFilter += - } - } +// if(world.isServer && BlockUtil.isIRRail(world, pos)) { +// TileRailBase base = world.getBlockEntity(pos, TileRailBase.class); +// //TODO +// if (base.getAugment() != null) { +// Augment.Properties properties = base.getAugmentProperties(); +//// properties.positiveFilter += +// } +// } return tryPlaceStock(player, world, pos, hand, null); } diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index dc147657b..8ee748de3 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -198,6 +198,9 @@ public void compileFilter() { } else if (str.startsWith("pack:")) { String pack = str.substring(5); list.add(s -> s.getDefinition().packName.equals(pack)); + } else if (str.startsWith("nametag;")) { + String nameTag = str.substring(8); + list.add(s -> s.tag.equals(nameTag)); } } } else { @@ -247,6 +250,9 @@ public void compileFilter() { } else if (str.startsWith("pack:")) { String pack = str.substring(5); list.add(s -> !s.getDefinition().packName.equals(pack)); + } else if (str.startsWith("nametag;")) { + String nameTag = str.substring(8); + list.add(s -> !s.tag.equals(nameTag)); } } } else { @@ -357,12 +363,16 @@ public void load(TagCompound nbt) { }); } } + case 5: + if (this.stockTag != null && !this.stockTag.isEmpty()) { + this.positive = this.positive + ",nametag:" + stockTag; + } } this.compileFilter(); } @Override public void save(TagCompound nbt) { - nbt.setInteger("version", 5); + nbt.setInteger("version", 6); } public TileRail getParentTile() { @@ -553,9 +563,6 @@ public T getStockNearBy(Class type){ if(!canInteractWith(overhead)) { return null; } - if (stockTag != null && !stockTag.equals(overhead.tag)) { - return null; - } return overhead.as(type); } @@ -1025,19 +1032,6 @@ public boolean onClick(Player player, Player.Hand hand, Facing facing, Vec3d hit return true; } - if (stack.is(Fuzzy.NAME_TAG) && player.hasPermission(Permissions.AUGMENT_TRACK)) { - if (getWorld().isServer) { - if (player.isCrouching()) { - stockTag = null; - player.sendMessage(ChatText.RESET_AUGMENT_FILTER.getMessage()); - } else { - stockTag = stack.getDisplayName(); - player.sendMessage(ChatText.SET_AUGMENT_FILTER.getMessage(stockTag)); - } - } - return true; - } - if (stack.is(Fuzzy.SNOW_LAYER)) { if (this.getWorld().isServer) { this.handleSnowTick(); From 8a02fdffc6fddae4e57137b6b49e706ddd097cc9 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 14 Sep 2025 12:20:45 +0800 Subject: [PATCH 06/15] feat: migrate tile existing `augmentFilterID` --- .../cam72cam/immersiverailroading/tile/TileRailBase.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index 8ee748de3..f7b2ef5c7 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -48,6 +48,9 @@ public class TileRailBase extends BlockEntityTrackTickable implements IRedstoneP private float railHeight = 0; @TagField("augment") private Augment augment; + @Deprecated + @TagField("augmentFilterID") + private String augmentFilterID; @TagField("positive_filter") private String positive; @TagField("negative_filter") @@ -76,6 +79,7 @@ public class TileRailBase extends BlockEntityTrackTickable implements IRedstoneP private int ticksExisted; public boolean blockUpdate; private Gauge augmentGauge; + @Deprecated @TagField("stockTag") private String stockTag; private EntityMoveableRollingStock overhead; @@ -367,6 +371,10 @@ public void load(TagCompound nbt) { if (this.stockTag != null && !this.stockTag.isEmpty()) { this.positive = this.positive + ",nametag:" + stockTag; } + if (this.augmentFilterID != null && !this.augmentFilterID.isEmpty()) { + this.positive = this.positive + ",stock:" + augmentFilterID.split("/")[2] + .replace(".json", "").replace(".caml", ""); + } } this.compileFilter(); } From f188c6612a46c9d43b4d78ad748e1536691e8b5d Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 14 Sep 2025 13:01:27 +0800 Subject: [PATCH 07/15] fix typo --- .../cam72cam/immersiverailroading/tile/TileRailBase.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index f7b2ef5c7..7f9c852a3 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -202,7 +202,7 @@ public void compileFilter() { } else if (str.startsWith("pack:")) { String pack = str.substring(5); list.add(s -> s.getDefinition().packName.equals(pack)); - } else if (str.startsWith("nametag;")) { + } else if (str.startsWith("nametag:")) { String nameTag = str.substring(8); list.add(s -> s.tag.equals(nameTag)); } @@ -254,7 +254,7 @@ public void compileFilter() { } else if (str.startsWith("pack:")) { String pack = str.substring(5); list.add(s -> !s.getDefinition().packName.equals(pack)); - } else if (str.startsWith("nametag;")) { + } else if (str.startsWith("nametag:")) { String nameTag = str.substring(8); list.add(s -> !s.tag.equals(nameTag)); } @@ -265,19 +265,24 @@ public void compileFilter() { compiledFilter = list; } + public Augment getAugment() { return this.augment; } + public Augment.Properties getAugmentProperties() { return new Augment.Properties(positive, negative, couplerMode, controlMode, redstoneMode, pushPull, detectorMode); } + public int getSnowLayers() { return this.snowLayers; } + public void setSnowLayers(int snowLayers) { this.snowLayers = snowLayers; this.markDirty(); } + public float getFullHeight() { return this.bedHeight + this.snowLayers / 8.0f; } From 44e6e5c19aafb897ba79330bd26c134cafd8ac8c Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Fri, 19 Sep 2025 18:06:43 +0800 Subject: [PATCH 08/15] Add display text --- .../java/cam72cam/immersiverailroading/library/GuiText.java | 1 + .../immersiverailroading/registry/DefinitionManager.java | 2 +- .../registry/EntityRollingStockDefinition.java | 4 ++++ .../resources/assets/immersiverailroading/lang/en_us.lang | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/cam72cam/immersiverailroading/library/GuiText.java b/src/main/java/cam72cam/immersiverailroading/library/GuiText.java index b530f8a02..b7f4c73d0 100644 --- a/src/main/java/cam72cam/immersiverailroading/library/GuiText.java +++ b/src/main/java/cam72cam/immersiverailroading/library/GuiText.java @@ -59,6 +59,7 @@ public enum GuiText { RADIO_CARD_NOT_LINKED("item.radio_card.not_linked"), MODELER_TOOLTIP("stock.modeler"), PACK_TOOLTIP("stock.pack"), + TAG_TOOLTIP("stock.tag"), TRACK_SWITCHER_TOOLTIP("item.track_exchanger"), PAINT_BRUSH_MODE_TOOLTIP("item.paint_brush.mode"), PAINT_BRUSH_DESCRIPTION_TOOLTIP("item.paint_brush.description"), diff --git a/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java b/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java index 5147114e9..3a4c010b8 100644 --- a/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java +++ b/src/main/java/cam72cam/immersiverailroading/registry/DefinitionManager.java @@ -212,6 +212,7 @@ private static void initModels() throws IOException { Progress.Bar bar = Progress.push("Loading Models", definitionIDMap.size()); + stockTags = new BiMultiMap<>(); Map loaded = getStockLoadingStream(definitionIDMap.entrySet()).map(tuple -> { String defID = tuple.getKey(); String defType = tuple.getValue(); @@ -258,7 +259,6 @@ private static void initModels() throws IOException { }).filter(Objects::nonNull).collect(Collectors.toMap(Pair::getKey, Pair::getValue)); definitions = new LinkedHashMap<>(); - stockTags = new BiMultiMap<>(); definitionIDMap.keySet().stream().filter(loaded::containsKey).forEach(x -> definitions.put(x, loaded.get(x))); Progress.pop(bar); diff --git a/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java b/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java index 4294081a1..f21e98fc4 100644 --- a/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java +++ b/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java @@ -821,6 +821,10 @@ public List getTooltip(Gauge gauge) { tips.add(GuiText.WEIGHT_TOOLTIP.toString(this.getWeight(gauge))); tips.add(GuiText.MODELER_TOOLTIP.toString(modelerName)); tips.add(GuiText.PACK_TOOLTIP.toString(packName)); + if (!tags.isEmpty()) { + String tag = String.join(",", tags); + tips.add(GuiText.TAG_TOOLTIP.toString(tag)); + } return tips; } diff --git a/src/main/resources/assets/immersiverailroading/lang/en_us.lang b/src/main/resources/assets/immersiverailroading/lang/en_us.lang index a2722e19b..9e4f70394 100644 --- a/src/main/resources/assets/immersiverailroading/lang/en_us.lang +++ b/src/main/resources/assets/immersiverailroading/lang/en_us.lang @@ -127,6 +127,7 @@ gui.immersiverailroading:stock.freight_capacity=Capacity: %s Stacks gui.immersiverailroading:stock.weight=Weight: %s Kg gui.immersiverailroading:stock.modeler=Modeler: %s gui.immersiverailroading:stock.pack=Pack: %s +gui.immersiverailroading:stock.tag=Tags: %s gui.immersiverailroading:overlay.on=On gui.immersiverailroading:overlay.off=Off From bd75f44ffd7c45d9f272ab478118e2b74686a08e Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Fri, 19 Sep 2025 19:37:15 +0800 Subject: [PATCH 09/15] i18n: add translation keys --- .../gui/AugmentFilterGUI.java | 37 ++++++++++--------- .../items/ItemRailAugment.java | 3 +- .../immersiverailroading/library/Augment.java | 6 +++ .../immersiverailroading/library/GuiText.java | 12 +++++- .../immersiverailroading/lang/en_us.lang | 10 +++++ .../rolling_stock/default/base.caml | 3 ++ 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java index 06c044e01..d6a23d975 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java @@ -43,8 +43,10 @@ public void init(IScreenBuilder screen) { int xOffset = 0; int yOffset = 40; - includeTags = new TextField(screen, xtop + xOffset, ytop + yOffset, - 200-1, 20); + int buttonWidth = 220; + int buttonHeight = 20; + + includeTags = new TextField(screen, xtop + xOffset, ytop + yOffset, buttonWidth-1, buttonHeight); includeTags.setText(properties.positiveFilter); includeTags.setValidator(s -> { properties.positiveFilter = s; @@ -52,8 +54,7 @@ public void init(IScreenBuilder screen) { }); yOffset += 40; - excludeTags = new TextField(screen, xtop + xOffset, ytop + yOffset, - 200-1, 20); + excludeTags = new TextField(screen, xtop + xOffset, ytop + yOffset, buttonWidth-1, buttonHeight); excludeTags.setText(properties.negativeFilter); excludeTags.setValidator(s -> { properties.negativeFilter = s; @@ -63,21 +64,21 @@ public void init(IScreenBuilder screen) { Function, String> translate = e -> TextUtil.translate(e.toString()); - stockDetectorMode = new Button(screen, xtop + xOffset, ytop + yOffset, "Stock Detect Mode: " + translate.apply(properties.stockDetectorMode)) { + stockDetectorMode = new Button(screen, xtop + xOffset, ytop + yOffset, buttonWidth, buttonHeight, GuiText.SELECTOR_AUGMENT_DETECT + translate.apply(properties.stockDetectorMode)) { @Override public void onClick(Player.Hand hand) { properties.stockDetectorMode = next(properties.stockDetectorMode, Player.Hand.PRIMARY); - stockDetectorMode.setText("Detecting: " + translate.apply(properties.stockDetectorMode)); + stockDetectorMode.setText(GuiText.SELECTOR_AUGMENT_DETECT + translate.apply(properties.stockDetectorMode)); } }; stockDetectorMode.setEnabled(this.augment == Augment.DETECTOR); yOffset += 25; - redstoneMode = new Button(screen, xtop + xOffset, ytop + yOffset, "Redstone Mode: " + translate.apply(properties.redstoneMode)) { + redstoneMode = new Button(screen, xtop + xOffset, ytop + yOffset, buttonWidth, buttonHeight, GuiText.SELECTOR_AUGMENT_REDSTONE + translate.apply(properties.redstoneMode)) { @Override public void onClick(Player.Hand hand) { properties.redstoneMode = next(properties.redstoneMode, Player.Hand.PRIMARY); - redstoneMode.setText("Redstone Mode: " + translate.apply(properties.redstoneMode)); + redstoneMode.setText(GuiText.SELECTOR_AUGMENT_REDSTONE + translate.apply(properties.redstoneMode)); } }; redstoneMode.setEnabled(this.augment == Augment.COUPLER @@ -87,7 +88,7 @@ public void onClick(Player.Hand hand) { || this.augment == Augment.FLUID_UNLOADER); yOffset += 25; - pushpull = new CheckBox(screen, xtop + xOffset, ytop + yOffset, "Enable Pushpull", properties.pushpull) { + pushpull = new CheckBox(screen, xtop + xOffset, ytop + yOffset, GuiText.SELECTOR_AUGMENT_PUSHPULL.toString(), properties.pushpull) { @Override public void onClick(Player.Hand hand) { properties.pushpull = !properties.pushpull; @@ -101,21 +102,21 @@ public void onClick(Player.Hand hand) { || this.augment == Augment.FLUID_UNLOADER); yOffset += 15; - couplerMode = new Button(screen, xtop + xOffset, ytop + yOffset, "Coupler Mode: " + translate.apply(properties.couplerAugmentMode)) { + couplerMode = new Button(screen, xtop + xOffset, ytop + yOffset, buttonWidth, buttonHeight, GuiText.SELECTOR_AUGMENT_COUPLER + translate.apply(properties.couplerAugmentMode)) { @Override public void onClick(Player.Hand hand) { properties.couplerAugmentMode = next(properties.couplerAugmentMode, Player.Hand.PRIMARY); - couplerMode.setText("Coupler Mode: " + translate.apply(properties.couplerAugmentMode)); + couplerMode.setText(GuiText.SELECTOR_AUGMENT_COUPLER + translate.apply(properties.couplerAugmentMode)); } }; couplerMode.setEnabled(this.augment == Augment.COUPLER); yOffset += 25; - locoControlMode = new Button(screen, xtop + xOffset, ytop + yOffset, "Locomotive Control Mode: " + translate.apply(properties.locoControlMode)) { + locoControlMode = new Button(screen, xtop + xOffset, ytop + yOffset, buttonWidth, buttonHeight, GuiText.SELECTOR_AUGMENT_CONTROL + translate.apply(properties.locoControlMode)) { @Override public void onClick(Player.Hand hand) { properties.locoControlMode = next(properties.locoControlMode, Player.Hand.PRIMARY); - locoControlMode.setText("Locomotive Control Mode: " + translate.apply(properties.locoControlMode)); + locoControlMode.setText(GuiText.SELECTOR_AUGMENT_CONTROL + translate.apply(properties.locoControlMode)); } }; locoControlMode.setEnabled(this.augment == Augment.LOCO_CONTROL); @@ -143,20 +144,20 @@ public void draw(IScreenBuilder builder, RenderState state) { GUIHelpers.drawRect(0, 0, GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight(), 0x88000000); - GUIHelpers.drawRect(0, 0, 200, GUIHelpers.getScreenHeight(), 0xCC000000); + GUIHelpers.drawRect(0, 0, 220, GUIHelpers.getScreenHeight(), 0xCC000000); int xtop = -GUIHelpers.getScreenWidth() / 2; int ytop = -GUIHelpers.getScreenHeight() / 4; - int xOffset = 100; + int xOffset = 110; int yOffset = 30; - GUIHelpers.drawCenteredString("Current Augment: " + TextUtil.translate("item.immersiverailroading:item_augment." + this.augment.toString() + ".name"), xOffset, 10, 0xFFFFFFFF); + GUIHelpers.drawCenteredString(GuiText.LABEL_CURRENT_AUGMENT + this.augment.toString(), xOffset, 10, 0xFFFFFFFF); - GUIHelpers.drawCenteredString("Included Tags", xOffset, yOffset, 0xFFFFFFFF); + GUIHelpers.drawCenteredString(GuiText.LABEL_INCLUDED_TAG.toString(), xOffset, yOffset, 0xFFFFFFFF); includeTags.setText(properties.positiveFilter); yOffset+=40; - GUIHelpers.drawCenteredString("Excluded Tags", xOffset, yOffset, 0xFFFFFFFF); + GUIHelpers.drawCenteredString(GuiText.LABEL_EXCLUDED_TAG.toString(), xOffset, yOffset, 0xFFFFFFFF); excludeTags.setText(properties.negativeFilter); } diff --git a/src/main/java/cam72cam/immersiverailroading/items/ItemRailAugment.java b/src/main/java/cam72cam/immersiverailroading/items/ItemRailAugment.java index a4a83ae8b..109d319ba 100644 --- a/src/main/java/cam72cam/immersiverailroading/items/ItemRailAugment.java +++ b/src/main/java/cam72cam/immersiverailroading/items/ItemRailAugment.java @@ -16,7 +16,6 @@ import cam72cam.mod.math.Vec3d; import cam72cam.mod.math.Vec3i; import cam72cam.mod.serialization.TagField; -import cam72cam.mod.text.TextUtil; import cam72cam.mod.util.Facing; import cam72cam.mod.world.World; @@ -117,7 +116,7 @@ public List getTooltip(ItemStack stack) @Override public String getCustomName(ItemStack stack) { - return TextUtil.translate("item.immersiverailroading:item_augment." + new Data(stack).augment.name() + ".name"); + return new Data(stack).augment.toString(); } public static class Data extends ItemDataSerializer { diff --git a/src/main/java/cam72cam/immersiverailroading/library/Augment.java b/src/main/java/cam72cam/immersiverailroading/library/Augment.java index 9d91bc9b1..c5b166fd0 100644 --- a/src/main/java/cam72cam/immersiverailroading/library/Augment.java +++ b/src/main/java/cam72cam/immersiverailroading/library/Augment.java @@ -5,6 +5,7 @@ import cam72cam.mod.serialization.TagCompound; import cam72cam.mod.serialization.TagField; import cam72cam.mod.serialization.TagMapped; +import cam72cam.mod.text.TextUtil; public enum Augment { SPEED_RETARDER, @@ -45,6 +46,11 @@ public Color color() { return Color.WHITE; } + @Override + public String toString() { + return TextUtil.translate("item.immersiverailroading:item_augment." + this.name() + ".name"); + } + @TagMapped(PropertyMapper.class) public static class Properties { public static final Properties EMPTY = new Properties("", "", diff --git a/src/main/java/cam72cam/immersiverailroading/library/GuiText.java b/src/main/java/cam72cam/immersiverailroading/library/GuiText.java index b7f4c73d0..587f836cc 100644 --- a/src/main/java/cam72cam/immersiverailroading/library/GuiText.java +++ b/src/main/java/cam72cam/immersiverailroading/library/GuiText.java @@ -22,6 +22,11 @@ public enum GuiText { SELECTOR_TRACK("selector.track"), SELECTOR_TRANSFER_TABLE_ENTRY_COUNT("selector.transfer_table_entry_count"), SELECTOR_TRANSFER_TABLE_ENTRY_SPACING("selector.transfer_table_entry_spacing"), + SELECTOR_AUGMENT_DETECT("selector.augment.detect"), + SELECTOR_AUGMENT_REDSTONE("selector.augment.redstone"), + SELECTOR_AUGMENT_PUSHPULL("selector.augment.pushpull"), + SELECTOR_AUGMENT_COUPLER("selector.augment.coupler"), + SELECTOR_AUGMENT_CONTROL("selector.augment.control"), SELECTOR_PLATE_TYPE("selector.plate_type"), SELECTOR_PLATE_BOILER("selector.plate_boiler"), SELECTOR_CAST_SINGLE("selector.cast_single"), @@ -64,12 +69,17 @@ public enum GuiText { PAINT_BRUSH_MODE_TOOLTIP("item.paint_brush.mode"), PAINT_BRUSH_DESCRIPTION_TOOLTIP("item.paint_brush.description"), + LABEL_CURRENT_AUGMENT("label.current_augment"), + LABEL_INCLUDED_TAG("label.included_tag"), + LABEL_EXCLUDED_TAG("label.excluded_tag"), + ON("overlay.on"), OFF("overlay.off"), NONE("none"), ; - private String value; + private final String value; + GuiText(String value) { this.value = value; } diff --git a/src/main/resources/assets/immersiverailroading/lang/en_us.lang b/src/main/resources/assets/immersiverailroading/lang/en_us.lang index 9e4f70394..2c8f667c3 100644 --- a/src/main/resources/assets/immersiverailroading/lang/en_us.lang +++ b/src/main/resources/assets/immersiverailroading/lang/en_us.lang @@ -90,6 +90,12 @@ gui.immersiverailroading:selector.cast_repeat=Repeat Casting gui.immersiverailroading:selector.paintbrush_random=Apply random variant gui.immersiverailroading:selector.paintbrush_apply_to_stock=Apply to single stock gui.immersiverailroading:selector.paintbrush_apply_to_train=Apply to whole train +# Migrated from old detector translations which already contain "Detecting", don't need for this +gui.immersiverailroading:selector.augment.detect=Current: +gui.immersiverailroading:selector.augment.redstone=Redstone Mode: +gui.immersiverailroading:selector.augment.pushpull=Pushpull +gui.immersiverailroading:selector.augment.coupler=Coupler Mode: +gui.immersiverailroading:selector.augment.control=Locomotive Control Mode: gui.immersiverailroading:item.switch_key=Right-Click a switch to cycle between: [ locked straight, locked turn, unlocked ]. Redstone is ignored when locked! gui.immersiverailroading:item.switch_key.data=Last locked switch: %s in %s @@ -129,6 +135,10 @@ gui.immersiverailroading:stock.modeler=Modeler: %s gui.immersiverailroading:stock.pack=Pack: %s gui.immersiverailroading:stock.tag=Tags: %s +gui.immersiverailroading:label.current_augment=Current Augment: +gui.immersiverailroading:label.included_tag=Included Tags: +gui.immersiverailroading:label.excluded_tag=Excluded Tags: + gui.immersiverailroading:overlay.on=On gui.immersiverailroading:overlay.off=Off diff --git a/src/main/resources/assets/immersiverailroading/rolling_stock/default/base.caml b/src/main/resources/assets/immersiverailroading/rolling_stock/default/base.caml index 807a70825..6d3bd7ab6 100644 --- a/src/main/resources/assets/immersiverailroading/rolling_stock/default/base.caml +++ b/src/main/resources/assets/immersiverailroading/rolling_stock/default/base.caml @@ -2,6 +2,9 @@ name = null # Required: Stock Name modeler = "N/A" # Optional: Modeler Name pack = "N/A" # Optional: Pack Name +# tags: Optional, stock's tags used by augment/displayed as tooltip +# tags: In CAML, specify this multiple times for multiple lines +# tags: In JSON, specify this as ["tag#1", "tag#2", "etc..."] # Model information darken_model = 0 # Deprecated: legacy feature From faded64c6f1877c6104ff25a60e296b1d82e3464 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 28 Sep 2025 06:18:14 +0800 Subject: [PATCH 10/15] fix: remove comment --- .../immersiverailroading/items/ItemRollingStock.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java b/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java index 41ef3f144..59c2a5c71 100644 --- a/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java +++ b/src/main/java/cam72cam/immersiverailroading/items/ItemRollingStock.java @@ -8,8 +8,6 @@ import cam72cam.immersiverailroading.registry.DefinitionManager; import cam72cam.immersiverailroading.registry.EntityRollingStockDefinition; import cam72cam.immersiverailroading.registry.LocomotiveDefinition; -import cam72cam.immersiverailroading.tile.TileRailBase; -import cam72cam.immersiverailroading.util.BlockUtil; import cam72cam.immersiverailroading.util.ItemCastingCost; import cam72cam.mod.entity.Entity; import cam72cam.mod.entity.Player; @@ -138,15 +136,6 @@ public List getTooltip(ItemStack stack) @Override public ClickResult onClickBlock(Player player, World world, Vec3i pos, Player.Hand hand, Facing facing, Vec3d hit) { -// if(world.isServer && BlockUtil.isIRRail(world, pos)) { -// TileRailBase base = world.getBlockEntity(pos, TileRailBase.class); -// //TODO -// if (base.getAugment() != null) { -// Augment.Properties properties = base.getAugmentProperties(); -//// properties.positiveFilter += -// } -// } - return tryPlaceStock(player, world, pos, hand, null); } From e452e13e1284395d697855f7327e6769e7c859ab Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 28 Sep 2025 18:00:23 +0800 Subject: [PATCH 11/15] feat: rewrite filter implement --- .../tile/TileRailBase.java | 115 +------- .../util/StockFilterCompiler.java | 270 ++++++++++++++++++ 2 files changed, 276 insertions(+), 109 deletions(-) create mode 100644 src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index 7f9c852a3..42ae51059 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -14,7 +14,6 @@ import cam72cam.immersiverailroading.library.*; import cam72cam.immersiverailroading.model.part.Door; import cam72cam.immersiverailroading.physics.MovementTrack; -import cam72cam.immersiverailroading.registry.DefinitionManager; import cam72cam.immersiverailroading.thirdparty.trackapi.BlockEntityTrackTickable; import cam72cam.immersiverailroading.util.*; import cam72cam.mod.block.IRedstoneProvider; @@ -55,7 +54,7 @@ public class TileRailBase extends BlockEntityTrackTickable implements IRedstoneP private String positive; @TagField("negative_filter") private String negative; - private List> compiledFilter = new LinkedList<>(); + private Predicate compiledFilter = s -> true; @TagField("snowLayers") private int snowLayers = 0; @TagField("flexible") @@ -157,113 +156,11 @@ public void setAugmentProperties(@Nonnull Augment.Properties properties) { } public void compileFilter() { - List> list = new LinkedList<>(); - - if (positive != null && !positive.isEmpty()) { - String[] positiveFilters = positive.split(","); - for (String str : positiveFilters) { - str = str.trim(); - if (str.startsWith("type:")) { - switch (str.substring(5)) { - case "diesel": - list.add(s -> s instanceof LocomotiveDiesel); - break; - case "steam": - list.add(s -> s instanceof LocomotiveSteam); - break; - case "handcar": - list.add(s -> s instanceof HandCar); - break; - case "passenger": - list.add(s -> s instanceof CarPassenger); - break; - case "tender": - list.add(s -> s instanceof Tender); - break; - case "tank": - list.add(s -> s instanceof CarTank); - break; - case "freight": - list.add(s -> s instanceof CarFreight); - } - } else if (str.startsWith("tag:")) { - String tag = str.substring(4); - list.add(s -> DefinitionManager.isTaggedWith(s.getDefinition(), tag)); - } else if (str.startsWith("stock:")) { - String defID = str.substring(6); - list.add(s -> s.getDefinitionID().split("/")[2] - .replace(".json", "").replace(".caml", "").equals(defID)); - } else if (str.startsWith("works:")) { - String works = str.substring(6); - list.add(s -> s instanceof Locomotive && ((Locomotive) s).getDefinition().works.equals(works)); - } else if (str.startsWith("author:")) { - String author = str.substring(7); - list.add(s -> s.getDefinition().modelerName.equals(author)); - } else if (str.startsWith("pack:")) { - String pack = str.substring(5); - list.add(s -> s.getDefinition().packName.equals(pack)); - } else if (str.startsWith("nametag:")) { - String nameTag = str.substring(8); - list.add(s -> s.tag.equals(nameTag)); - } - } - } else { - list.add(s -> true); - } - - if (negative != null && !negative.isEmpty()) { - String[] negativeFilters = negative.split(","); - for (String str : negativeFilters) { - str = str.trim(); - if (str.startsWith("type:")) { - switch (str.substring(5)) { - case "diesel": - list.add(s -> !(s instanceof LocomotiveDiesel)); - break; - case "steam": - list.add(s -> !(s instanceof LocomotiveSteam)); - break; - case "handcar": - list.add(s -> !(s instanceof HandCar)); - break; - case "passenger": - list.add(s -> !(s instanceof CarPassenger)); - break; - case "tender": - list.add(s -> !(s instanceof Tender)); - break; - case "tank": - list.add(s -> !(s instanceof CarTank)); - break; - case "freight": - list.add(s -> !(s instanceof CarFreight)); - } - } else if (str.startsWith("tag:")) { - String tag = str.substring(4); - list.add(s -> !DefinitionManager.isTaggedWith(s.getDefinition(), tag)); - } else if (str.startsWith("stock:")) { - String defID = str.substring(6); - list.add(s -> !s.getDefinitionID().split("/")[2] - .replace(".json", "").replace(".caml", "").equals(defID)); - } else if (str.startsWith("works:")) { - String works = str.substring(6); - list.add(s -> !(s instanceof Locomotive && ((Locomotive) s).getDefinition().works.equals(works))); - } else if (str.startsWith("author:")) { - String author = str.substring(7); - list.add(s -> !s.getDefinition().modelerName.equals(author)); - } else if (str.startsWith("pack:")) { - String pack = str.substring(5); - list.add(s -> !s.getDefinition().packName.equals(pack)); - } else if (str.startsWith("nametag:")) { - String nameTag = str.substring(8); - list.add(s -> !s.tag.equals(nameTag)); - } - } - } else { - list.add(s -> true); - } + Predicate positive = StockFilterCompiler.compile(this.positive); + Predicate negative = StockFilterCompiler.compile(this.negative); - compiledFilter = list; + positive.and(negative.negate()); + compiledFilter = positive; } public Augment getAugment() { @@ -581,7 +478,7 @@ public T getStockNearBy(Class type){ } public boolean canInteractWith(EntityRollingStock stock) { - return compiledFilter == null || compiledFilter.isEmpty() || compiledFilter.stream().allMatch(p -> p.test(stock)); + return compiledFilter == null || compiledFilter.test(stock); } private boolean canOperate() { diff --git a/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java b/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java new file mode 100644 index 000000000..7685c301e --- /dev/null +++ b/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java @@ -0,0 +1,270 @@ +package cam72cam.immersiverailroading.util; + +import cam72cam.immersiverailroading.entity.*; +import cam72cam.immersiverailroading.registry.DefinitionManager; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Predicate; +import java.util.function.Supplier; + +public class StockFilterCompiler { + private static final Map> prefixes = new HashMap<>(); + private static final String AND = "&&"; + private static final String OR = "||"; + private static final String START_PAREN = "("; + private static final String END_PAREN = ")"; + + static { + prefixes.put("type", (stock, content) -> { + switch (content) { + case "locomotive": + return stock instanceof LocomotiveDiesel + || stock instanceof LocomotiveSteam + || stock instanceof HandCar; + case "diesel": + return stock instanceof LocomotiveDiesel; + case "steam": + return stock instanceof LocomotiveSteam; + case "handcar": + return stock instanceof HandCar; + case "passenger": + return stock instanceof CarPassenger; + case "tender": + return stock instanceof Tender; + case "tank": + return stock instanceof CarTank; + case "freight": + return stock instanceof CarFreight; + default: + return false; + } + }); + prefixes.put("tag", (stock, content) -> DefinitionManager.isTaggedWith(stock.getDefinition(), content)); + prefixes.put("stock", (stock, content) -> { + String definitionFileName = stock.getDefinitionID().split("/")[2]; + return definitionFileName.substring(0, definitionFileName.length() - 5).equals(content); + }); + prefixes.put("works", (stock, content) -> stock instanceof Locomotive && ((Locomotive) stock).getDefinition().works.equals(content)); + prefixes.put("author", (stock, content) -> stock.getDefinition().modelerName.equals(content)); + prefixes.put("pack", (stock, content) -> stock.getDefinition().packName.equals(content)); + prefixes.put("nametag", (stock, content) -> stock.tag.equals(content)); + } + + public static Predicate compile(String expression) { + //A filter supports (/&&/||/) to add logic calculation + if (expression == null || expression.trim().isEmpty()) { + return s -> true; + } + + expression = expression.trim(); + + List tokens = new ArrayList<>(); + StringBuilder buffer = new StringBuilder(); + int parenDepth = 0; + + Runnable flushBuffer = () -> { + if (buffer.length() > 0) { + String token = buffer.toString().trim(); + if (!token.isEmpty()) { + tokens.add(token); + } + buffer.setLength(0); + } + }; + + //Parse tokens + for (int i = 0; i < expression.length(); i++) { + char c = expression.charAt(i); + + switch (c) { + case '(': + flushBuffer.run(); + parenDepth++; + tokens.add(START_PAREN); + break; + case ')': + flushBuffer.run(); + parenDepth--; + if (parenDepth < 0) { + throw new IllegalArgumentException("Unmatched closing parenthesis"); + } + tokens.add(END_PAREN); + break; + case '&': + if (i + 1 < expression.length() && expression.charAt(i + 1) == '&') { + flushBuffer.run(); + tokens.add(AND); + i++; + } else { + buffer.append(c); + } + break; + case '|': + if (i + 1 < expression.length() && expression.charAt(i + 1) == '|') { + flushBuffer.run(); + tokens.add(OR); + i++; + } else { + buffer.append(c); + } + break; + default: + //Default token + buffer.append(c); + } + } + + if (parenDepth != 0) { + throw new IllegalArgumentException("Unmatched opening parenthesis"); + } + + flushBuffer.run(); + Node node = buildAST(tokens); + return node.predicate.get(); + } + + private static Node buildAST(List tokens) { + Node root = new Node(); + Node current = root; + Stack paren = new Stack<>(); + + BiFunction and = (b1, b2) -> b1 && b2; + BiFunction or = (b1, b2) -> b1 || b2; + + //I used a complex way to build it but it works... + for (String token : tokens) { + switch (token) { + case START_PAREN: + Node child = new Node(); + if (current.leftChild == null) { + current.leftChild = child; + } else { + current.rightChild = child; + } + child.parent = current; + current = child; + paren.push(current); + break; + case END_PAREN: + current = paren.pop(); + break; + case AND: + if (current.parent != null) { + if (current.parent.function == null) { + current = current.parent; + current.function = and; + } else { + if (current.parent.function == and) { + Node insert = new Node(); + if (current.parent.leftChild == current) { + current.parent.leftChild = insert; + } else { + current.parent.rightChild = insert; + } + current.parent = insert; + current = insert; + current.function = and; + } else { + Node parent = current.parent; + if (parent == root) { + Node newRoot = new Node(); + root.parent = newRoot; + root = newRoot; + current = root; + current.function = and; + } else { + Node grand = parent.parent; + Node insert = new Node(); + if (grand.leftChild == parent) { + grand.leftChild = insert; + } else { + grand.rightChild = insert; + } + parent.parent = insert; + current = insert; + current.function = and; + } + } + } + Node inter = current; + current.predicate = () -> inter.leftChild.predicate.get().and(inter.rightChild.predicate.get()); + } else { + Node newParent = new Node(); + newParent.leftChild = current; + current.parent = newParent; + current = newParent; + current.function = and; + root = newParent; + } + break; + case OR: + if (current.parent != null) { + if (current.parent.function == null) { + current = current.parent; + current.function = or; + } else { + Node insert = new Node(); + if (current.parent.leftChild == current) { + current.parent.leftChild = insert; + } else { + current.parent.rightChild = insert; + } + current.parent = insert; + current = insert; + current.function = or; + } + Node inter = current; + current.predicate = () -> inter.leftChild.predicate.get().or(inter.rightChild.predicate.get()); + } else { + Node newParent = new Node(); + newParent.leftChild = current; + current.parent = newParent; + current = newParent; + current.function = or; + root = newParent; + } + break; + default: + Node newNode = new Node(); + newNode.predicate = () -> getPredicate(token); + newNode.parent = current; + if (current.leftChild == null) { + current.leftChild = newNode; + } else { + current.rightChild = newNode; + } + current = newNode; + } + } + + while (root.leftChild == null || root.rightChild == null) { + if (root.leftChild == null && root.rightChild == null) { + break; + } + root = root.leftChild == null ? root.rightChild : root.leftChild; + } + + return root; + } + + private static Predicate getPredicate(String token) { + String[] strings = token.split(":"); + if (strings.length == 2) { + return stock -> prefixes.get(strings[0]).apply(stock, strings[1]); + } else { + throw new IllegalArgumentException("Invalid token format: " + token); + } + } + + private static class Node { + //If is leaf... + Supplier> predicate; + //It is not leaf... + BiFunction function; + + Node parent; + Node leftChild; + Node rightChild; + } +} From 239628bbefe4c1d5fd6bbef4d014ef33d9f6a007 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 28 Sep 2025 18:18:37 +0800 Subject: [PATCH 12/15] fix: add error handler --- .../library/ChatText.java | 1 + .../tile/TileRailBase.java | 25 ++++++++++++++----- .../immersiverailroading/lang/en_us.lang | 1 + 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/library/ChatText.java b/src/main/java/cam72cam/immersiverailroading/library/ChatText.java index 805f610ca..9fc3723c6 100644 --- a/src/main/java/cam72cam/immersiverailroading/library/ChatText.java +++ b/src/main/java/cam72cam/immersiverailroading/library/ChatText.java @@ -18,6 +18,7 @@ public enum ChatText { COUPLER_STATUS_COUPLED("coupler.status.coupled"), COUPLER_STATUS_DECOUPLED_ENGAGED("coupler.status.decoupled.engaged"), COUPLER_STATUS_DECOUPLED_DISENGAGED("coupler.status.decoupled.disengaged"), + AUGMENT_FILTER_FAIL("augment.filter_fail"), BUILD_MISSING("build.missing"), SET_AUGMENT_FILTER("augment.set"), RESET_AUGMENT_FILTER("augment.reset"), diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index 42ae51059..c14c5bc68 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -28,6 +28,7 @@ import cam72cam.mod.sound.Audio; import cam72cam.mod.sound.SoundCategory; import cam72cam.mod.sound.StandardSound; +import cam72cam.mod.text.PlayerMessage; import cam72cam.mod.util.Facing; import cam72cam.mod.serialization.TagCompound; import cam72cam.immersiverailroading.thirdparty.trackapi.ITrack; @@ -156,10 +157,22 @@ public void setAugmentProperties(@Nonnull Augment.Properties properties) { } public void compileFilter() { - Predicate positive = StockFilterCompiler.compile(this.positive); - Predicate negative = StockFilterCompiler.compile(this.negative); - - positive.and(negative.negate()); + Predicate positive; + Predicate negative; + try { + positive = StockFilterCompiler.compile(this.positive); + negative = StockFilterCompiler.compile(this.negative); + } catch (Exception e) { + if (getWorld().isServer) { + getWorld().getEntities(Player.class).stream() + .filter(player -> player.getPosition().distanceTo(new Vec3d(this.getPos())) < 20) + .forEach(player -> player.asPlayer().sendMessage( + PlayerMessage.translate(ChatText.AUGMENT_FILTER_FAIL.getRaw(), + this.getPos().x, this.getPos().y, this.getPos().z))); + } + return; + } + positive = positive.and(negative.negate()); compiledFilter = positive; } @@ -271,10 +284,10 @@ public void load(TagCompound nbt) { } case 5: if (this.stockTag != null && !this.stockTag.isEmpty()) { - this.positive = this.positive + ",nametag:" + stockTag; + this.positive = this.positive + "&& nametag:" + stockTag; } if (this.augmentFilterID != null && !this.augmentFilterID.isEmpty()) { - this.positive = this.positive + ",stock:" + augmentFilterID.split("/")[2] + this.positive = this.positive + "&& stock:" + augmentFilterID.split("/")[2] .replace(".json", "").replace(".caml", ""); } } diff --git a/src/main/resources/assets/immersiverailroading/lang/en_us.lang b/src/main/resources/assets/immersiverailroading/lang/en_us.lang index 2c8f667c3..1779c4c0e 100644 --- a/src/main/resources/assets/immersiverailroading/lang/en_us.lang +++ b/src/main/resources/assets/immersiverailroading/lang/en_us.lang @@ -41,6 +41,7 @@ chat.immersiverailroading:coupler.disengaged=Coupler %s set to shunting mode chat.immersiverailroading:coupler.status.coupled=%s Coupler is coupled to %s (%s, %s, %s) chat.immersiverailroading:coupler.status.decoupled.engaged=%s Coupler is decoupled in normal mode chat.immersiverailroading:coupler.status.decoupled.disengaged=%s Coupler is decoupled in shunting mode +chat.immersiverailroading:augment.filter_fail=Failed to compile filter for augment at (%s, %s, %s), check if its format is correct! chat.immersiverailroading:build.missing=Missing %s %s chat.immersiverailroading:augment.set=Set Augment to act only upon %s chat.immersiverailroading:augment.reset=Set Augment to act upon any stock From 67a5d4571ef85149b98b9bd728e41f512a5436a0 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 28 Sep 2025 18:27:21 +0800 Subject: [PATCH 13/15] docs: add comment --- .../util/StockFilterCompiler.java | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java b/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java index 7685c301e..133fc411a 100644 --- a/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java +++ b/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java @@ -132,10 +132,11 @@ private static Node buildAST(List tokens) { BiFunction and = (b1, b2) -> b1 && b2; BiFunction or = (b1, b2) -> b1 || b2; - //I used a complex way to build it but it works... + //I used a complex way to build it, but it works... for (String token : tokens) { switch (token) { case START_PAREN: + //Add a new child node and move current pointer to it Node child = new Node(); if (current.leftChild == null) { current.leftChild = child; @@ -144,18 +145,32 @@ private static Node buildAST(List tokens) { } child.parent = current; current = child; + //Store status paren.push(current); break; case END_PAREN: + //Revert status current = paren.pop(); break; case AND: - if (current.parent != null) { - if (current.parent.function == null) { + if (current.parent == null) { + //If current is root node then add a new root, then move current pointer to it + Node newParent = new Node(); + newParent.leftChild = current; + current.parent = newParent; + current = newParent; + current.symbol = and; + //Change root + root = newParent; + } else { + if (current.parent.symbol == null) { + //Move current pointer to parent and change symbol to && current = current.parent; - current.function = and; + current.symbol = and; } else { - if (current.parent.function == and) { + if (current.parent.symbol == and) { + //If current token's symbol has higher or equal priority than parent's, insert + //a node between them and move current pointer to inserted Node insert = new Node(); if (current.parent.leftChild == current) { current.parent.leftChild = insert; @@ -164,15 +179,17 @@ private static Node buildAST(List tokens) { } current.parent = insert; current = insert; - current.function = and; + current.symbol = and; } else { + //Otherwise insert a node between parent and grandparent Node parent = current.parent; if (parent == root) { + //If parent is root then add a new root, then move current pointer to it Node newRoot = new Node(); root.parent = newRoot; root = newRoot; current = root; - current.function = and; + current.symbol = and; } else { Node grand = parent.parent; Node insert = new Node(); @@ -183,27 +200,23 @@ private static Node buildAST(List tokens) { } parent.parent = insert; current = insert; - current.function = and; + current.symbol = and; } } } Node inter = current; + //The tree may not be completed, use Supplier to avoid NPE current.predicate = () -> inter.leftChild.predicate.get().and(inter.rightChild.predicate.get()); - } else { - Node newParent = new Node(); - newParent.leftChild = current; - current.parent = newParent; - current = newParent; - current.function = and; - root = newParent; } break; case OR: + //Same as above if (current.parent != null) { - if (current.parent.function == null) { + if (current.parent.symbol == null) { current = current.parent; - current.function = or; + current.symbol = or; } else { + //OR's priority is lower than AND so no check Node insert = new Node(); if (current.parent.leftChild == current) { current.parent.leftChild = insert; @@ -212,7 +225,7 @@ private static Node buildAST(List tokens) { } current.parent = insert; current = insert; - current.function = or; + current.symbol = or; } Node inter = current; current.predicate = () -> inter.leftChild.predicate.get().or(inter.rightChild.predicate.get()); @@ -221,11 +234,12 @@ private static Node buildAST(List tokens) { newParent.leftChild = current; current.parent = newParent; current = newParent; - current.function = or; + current.symbol = or; root = newParent; } break; default: + //Add a new child node and move current pointer to it Node newNode = new Node(); newNode.predicate = () -> getPredicate(token); newNode.parent = current; @@ -238,6 +252,7 @@ private static Node buildAST(List tokens) { } } + //Remove empty nodes from root while (root.leftChild == null || root.rightChild == null) { if (root.leftChild == null && root.rightChild == null) { break; @@ -261,7 +276,7 @@ private static class Node { //If is leaf... Supplier> predicate; //It is not leaf... - BiFunction function; + BiFunction symbol; Node parent; Node leftChild; From b096e192c37e4006994800aad48ace8aa0e14316 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 28 Sep 2025 18:31:28 +0800 Subject: [PATCH 14/15] docs: add comment --- .../immersiverailroading/util/StockFilterCompiler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java b/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java index 133fc411a..e2b6f1c98 100644 --- a/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java +++ b/src/main/java/cam72cam/immersiverailroading/util/StockFilterCompiler.java @@ -214,7 +214,6 @@ private static Node buildAST(List tokens) { if (current.parent != null) { if (current.parent.symbol == null) { current = current.parent; - current.symbol = or; } else { //OR's priority is lower than AND so no check Node insert = new Node(); @@ -225,8 +224,8 @@ private static Node buildAST(List tokens) { } current.parent = insert; current = insert; - current.symbol = or; } + current.symbol = or; Node inter = current; current.predicate = () -> inter.leftChild.predicate.get().or(inter.rightChild.predicate.get()); } else { @@ -273,9 +272,10 @@ private static Predicate getPredicate(String token) { } private static class Node { - //If is leaf... + //If this node is a leaf... Supplier> predicate; - //It is not leaf... + //If this node isn't a leaf... + //May not need a function here, but reserved for scalability BiFunction symbol; Node parent; From fa5dfdf9acd812d14c64e6b250cd84858c0070be Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Sun, 28 Sep 2025 19:06:53 +0800 Subject: [PATCH 15/15] feat: add cg filter for actuator --- .../gui/AugmentFilterGUI.java | 16 ++++++++++ .../immersiverailroading/library/Augment.java | 8 +++-- .../immersiverailroading/library/GuiText.java | 1 + .../tile/TileRailBase.java | 29 +++++++++++++++---- .../immersiverailroading/lang/en_us.lang | 1 + 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java index d6a23d975..e39248bd8 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/AugmentFilterGUI.java @@ -21,6 +21,7 @@ public class AugmentFilterGUI implements IScreen { private final Augment.Properties properties; private TextField includeTags; private TextField excludeTags; + private TextField doorActuatorFilter; private Button stockDetectorMode; private Button redstoneMode; private CheckBox pushpull; @@ -46,6 +47,14 @@ public void init(IScreenBuilder screen) { int buttonWidth = 220; int buttonHeight = 20; + doorActuatorFilter = new TextField(screen, xtop + xOffset + 240, ytop + yOffset, buttonWidth - 50, buttonHeight); + doorActuatorFilter.setText(properties.doorActuatorFilter); + doorActuatorFilter.setVisible(augment.equals(Augment.ACTUATOR)); + doorActuatorFilter.setValidator(s -> { + properties.doorActuatorFilter = s; + return true; + }); + includeTags = new TextField(screen, xtop + xOffset, ytop + yOffset, buttonWidth-1, buttonHeight); includeTags.setText(properties.positiveFilter); includeTags.setValidator(s -> { @@ -135,6 +144,9 @@ public void onClose() { if (properties.negativeFilter == null) { properties.negativeFilter = ""; } + if (properties.doorActuatorFilter == null) { + properties.doorActuatorFilter = ""; + } new AugmentFilterChangePacket(pos, properties).sendToServer(); } @@ -155,6 +167,10 @@ public void draw(IScreenBuilder builder, RenderState state) { GUIHelpers.drawCenteredString(GuiText.LABEL_INCLUDED_TAG.toString(), xOffset, yOffset, 0xFFFFFFFF); includeTags.setText(properties.positiveFilter); + if (augment == Augment.ACTUATOR) { + GUIHelpers.drawCenteredString(GuiText.LABEL_ACTUATOR_FILTER.toString(), xOffset + 210, yOffset, 0xFFFFFFFF); + doorActuatorFilter.setText(properties.doorActuatorFilter); + } yOffset+=40; GUIHelpers.drawCenteredString(GuiText.LABEL_EXCLUDED_TAG.toString(), xOffset, yOffset, 0xFFFFFFFF); diff --git a/src/main/java/cam72cam/immersiverailroading/library/Augment.java b/src/main/java/cam72cam/immersiverailroading/library/Augment.java index c5b166fd0..155c44172 100644 --- a/src/main/java/cam72cam/immersiverailroading/library/Augment.java +++ b/src/main/java/cam72cam/immersiverailroading/library/Augment.java @@ -53,7 +53,7 @@ public String toString() { @TagMapped(PropertyMapper.class) public static class Properties { - public static final Properties EMPTY = new Properties("", "", + public static final Properties EMPTY = new Properties("", "", "", CouplerAugmentMode.ENGAGED, LocoControlMode.THROTTLE, RedstoneMode.ENABLED, @@ -62,6 +62,7 @@ public static class Properties { public String positiveFilter; public String negativeFilter; + public String doorActuatorFilter; public CouplerAugmentMode couplerAugmentMode; public LocoControlMode locoControlMode; public RedstoneMode redstoneMode; @@ -71,10 +72,11 @@ public static class Properties { public Properties() { } - public Properties(String positiveFilter, String negativeFilter, CouplerAugmentMode couplerAugmentMode, + public Properties(String positiveFilter, String negativeFilter, String doorActuatorFilter, CouplerAugmentMode couplerAugmentMode, LocoControlMode locoControlMode, RedstoneMode redstoneMode, boolean pushpull, StockDetectorMode stockDetectorMode) { this.positiveFilter = positiveFilter; this.negativeFilter = negativeFilter; + this.doorActuatorFilter = doorActuatorFilter; this.couplerAugmentMode = couplerAugmentMode; this.locoControlMode = locoControlMode; this.redstoneMode = redstoneMode; @@ -86,6 +88,7 @@ public TagCompound toNBT() { TagCompound compound = new TagCompound(); compound.setString("positive", positiveFilter); compound.setString("negative", negativeFilter); + compound.setString("door_actuator", doorActuatorFilter); compound.setString("coupler", couplerAugmentMode.name()); compound.setString("loco", locoControlMode.name()); compound.setString("redstone", redstoneMode.name()); @@ -98,6 +101,7 @@ public static Properties fromNBT(TagCompound compound) { Properties properties = new Properties( compound.getString("positive"), compound.getString("negative"), + compound.getString("door_actuator"), CouplerAugmentMode.valueOf(compound.getString("coupler")), LocoControlMode.valueOf(compound.getString("loco")), RedstoneMode.valueOf(compound.getString("redstone")), diff --git a/src/main/java/cam72cam/immersiverailroading/library/GuiText.java b/src/main/java/cam72cam/immersiverailroading/library/GuiText.java index fdec70e68..7ca93d7b5 100644 --- a/src/main/java/cam72cam/immersiverailroading/library/GuiText.java +++ b/src/main/java/cam72cam/immersiverailroading/library/GuiText.java @@ -71,6 +71,7 @@ public enum GuiText { PAINT_BRUSH_DESCRIPTION_TOOLTIP("item.paint_brush.description"), LABEL_CURRENT_AUGMENT("label.current_augment"), + LABEL_ACTUATOR_FILTER("label.actuator_filter"), LABEL_INCLUDED_TAG("label.included_tag"), LABEL_EXCLUDED_TAG("label.excluded_tag"), diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index c14c5bc68..f60b7f74f 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -55,7 +55,9 @@ public class TileRailBase extends BlockEntityTrackTickable implements IRedstoneP private String positive; @TagField("negative_filter") private String negative; - private Predicate compiledFilter = s -> true; + private Predicate compiledFilter; + @TagField("actuator_filter") + private String actuatorFilter; @TagField("snowLayers") private int snowLayers = 0; @TagField("flexible") @@ -119,7 +121,7 @@ public float getRailHeight() { public void setAugment(Augment augment) { this.augment = augment; - Augment.Properties properties = new Augment.Properties("", "", + Augment.Properties properties = new Augment.Properties("", "","", CouplerAugmentMode.ENGAGED, LocoControlMode.THROTTLE, RedstoneMode.ENABLED, @@ -146,6 +148,7 @@ public void setAugment(Augment augment) { public void setAugmentProperties(@Nonnull Augment.Properties properties) { this.positive = properties.positiveFilter; this.negative = properties.negativeFilter; + this.actuatorFilter = properties.doorActuatorFilter; this.couplerMode = properties.couplerAugmentMode; this.controlMode = properties.locoControlMode; this.redstoneMode = properties.redstoneMode; @@ -170,6 +173,7 @@ public void compileFilter() { PlayerMessage.translate(ChatText.AUGMENT_FILTER_FAIL.getRaw(), this.getPos().x, this.getPos().y, this.getPos().z))); } + compiledFilter = stock -> true; return; } positive = positive.and(negative.negate()); @@ -181,7 +185,7 @@ public Augment getAugment() { } public Augment.Properties getAugmentProperties() { - return new Augment.Properties(positive, negative, couplerMode, controlMode, redstoneMode, pushPull, detectorMode); + return new Augment.Properties(positive, negative, actuatorFilter, couplerMode, controlMode, redstoneMode, pushPull, detectorMode); } public int getSnowLayers() { @@ -781,9 +785,22 @@ public void update() { EntityRollingStock stock = this.getStockNearBy(EntityRollingStock.class); if (stock != null) { float value = getWorld().getRedstone(getPos())/15f; - for (Door d : stock.getDefinition().getModel().getDoors()) { - if (d.type == Door.Types.EXTERNAL) { - stock.setControlPosition(d, value); + if (actuatorFilter == null || actuatorFilter.isEmpty()) { + for (Door d : stock.getDefinition().getModel().getDoors()) { + if (d.type == Door.Types.EXTERNAL) { + stock.setControlPosition(d, value); + } + } + } else { + String[] cgs = actuatorFilter.split(","); + for (String cg : cgs){ + cg = cg.trim(); + if(cg.isEmpty()) continue; + for (Door d : stock.getDefinition().getModel().getDoors()) { + if (d.controlGroup.equals(cg)) { + stock.setControlPosition(d, value); + } + } } } } diff --git a/src/main/resources/assets/immersiverailroading/lang/en_us.lang b/src/main/resources/assets/immersiverailroading/lang/en_us.lang index 834f3696c..133457c12 100644 --- a/src/main/resources/assets/immersiverailroading/lang/en_us.lang +++ b/src/main/resources/assets/immersiverailroading/lang/en_us.lang @@ -138,6 +138,7 @@ gui.immersiverailroading:stock.pack=Pack: %s gui.immersiverailroading:stock.tag=Tags: %s gui.immersiverailroading:label.current_augment=Current Augment: +gui.immersiverailroading:label.actuator_filter=Door Actuator CG filter gui.immersiverailroading:label.included_tag=Included Tags: gui.immersiverailroading:label.excluded_tag=Excluded Tags: