Use PistonStructureResolver in PistonCalculator

Numbering now comes directly from the vanilla resolver instead of a
custom breadth-first search, so the order matches the real move order
(including backtracking through slime/honey chains). The display of
more than 12 blocks is removed; a blocked piston is shown in red.

Refs: #158
This commit is contained in:
2026-10-18 20:09:10 +02:00
parent 253a975c73
commit 230f8c521a
@@ -23,15 +23,17 @@ import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.Permission;
import de.steamwar.entity.*; import de.steamwar.entity.*;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.piston.PistonStructureResolver;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Piston; import org.bukkit.block.data.type.Piston;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Display; import org.bukkit.entity.Display;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.TextDisplay; import org.bukkit.entity.TextDisplay;
@@ -170,7 +172,7 @@ public class PistonCalculator implements Listener {
private boolean pulling = false; private boolean pulling = false;
private List<Location> movedBlocks = new ArrayList<>(); private List<Location> movedBlocks = new ArrayList<>();
private List<Location> brokenBlocks = new ArrayList<>(); private List<Location> brokenBlocks = new ArrayList<>();
private Set<Location> immovableBlocks = new HashSet<>(); private boolean blocked = false;
public PistOrder(Block piston) { public PistOrder(Block piston) {
this.piston = piston; this.piston = piston;
@@ -179,7 +181,7 @@ public class PistonCalculator implements Listener {
public void calculate() { public void calculate() {
movedBlocks.clear(); movedBlocks.clear();
brokenBlocks.clear(); brokenBlocks.clear();
immovableBlocks.clear(); blocked = false;
if (piston.isEmpty()) { if (piston.isEmpty()) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
@@ -199,7 +201,7 @@ public class PistonCalculator implements Listener {
pulling = piston.getType() == Material.STICKY_PISTON && (piston.getRelative(pistonData.getFacing()).getType() == Material.AIR || pistonData.isExtended()); pulling = piston.getType() == Material.STICKY_PISTON && (piston.getRelative(pistonData.getFacing()).getType() == Material.AIR || pistonData.isExtended());
calculate(piston, pistonData.getFacing(), (pulling ? pistonData.getFacing().getOppositeFace() : pistonData.getFacing())); resolve(pistonData.getFacing());
Collections.reverse(movedBlocks); Collections.reverse(movedBlocks);
Collections.reverse(brokenBlocks); Collections.reverse(brokenBlocks);
@@ -207,86 +209,23 @@ public class PistonCalculator implements Listener {
visualize(); visualize();
} }
private void calculate(Block origin, BlockFace facing, BlockFace direction) { private void resolve(BlockFace facing) {
Block calcOrigin = origin; PistonStructureResolver resolver = new PistonStructureResolver(
if (facing != direction) calcOrigin = origin.getRelative(facing, 3); ((CraftWorld) piston.getWorld()).getHandle(),
new BlockPos(piston.getX(), piston.getY(), piston.getZ()),
List<Block> toCalc = new LinkedList<>(); Direction.valueOf(facing.name()),
calcDirection(origin, null, calcOrigin, facing != direction ? origin.getRelative(facing) : null, facing, direction, toCalc); !pulling
);
while (!toCalc.isEmpty()) { if (!resolver.resolve()) {
Block current = toCalc.remove(0); blocked = true;
if (!movedBlocks.contains(current.getLocation())) { return;
movedBlocks.add(current.getLocation());
}
Material type = current.getType();
if (type != Material.SLIME_BLOCK && type != Material.HONEY_BLOCK) continue;
Material oppositeType = type == Material.SLIME_BLOCK ? Material.HONEY_BLOCK : Material.SLIME_BLOCK;
for (BlockFace face : FACES) {
Block block = current.getRelative(face);
if (block.getType().isAir()) continue;
if (isImmovable(block) || block.getPistonMoveReaction() == PistonMoveReaction.BREAK) continue;
if (face != direction || face == direction.getOppositeFace()) {
switch (block.getType()) {
case BLACK_GLAZED_TERRACOTTA:
case GRAY_GLAZED_TERRACOTTA:
case BLUE_GLAZED_TERRACOTTA:
case BROWN_GLAZED_TERRACOTTA:
case CYAN_GLAZED_TERRACOTTA:
case GREEN_GLAZED_TERRACOTTA:
case LIGHT_BLUE_GLAZED_TERRACOTTA:
case LIGHT_GRAY_GLAZED_TERRACOTTA:
case LIME_GLAZED_TERRACOTTA:
case MAGENTA_GLAZED_TERRACOTTA:
case ORANGE_GLAZED_TERRACOTTA:
case PINK_GLAZED_TERRACOTTA:
case PURPLE_GLAZED_TERRACOTTA:
case RED_GLAZED_TERRACOTTA:
case WHITE_GLAZED_TERRACOTTA:
case YELLOW_GLAZED_TERRACOTTA:
continue;
default:
break;
}
}
if (block.getType() != oppositeType) {
if (!movedBlocks.contains(block.getLocation())) toCalc.add(block);
calcDirection(null, origin, block, null, facing, direction, toCalc);
}
}
} }
resolver.getToPush().forEach(pos -> movedBlocks.add(toLocation(pos)));
movedBlocks.remove(origin.getLocation()); resolver.getToDestroy().forEach(pos -> brokenBlocks.add(toLocation(pos)));
if (facing != direction) movedBlocks.remove(origin.getRelative(facing, 1).getLocation());
if (pulling) immovableBlocks.remove(origin.getRelative(facing).getLocation());
} }
private void calcDirection(Block origin, Block blockOrigin, Block calcOrigin, Block ignore, BlockFace facing, BlockFace direction, List<Block> toCalc) { private Location toLocation(BlockPos pos) {
for (int i = 1; i < 24; i++) { return new Location(piston.getWorld(), pos.getX(), pos.getY(), pos.getZ());
Block block = calcOrigin.getRelative(direction, i);
if (block.equals(ignore)) return;
if (block.getPistonMoveReaction() == PistonMoveReaction.BREAK) {
if (!brokenBlocks.contains(block.getLocation())) {
brokenBlocks.add(block.getLocation());
}
return;
}
if (isImmovable(block)) {
immovableBlocks.add(block.getLocation());
return;
}
if (block.getType().isAir()) return;
if (facing == direction && block.equals(blockOrigin)) {
immovableBlocks.add(block.getLocation());
return;
}
if (facing != direction && (block.equals(origin) || block.getRelative(facing.getOppositeFace()).equals(origin))) {
return;
}
if (!movedBlocks.contains(block.getLocation())) toCalc.add(block);
}
} }
private void visualize() { private void visualize() {
@@ -321,21 +260,11 @@ public class PistonCalculator implements Listener {
textDisplay.setBrightness(new Display.Brightness(15, 15)); textDisplay.setBrightness(new Display.Brightness(15, 15));
} }
for (Location location : immovableBlocks) {
CWireframe wireframe = new CWireframe(server);
wireframe.setPos1(location);
wireframe.setPos2(location);
wireframe.setWidth(1 / 32f);
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
}
CWireframe wireframe = new CWireframe(server); CWireframe wireframe = new CWireframe(server);
wireframe.setPos1(piston.getLocation()); wireframe.setPos1(piston.getLocation());
wireframe.setPos2(piston.getLocation()); wireframe.setPos2(piston.getLocation());
if (!immovableBlocks.isEmpty()) { if (blocked) {
wireframe.setBlock(Material.RED_CONCRETE.createBlockData()); wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
} else if (movedBlocks.size() > 12) {
wireframe.setBlock(Material.YELLOW_CONCRETE.createBlockData());
} else { } else {
wireframe.setBlock(Material.LIME_CONCRETE.createBlockData()); wireframe.setBlock(Material.LIME_CONCRETE.createBlockData());
} }
@@ -348,7 +277,11 @@ public class PistonCalculator implements Listener {
} else { } else {
text.append("§ePush\n"); text.append("§ePush\n");
} }
text.append("§f").append(movedBlocks.size()).append(" §eBlocks"); if (blocked) {
text.append("§cBlocked");
} else {
text.append("§f").append(movedBlocks.size()).append(" §eBlocks");
}
textDisplay.setText(text.toString()); textDisplay.setText(text.toString());
textDisplay.setBillboard(Display.Billboard.CENTER); textDisplay.setBillboard(Display.Billboard.CENTER);
textDisplay.setAlignment(TextDisplay.TextAlignment.CENTER); textDisplay.setAlignment(TextDisplay.TextAlignment.CENTER);
@@ -358,26 +291,4 @@ public class PistonCalculator implements Listener {
textDisplay.setBrightness(new Display.Brightness(15, 15)); textDisplay.setBrightness(new Display.Brightness(15, 15));
} }
} }
private boolean isImmovable(Block block) {
if (block.isEmpty() || block.isLiquid()) {
return false;
}
BlockData blockData = block.getBlockData();
if (blockData instanceof Piston) {
return ((Piston) blockData).isExtended();
}
if (block.getState() instanceof TileState) {
return true;
}
PistonMoveReaction reaction = block.getPistonMoveReaction();
if (reaction == PistonMoveReaction.IGNORE) return true;
if (reaction == PistonMoveReaction.BLOCK) return true;
switch (block.getType()) {
case OBSIDIAN:
return true;
default:
return false;
}
}
} }