Fix lag from PistonCalculator at high TPS

Bundle piston order recalculations into one run every 200ms (wall clock,
independent of TPS) instead of one full recalculation and entity rebuild
per piston/place/break event. Tracked pistons that get moved are removed
from the visualization instead of being moved along.

Fixes #227
This commit is contained in:
2026-10-19 09:19:41 +02:00
parent 5cc5529533
commit d14ddc4fab
@@ -94,41 +94,48 @@ public class PistonCalculator implements Listener {
@EventHandler @EventHandler
public void onBlockPistonExtend(BlockPistonExtendEvent event) { public void onBlockPistonExtend(BlockPistonExtendEvent event) {
if (pistOrders.isEmpty()) return; if (pistOrders.isEmpty()) return;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { removeMovedPistOrders(event.getBlocks());
movePistOrders(event.getDirection(), event.getBlocks()); scheduleRecalculate();
pistOrders.values().forEach(PistOrder::calculate);
}, 3);
} }
@EventHandler @EventHandler
public void onBlockPistonRetract(BlockPistonRetractEvent event) { public void onBlockPistonRetract(BlockPistonRetractEvent event) {
if (pistOrders.isEmpty()) return; if (pistOrders.isEmpty()) return;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { removeMovedPistOrders(event.getBlocks());
movePistOrders(event.getDirection(), event.getBlocks()); scheduleRecalculate();
pistOrders.values().forEach(PistOrder::calculate);
}, 3);
} }
private void movePistOrders(BlockFace direction, List<Block> blocks) { private void removeMovedPistOrders(List<Block> blocks) {
Set<PistOrder> orders = new HashSet<>();
blocks.forEach(block -> { blocks.forEach(block -> {
PistOrder pistOrder = pistOrders.get(block.getLocation()); PistOrder pistOrder = pistOrders.remove(block.getLocation());
if (pistOrder == null) return; if (pistOrder != null) pistOrder.server.close();
pistOrders.remove(block.getLocation());
pistOrder.piston = pistOrder.piston.getRelative(direction);
orders.add(pistOrder);
});
orders.forEach(pistOrder -> {
pistOrders.put(pistOrder.piston.getLocation(), pistOrder);
}); });
} }
private static final long RECALCULATE_INTERVAL_MS = 200;
private boolean recalculationScheduled = false;
private long lastRecalculation = 0;
private void scheduleRecalculate() {
if (recalculationScheduled) return;
recalculationScheduled = true;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), this::recalculateIfDue, 1);
}
private void recalculateIfDue() {
if (System.currentTimeMillis() - lastRecalculation < RECALCULATE_INTERVAL_MS) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), this::recalculateIfDue, 1);
return;
}
recalculationScheduled = false;
lastRecalculation = System.currentTimeMillis();
pistOrders.values().forEach(PistOrder::calculate);
}
@EventHandler @EventHandler
public void onBlockPlace(BlockPlaceEvent event) { public void onBlockPlace(BlockPlaceEvent event) {
if (pistOrders.isEmpty()) return; if (pistOrders.isEmpty()) return;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { scheduleRecalculate();
pistOrders.values().forEach(PistOrder::calculate);
}, 1);
} }
@EventHandler @EventHandler
@@ -138,9 +145,7 @@ public class PistonCalculator implements Listener {
PistOrder pistOrder = pistOrders.remove(event.getBlock().getLocation()); PistOrder pistOrder = pistOrders.remove(event.getBlock().getLocation());
pistOrder.server.close(); pistOrder.server.close();
} }
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { scheduleRecalculate();
pistOrders.values().forEach(PistOrder::calculate);
}, 1);
} }
@EventHandler @EventHandler
@@ -166,7 +171,7 @@ public class PistonCalculator implements Listener {
private final Map<Location, PistOrder> pistOrders = new HashMap<>(); private final Map<Location, PistOrder> pistOrders = new HashMap<>();
private final class PistOrder { private final class PistOrder {
private Block piston; private final Block piston;
private REntityServer server = new REntityServer(); private REntityServer server = new REntityServer();
private boolean pulling = false; private boolean pulling = false;