4747import java .util .HashSet ;
4848import java .util .Iterator ;
4949import java .util .List ;
50+ import java .util .Locale ;
5051import java .util .Set ;
5152
5253public class Pipes extends AbstractCraftBookMechanic {
@@ -130,7 +131,17 @@ else if (SignUtil.isStandingSign(block.getRelative(face)) && face != BlockFace.U
130131 return null ;
131132 }
132133
133- private void searchNearbyPipes (Block block , Set <Vector > visitedPipes , List <ItemStack > items ) {
134+ /** True when the sign's first line marks the pull pass-through: 'pass', 'bypass' or 'b'. */
135+ private static boolean isPassThroughSign (ChangedSign sign ) {
136+ if (sign == null )
137+ return false ;
138+ return switch (sign .getLine (0 ).trim ().toLowerCase (Locale .ROOT )) {
139+ case "pass" , "bypass" , "b" -> true ;
140+ default -> false ;
141+ };
142+ }
143+
144+ private void searchNearbyPipes (Block block , Set <Vector > visitedPipes , List <ItemStack > items , boolean passThrough ) {
134145 Deque <Block > searchQueue = new ArrayDeque <>();
135146 searchQueue .addFirst (block );
136147
@@ -165,44 +176,48 @@ private void searchNearbyPipes(Block block, Set<Vector> visitedPipes, List<ItemS
165176
166177 filteredItems = filterEvent .getFilteredItems ();
167178
168- if (filteredItems .isEmpty ())
169- continue ;
170-
171- List <ItemStack > newItems = new ArrayList <>();
172-
173- Block fac = bl .getRelative (p .getFacing ());
174-
175- PipePutEvent event = new PipePutEvent (bl , new ArrayList <>(filteredItems ), fac );
176- Bukkit .getPluginManager ().callEvent (event );
177-
178- if (!event .isCancelled ()) {
179- if (InventoryUtil .doesBlockHaveInventory (fac )) {
180- InventoryHolder holder = (InventoryHolder ) fac .getState ();
181- newItems .addAll (InventoryUtil .addItemsToInventory (holder , event .getItems ().toArray (new ItemStack [event .getItems ().size ()])));
182- } else if (fac .getType () == Material .JUKEBOX ) {
183- Jukebox juke = (Jukebox ) fac .getState ();
184- List <ItemStack > its = new ArrayList <>(event .getItems ());
185- if (juke .getPlaying () == Material .AIR ) {
186- Iterator <ItemStack > iter = its .iterator ();
187- while (iter .hasNext ()) {
188- ItemStack st = iter .next ();
189- if (!st .getType ().isRecord ()) continue ;
190- juke .setPlaying (st .getType ());
191- juke .update ();
192- if (st .getAmount () > 1 )
193- st .setAmount (st .getAmount () - 1 );
194- else
195- iter .remove ();
196- break ;
179+ if (filteredItems .isEmpty ()) {
180+ // Unmatched items stop at a filtered output, unless this pull runs
181+ // pass-through; then they continue searching for their own output.
182+ if (!passThrough )
183+ continue ;
184+ } else {
185+ List <ItemStack > newItems = new ArrayList <>();
186+
187+ Block fac = bl .getRelative (p .getFacing ());
188+
189+ PipePutEvent event = new PipePutEvent (bl , new ArrayList <>(filteredItems ), fac );
190+ Bukkit .getPluginManager ().callEvent (event );
191+
192+ if (!event .isCancelled ()) {
193+ if (InventoryUtil .doesBlockHaveInventory (fac )) {
194+ InventoryHolder holder = (InventoryHolder ) fac .getState ();
195+ newItems .addAll (InventoryUtil .addItemsToInventory (holder , event .getItems ().toArray (new ItemStack [event .getItems ().size ()])));
196+ } else if (fac .getType () == Material .JUKEBOX ) {
197+ Jukebox juke = (Jukebox ) fac .getState ();
198+ List <ItemStack > its = new ArrayList <>(event .getItems ());
199+ if (juke .getPlaying () == Material .AIR ) {
200+ Iterator <ItemStack > iter = its .iterator ();
201+ while (iter .hasNext ()) {
202+ ItemStack st = iter .next ();
203+ if (!st .getType ().isRecord ()) continue ;
204+ juke .setPlaying (st .getType ());
205+ juke .update ();
206+ if (st .getAmount () > 1 )
207+ st .setAmount (st .getAmount () - 1 );
208+ else
209+ iter .remove ();
210+ break ;
211+ }
197212 }
213+ newItems .addAll (its );
214+ } else {
215+ newItems .addAll (event .getItems ());
198216 }
199- newItems .addAll (its );
200- } else {
201- newItems .addAll (event .getItems ());
202- }
203217
204- items .removeAll (filteredItems );
205- items .addAll (newItems );
218+ items .removeAll (filteredItems );
219+ items .addAll (newItems );
220+ }
206221 }
207222 } else if (blType == Material .DROPPER ) {
208223 ChangedSign sign = getSignOnPiston (bl );
@@ -224,20 +239,22 @@ private void searchNearbyPipes(Block block, Set<Vector> visitedPipes, List<ItemS
224239
225240 List <ItemStack > filteredItems = new ArrayList <>(VerifyUtil .withoutNulls (ItemUtil .filterItems (items , pFilters , pExceptions )));
226241
227- if (filteredItems .isEmpty ())
228- continue ;
229-
230- Dropper dropper = (Dropper ) bl .getState ();
231- List <ItemStack > newItems =
232- new ArrayList <>(dropper .getInventory ().addItem (filteredItems .toArray (new ItemStack [filteredItems .size ()])).values ());
242+ if (filteredItems .isEmpty ()) {
243+ if (!passThrough )
244+ continue ;
245+ } else {
246+ Dropper dropper = (Dropper ) bl .getState ();
247+ List <ItemStack > newItems =
248+ new ArrayList <>(dropper .getInventory ().addItem (filteredItems .toArray (new ItemStack [filteredItems .size ()])).values ());
233249
234- for (ItemStack stack : dropper .getInventory ().getContents ())
235- if (ItemUtil .isStackValid (stack ))
236- for (int i = 0 ; i < stack .getAmount (); i ++)
237- dropper .drop ();
250+ for (ItemStack stack : dropper .getInventory ().getContents ())
251+ if (ItemUtil .isStackValid (stack ))
252+ for (int i = 0 ; i < stack .getAmount (); i ++)
253+ dropper .drop ();
238254
239- items .removeAll (filteredItems );
240- items .addAll (newItems );
255+ items .removeAll (filteredItems );
256+ items .addAll (newItems );
257+ }
241258 }
242259
243260 if (!items .isEmpty ()) {
@@ -348,6 +365,10 @@ private void startPipe(Block block, List<ItemStack> items, boolean request) {
348365
349366 if (block .getType () == Material .STICKY_PISTON ) {
350367
368+ // The pulling piston's own sign can mark the whole pull as pass-through,
369+ // so its items flow past filtered outputs they don't match.
370+ boolean passThrough = isPassThroughSign (sign );
371+
351372 List <ItemStack > leftovers = new ArrayList <>();
352373
353374 Piston p = (Piston ) block .getBlockData ();
@@ -384,7 +405,7 @@ private void startPipe(Block block, List<ItemStack> items, boolean request) {
384405 items .addAll (event .getItems ());
385406 if (!event .isCancelled ()) {
386407 visitedPipes .add (fac .getLocation ().toVector ());
387- searchNearbyPipes (block , visitedPipes , items );
408+ searchNearbyPipes (block , visitedPipes , items , passThrough );
388409 }
389410
390411 if (!items .isEmpty ()) {
@@ -415,7 +436,7 @@ private void startPipe(Block block, List<ItemStack> items, boolean request) {
415436 items .addAll (event .getItems ());
416437 if (!event .isCancelled ()) {
417438 visitedPipes .add (fac .getLocation ().toVector ());
418- searchNearbyPipes (block , visitedPipes , items );
439+ searchNearbyPipes (block , visitedPipes , items , passThrough );
419440 }
420441
421442 if (!items .isEmpty ()) {
@@ -444,7 +465,7 @@ private void startPipe(Block block, List<ItemStack> items, boolean request) {
444465
445466 if (!event .isCancelled () && !items .isEmpty ()) {
446467 visitedPipes .add (fac .getLocation ().toVector ());
447- searchNearbyPipes (block , visitedPipes , items );
468+ searchNearbyPipes (block , visitedPipes , items , passThrough );
448469 }
449470
450471 leftovers .addAll (items );
@@ -455,7 +476,7 @@ private void startPipe(Block block, List<ItemStack> items, boolean request) {
455476 items .addAll (event .getItems ());
456477 if (!event .isCancelled () && !items .isEmpty ()) {
457478 visitedPipes .add (fac .getLocation ().toVector ());
458- searchNearbyPipes (block , visitedPipes , items );
479+ searchNearbyPipes (block , visitedPipes , items , passThrough );
459480 }
460481 leftovers .addAll (items );
461482 }
0 commit comments