6f%ej07=l*|J}M_}G6eRU
zAP?Nndv{{T9Ivz23Fp(*zCQbf&MmYvipYP-DyZ`Q_HSU*&YQnWuUgI
zES&Sys^}!y9at6RQX`L}TWrYkG8PYrtTbsAm
zNYcxS&2EttM;n?jnv-;Pq*S})eHF%pi7b<0cB->4f9#)o_K$n+IrrRqKi_lD_uM>#
z;*Z1}z{~e=)z@mZOeRwx5ai_KM&Ktkb<+RSaQH(JfgexfJdK8
zou)w6(DOXuEUTp|j^F%Dp1K2^Wu;?mQH(8=S3x9}LPV*Y&|XEi2sBa75)mgGmt@KH
z?^cn!>d7L9FFvb$7X4G$x~a*@N#_ggduu{^p-Sp#&_o&1vq>f=Oy)_n77f~_;*C~*
z1-2#4hAP#tqo!eCQ~SlLwwd|ihzzJkEBjs1d~W7uq1y5aUfdK-q0Q8SK&($V120y0
zqjs1dZkS=)G_g`+&>4)7G5bW$ylhBOi397k_sBw#-++5-#e%Y|7afHabQwXyCA`FW
z!A~xooMJS2o4&-|RK}5gN%W?ovB!3WuAeUA=IZ*l
z%%LZ?%t7(1HXHCd>rmR2TI{~L75(MCkah^E7RM1&pCe2gd|MKsZIX3oS2O7QnQE0C
z+{w>eT+ZF?=@yq(ViB-sN)|isHcrM{4)&FW_?%shIkoXvmC9?y85>r9bFRaMk~baS
z_oMUja8qu&z?$2Y02Gd%Z8zMOCQf>hXIMi+Z)%r0<-Qi5kXlIn++Z!kpn)BPdVqugO?IMS6%-9$^D2-v3
zlFYGKyJ@I-r5$^LTX!Ywt$*&BCMcyM9=U#6>CB6-d2lt}Vj#=lZc1xMcc*y?-sckr
z8V9els`;2m37ne&2`3_CUynJn^FokwS9><(b`2ZrsO|^Z9y_JN8Hci`xD8=8j{ebb
zQ|>9M%DNiFqVf)$uOaQ|Ga7s3EBLa`2^nZf;;^;O1RZ>7N4Qg&Kb+)FrF)}qO!&t}Hfvfk<%kHD4-lr0fIDnOITNxE`moA?ib!!ftq}En
zLifO`pxSpTOl@^4c=xt#kpQQq5A%^ZL2x8V*Z8M(|8lNl1Bhk~vNZc^#Pmbq=3Q6}$J6oYr`U(QEa
zxS%;%(29(f1A1acqHs&7`F*`tBM&zj4b2u}=!qCTe>MZtFD{CV-YO*xo>wT9M}A??
zVh&GDS+d|+QrFS67y8R7Vy1s3T32UFizy8N?Z2XkEpP5ME4tJV92BkO&8gL_y`|g0
z2~B{sKK(X!MaF_A3tbfPJ=t>*X7fz&no@%0Gzko=V~ie|snlht@6tGSekYuThAq9t
z;_slbA{aBRR+`4IMs{GhsuEcv8oP8JtM3Vn*}6y6(lF3Gmz3zsUgV7&(*(i(2xM@L
v16-
Date: Thu, 25 Jun 2026 01:03:42 +0200
Subject: [PATCH 11/13] added functionality to the skipDownstream toggle, as
well as improved the look of the GUI
---
.../gui/MaintenanceBoxScreen.java | 1 +
.../mixin/DestinationInstructionMixin.java | 114 +++++++++++++++---
.../railway/MaintenanceSkipState.java | 4 +
.../railway/OfflineStationManager.java | 16 ++-
.../lang/en_us.json | 1 +
.../textures/gui/maintenance_box.png | Bin 1807 -> 1744 bytes
6 files changed, 112 insertions(+), 24 deletions(-)
create mode 100644 src/main/java/net/dantemc/create_maintenance_control/railway/MaintenanceSkipState.java
diff --git a/src/main/java/net/dantemc/create_maintenance_control/content/maintenance_box/gui/MaintenanceBoxScreen.java b/src/main/java/net/dantemc/create_maintenance_control/content/maintenance_box/gui/MaintenanceBoxScreen.java
index 5538b5c..9772d82 100644
--- a/src/main/java/net/dantemc/create_maintenance_control/content/maintenance_box/gui/MaintenanceBoxScreen.java
+++ b/src/main/java/net/dantemc/create_maintenance_control/content/maintenance_box/gui/MaintenanceBoxScreen.java
@@ -101,6 +101,7 @@ protected void init() {
))
.writingTo(skipDownstreamLabel)
.titled(Component.translatable("gui.maintenance_box.skip_downstream_stations.tooltip"))
+ .addHint(Component.translatable("gui.maintenance_box.skip_downstream_stations.explanation"))
.setState(blockEntity.shouldSkipDownstream() ? 1 : 0);
addRenderableWidget(skipDownstreamSelector);
diff --git a/src/main/java/net/dantemc/create_maintenance_control/mixin/DestinationInstructionMixin.java b/src/main/java/net/dantemc/create_maintenance_control/mixin/DestinationInstructionMixin.java
index b62c2db..2c3fcaa 100644
--- a/src/main/java/net/dantemc/create_maintenance_control/mixin/DestinationInstructionMixin.java
+++ b/src/main/java/net/dantemc/create_maintenance_control/mixin/DestinationInstructionMixin.java
@@ -6,10 +6,13 @@
import com.simibubi.create.content.trains.station.GlobalStation;
import net.dantemc.create_maintenance_control.CreateMaintenance;
+import net.dantemc.create_maintenance_control.railway.MaintenanceEntry;
+import net.dantemc.create_maintenance_control.railway.MaintenanceSkipState;
import net.dantemc.create_maintenance_control.railway.OfflineStationManager;
import net.minecraft.world.level.Level;
import com.simibubi.create.content.trains.graph.EdgePointType;
+import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -61,38 +64,111 @@ public abstract class DestinationInstructionMixin {
CallbackInfoReturnable cir) {
String regex = getFilterForRegex();
+ MaintenanceSkipState state = maintenance$analyzeMatchingStations(runtime, level, regex);
- boolean foundMatch = false;
- boolean allOffline = true;
+ if (!state.anyMatch())
+ return;
+
+ if (!state.allSkipped())
+ return;
+
+ int scheduleSize = runtime.schedule.entries.size();
+
+ CreateMaintenance.debug("All matching stations for '{}' are under maintenance", getFilter());
+
+ // skip 1 entry (default behavior)
+ if (!state.skipDownstream()) {
+ runtime.currentEntry = (runtime.currentEntry + 1) % scheduleSize;
+ cir.setReturnValue(null);
+ cir.cancel();
+ return;
+ }
+
+ // skipDownstream = true:
+ // search for next destination entry that train can pathfind to
+ int nextReachableEntry = maintenance$findNextReachableDestinationEntry(runtime, level);
+
+ if (nextReachableEntry != -1) {
+ CreateMaintenance.debug("Skipping downstream to schedule entry {}", nextReachableEntry);
+ runtime.currentEntry = nextReachableEntry;
+ } else {
+ // fallback: if no stations work, only skip one
+ runtime.currentEntry = (runtime.currentEntry + 1) % scheduleSize;
+ }
+
+ cir.setReturnValue(null);
+ cir.cancel();
+ }
+
+ @Unique
+ private MaintenanceSkipState maintenance$analyzeMatchingStations(ScheduleRuntime runtime, Level level, String regex) {
+ boolean anyMatch = false;
+ boolean allSkipped = true;
+ boolean skipDownstream = false;
for (GlobalStation station : runtime.train.graph.getPoints(EdgePointType.STATION)) {
if (!station.name.matches(regex))
continue;
- foundMatch = true;
+ anyMatch = true;
- if (!OfflineStationManager.isStationSkipped(level, station)) {
- allOffline = false;
- break;
+ MaintenanceEntry entry = OfflineStationManager.getMatchingEntry(level, station);
+ if (entry == null) {
+ allSkipped = false;
+ continue;
+ }
+
+ if (entry.skipDownstream()) {
+ skipDownstream = true;
}
}
- if (!foundMatch || !allOffline) {
- return;
+ return new MaintenanceSkipState(anyMatch, allSkipped, skipDownstream);
+ }
+
+ @Unique
+ private ArrayList maintenance$getValidStations(ScheduleRuntime runtime, Level level, String regex) {
+ ArrayList validStations = new ArrayList<>();
+
+ for (GlobalStation station : runtime.train.graph.getPoints(EdgePointType.STATION)) {
+ if (!station.name.matches(regex))
+ continue;
+
+ if (OfflineStationManager.isStationSkipped(level, station))
+ continue;
+
+ validStations.add(station);
}
- CreateMaintenance.debug(
- "Skipping destination entry {} with filter '{}' for {} because matching stations are under maintenance",
- runtime.currentEntry,
- getFilterForRegex(),
- runtime.train
- );
+ return validStations;
+ }
+
+ @Unique
+ private boolean maintenance$destinationEntryHasPath(ScheduleRuntime runtime, Level level, DestinationInstruction instruction) {
+ ArrayList validStations = maintenance$getValidStations(runtime, level, instruction.getFilterForRegex());
- runtime.currentEntry =
- (runtime.currentEntry + 1)
- % runtime.schedule.entries.size();
+ if (validStations.isEmpty())
+ return false;
- cir.setReturnValue(null);
- cir.cancel();
+ DiscoveredPath path = runtime.train.navigation.findPathTo(validStations, Double.MAX_VALUE);
+ return path != null;
+ }
+
+ @Unique
+ private int maintenance$findNextReachableDestinationEntry(ScheduleRuntime runtime, Level level) {
+ int scheduleSize = runtime.schedule.entries.size();
+ int originalEntry = runtime.currentEntry;
+
+ for (int offset = 1; offset < scheduleSize; offset++) {
+ int candidateIndex = (originalEntry + offset) % scheduleSize;
+
+ var scheduleEntry = runtime.schedule.entries.get(candidateIndex);
+ if (scheduleEntry.instruction instanceof DestinationInstruction destinationInstruction) {
+ if (maintenance$destinationEntryHasPath(runtime, level, destinationInstruction)) {
+ return candidateIndex;
+ }
+ }
+ }
+ return -1;
}
}
\ No newline at end of file
diff --git a/src/main/java/net/dantemc/create_maintenance_control/railway/MaintenanceSkipState.java b/src/main/java/net/dantemc/create_maintenance_control/railway/MaintenanceSkipState.java
new file mode 100644
index 0000000..75b725b
--- /dev/null
+++ b/src/main/java/net/dantemc/create_maintenance_control/railway/MaintenanceSkipState.java
@@ -0,0 +1,4 @@
+package net.dantemc.create_maintenance_control.railway;
+
+public record MaintenanceSkipState(boolean anyMatch, boolean allSkipped, boolean skipDownstream) {
+}
diff --git a/src/main/java/net/dantemc/create_maintenance_control/railway/OfflineStationManager.java b/src/main/java/net/dantemc/create_maintenance_control/railway/OfflineStationManager.java
index ad8fccd..6415d4a 100644
--- a/src/main/java/net/dantemc/create_maintenance_control/railway/OfflineStationManager.java
+++ b/src/main/java/net/dantemc/create_maintenance_control/railway/OfflineStationManager.java
@@ -78,14 +78,20 @@ public static void unregisterBox(Level level, BlockPos boxPos) {
getData(level).removeEntry(boxPos);
}
- public static boolean isStationSkipped(Level level, GlobalStation station) {
+ public static MaintenanceEntry getMatchingEntry(Level level, GlobalStation station) {
String stationName = station.name;
for (MaintenanceEntry entry : getData(level).getEntries().values()) {
- if (entry.shouldSkip() && Objects.equals(entry.stationFilter(), stationName)) {
- return true;
- }
+ if (!entry.shouldSkip())
+ continue;
+
+ if (Objects.equals(entry.stationFilter(), stationName))
+ return entry;
}
- return false;
+ return null;
+ }
+
+ public static boolean isStationSkipped(Level level, GlobalStation station) {
+ return getMatchingEntry(level, station) != null;
}
}
diff --git a/src/main/resources/assets/create_maintenance_control/lang/en_us.json b/src/main/resources/assets/create_maintenance_control/lang/en_us.json
index 62507d7..ac1231b 100644
--- a/src/main/resources/assets/create_maintenance_control/lang/en_us.json
+++ b/src/main/resources/assets/create_maintenance_control/lang/en_us.json
@@ -16,6 +16,7 @@
"gui.maintenance_box.redstone_mode.inverted": "Powered = Maintenance",
"gui.maintenance_box.skip_downstream_stations.tooltip": "Skip downstream stations",
+ "gui.maintenance_box.skip_downstream_stations.explanation": "Note: When enabled, trains may skip downstream schedule entries until a reachable station is found.",
"gui.maintenance_box.skip_downstream_stations.false": "Off",
"gui.maintenance_box.skip_downstream_stations.true": "On",
diff --git a/src/main/resources/assets/create_maintenance_control/textures/gui/maintenance_box.png b/src/main/resources/assets/create_maintenance_control/textures/gui/maintenance_box.png
index d670ad65f7781fc0a6517ea19cbca86ef5fbc8e8..b1af8fb533a3085e03c16b6e6e7003137d8a4b71 100644
GIT binary patch
delta 1358
zcmeC@yTCiakuhPSlL@D}xw*T$dw6*G#JTD`{}dfL7#J87N`m}?Ctj3iWr&H9emn7B
z4`*)~Bf|@328Q0rlNl51)12O3@px@`je&vXiKmNWNJZS+x!plhjsmXj;S8>-j?5ab
z{{K(Snt0-k?##LF{AYabPu7~eD{$BED{E7FJ&(?qC@F3H^4^xiKPvpnuN;~2aXEML
zvbD({p2?+|&Ha}(^XD8UAX~(N5r}>m`Z|K>isJrvffDx1G@pHztiN~X@Qjyx3fZJK
z7;yiISrwAD`DTudoc{FFPm?xE^tvtPG?*=6Y`xF6w*HSGP;Jeu-M@{?zps9^@!U(j
zqWkwbD=KP!{VUrZ6@LEIuIDd59&~Q_`j3D0*I(byuen~&b!g+WQ~Um(bZ>aOI-6nN
zv(8C87Snw48eShQ?2mpJWw?{E{@rwarsqvBc_kzYs_(q!G)c%f(kmI^w}`RXIq&3!
zEG1qExng5B@yVGD#~0i-*fTAFxyJ1-L;unxQV&eu-1@Ob5O!tkTniaXgV`1Z)v
zz2-1GQ2u1_)s6y
zvPR)v!-kKM98nJY7#q1y2%FhCGdqO~s1;Q7v9ahYdcJs}!^;ux-1FdIrIf(N4xWd}
z#-<8sqKryy&$1k@7_B_gbLLvZj?zgTQZv>wPUJo+Y_6BgoD|-rQWSBH%|&0-`OS(o
zydCk{GhR4WN>A7jA@MQU$Xp?HD}zwmle~I|OGW#Z^qjcYu;uF>52*?J89TZ8CV%@B
zp5gGSi&Fec7EzdrMnILrxuDO+@F7eK_@;zWjZLul`McVi?$`08QVJv{d;X$U0;jHZUgf
z9&kTE0OsI!|yt?1E3VZ3bYSw-SwZ2
zE6jfAyDgD!s6T&esq$Zyh>Z-t|FwV(0K4}FgINP_0#GlQIwIB3KMQI<#JeTTTK^7p
zfIf?AdR~c=zM|
z@6k+p_wUbYSloE|Z#rY&@7clyzfu>dA1JM_uV&KmW190_Tj0UI{k4qD-?ytIG}p`2
z2|d`eUzO3`8Ioc7lkM!va*b6M)BM1vZ3=FVdQ&MBb@0A3hIKL7v#
delta 1417
zcmcb>+s`+_k+E)~lZm>erDb?{xKdw2y`uhx=WDKar6yl3KNDgjQyyO~Z>@hgV}-7c
zxw-kodUcLB+$YN!7#OrBu2bg!4+jiJD?Y?byw=0Htc;Q21v3M~vdP(uiS@~CpXT+w
zf4q}{f#tWSi(^Pd+}pXl{!+dot?W~p#8&Go%-^c}N$qu^>eF^{Ed0N`TR?BE@P^`UgB^SRB+Yy{foVfA
zH`f9NAo?Q{9KgUDz)+Sa*njAU<+6GSKK2_sT{)&ZKi;#sAyDGs_n@6d5;Aub
z^(xCm>TA{Bd#naKI1&T)UfUmDf9Rat&h2c7sF&OS@AC?V^>uv_^@8>L
z|8)b|Wd+AreUkpBx`u2x@_K%v)a&(Hi<(5^?9WJw8{GW9o=1bb;&r^Fg;&Fy`ctev
zX`8=B|9@I!J6~IB#=ZOb_kOJpn|)O2?t!0T1~b=wXO7TQ&yHoB{g)wZ^~+}(jGO*v
ziYu@Qs4vi7@KRpApoih+|0CQdf+4=ChXjuQj)rf}4&Mzg)N#msm$10bl=T0ZgaAKN
z6&sY{E@5$*Y1O~iVxPbI9Cx@{e>rL6PJhz{TN~al{kTC~;dgxvQG`012y5fAtvt-R^~aq$41D4;z+2i0RFj4VE;f&uNdmz+)-k&37qw~mhtwbo24NYHA@)Y+-YLcIMCCe{zP%vJQf=U#by2d
z=jXDW;l5#KzrRFsgMPyAPnS{;G&bZhhq2n&nlf4ON{9-~XY6fwd-}B_vl(m1-`0k=
zuV24rWL9JS^H!f##w@}6!2L(1|ED$F&3o>A;5Ijh#f|$j8Y1fM*T-{M+_^ui;grMO
z{el-R-k;O(YklLihIRkGv2bKD&iyOP@#0;6`~mB~x||gU>iO2QmVDc;^58x!4p
From 6a652854f8d464415962c8dbae1397680a404010 Mon Sep 17 00:00:00 2001
From: DanteMinecraft
Date: Thu, 25 Jun 2026 01:06:55 +0200
Subject: [PATCH 12/13] updated swedish translation
---
.../create_maintenance_control/lang/sv_se.json | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/main/resources/assets/create_maintenance_control/lang/sv_se.json b/src/main/resources/assets/create_maintenance_control/lang/sv_se.json
index 28a5f1e..8c25e57 100644
--- a/src/main/resources/assets/create_maintenance_control/lang/sv_se.json
+++ b/src/main/resources/assets/create_maintenance_control/lang/sv_se.json
@@ -8,8 +8,20 @@
"block.create_maintenance_control.maintenance_box.tooltip.behaviour1": "TÄg som har denna station _i sina scheman_ kommer att _hoppa över den_ medan underhÄllet pÄgÄr.",
"block.create_maintenance_control.maintenance_box.tooltip.behaviour2": "NÀr blocket _fÄr en redstonesignal_ kommer tÄg att _stanna vid stationen_ som vanligt.",
+ "gui.maintenance_box.title": "UnderhÄllsblock",
+ "gui.maintenance_box.station_filter.tooltip": "Stationsfilter",
+
+ "gui.maintenance_box.redstone_mode.tooltip": "Redstone-lÀge",
+ "gui.maintenance_box.redstone_mode.default": "Odriven = UnderhÄll",
+ "gui.maintenance_box.redstone_mode.inverted": "Driven = UnderhÄll",
+
+ "gui.maintenance_box.skip_downstream_stations.tooltip": "Hoppa över efterföljande stationer",
+ "gui.maintenance_box.skip_downstream_stations.explanation": "Obs: NÀr detta Àr aktiverat kan tÄg hoppa över efterföljande schemaposter tills en nÄbar station hittas.",
+ "gui.maintenance_box.skip_downstream_stations.false": "Av",
+ "gui.maintenance_box.skip_downstream_stations.true": "PĂ„",
+
"create_maintenance_control.configuration.title": "Create: Maintenance Control-konfigurationer",
"create_maintenance_control.configuration.section.create_maintenance_control.common.toml": "Create: Maintenance Control-konfigurationer",
"create_maintenance_control.configuration.section.create_maintenance_control.common.toml.title": "Create: Maintenance Control-konfigurationer",
"create_maintenance_control.configuration.debugLogs": "Aktivera debug-loggar"
-}
+}
\ No newline at end of file
From fd9f49f4672212b999ef7ffd8ed5d1d173af7003 Mon Sep 17 00:00:00 2001
From: DanteMC <68405877+DanteMinecraft@users.noreply.github.com>
Date: Thu, 25 Jun 2026 02:26:52 +0200
Subject: [PATCH 13/13] added roadmap link in README
Removed planned options for Maintenance Box GUI from README.
---
README.md | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/README.md b/README.md
index 4aa2d3c..6e64ada 100644
--- a/README.md
+++ b/README.md
@@ -48,12 +48,7 @@ This includes initial testing with:
- Steam 'n' Rails
## đ Planned
-
-### Maintenance Box GUI
-Planned options include:
- - a station name filter field
- - a redstone behavior toggle (powered = maintenance / unpowered = maintenance)
- - a "__Skip unreachable downstream stations__" toggle for more advanced network layouts
+See [Roadmap](https://github.com/DanteMinecraft/Create-Maintenance/wiki/Roadmap)
### Documentation / Usability
- a Ponder scene for the Maintenance Box