Guest User

FlowTask.java

a guest
Aug 9th, 2025
37
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 8.28 KB | None | 0 0
  1. package me.lukas.minecraft2.core;
  2.  
  3. import org.bukkit.Bukkit;
  4. import org.bukkit.Material;
  5. import org.bukkit.block.Block;
  6. import org.bukkit.block.BlockFace;
  7. import org.bukkit.block.data.Levelled;
  8. import org.bukkit.plugin.Plugin;
  9. import org.bukkit.scheduler.BukkitRunnable;
  10.  
  11. import java.util.*;
  12. import java.util.concurrent.ConcurrentHashMap;
  13.  
  14. public class FlowTask extends BukkitRunnable {
  15.  
  16.   private final Plugin plugin;
  17.   private final Set<Block> activeBlocks = ConcurrentHashMap.newKeySet();
  18.   private static final BlockFace[] HORIZONTAL_FACES = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST};
  19.   private static final int PRESSURE_SEARCH_LIMIT = 96;
  20.  
  21.   public FlowTask(Plugin plugin) {
  22.     this.plugin = plugin;
  23.   }
  24.  
  25.   public void addActiveBlock(Block block) {
  26.     if (block.getChunk().isLoaded() && block.getType() == Material.WATER) {
  27.       activeBlocks.add(block);
  28.     }
  29.   }
  30.  
  31.   @Override
  32.   public void run() {
  33.     if (activeBlocks.isEmpty()) return;
  34.     Set<Block> processingBlocks = new HashSet<>(activeBlocks);
  35.     activeBlocks.clear();
  36.     Map<Block, Integer> pendingChanges = new ConcurrentHashMap<>();
  37.     for (Block sourceBlock : processingBlocks) {
  38.       if (!sourceBlock.getChunk().isLoaded() || sourceBlock.getType() != Material.WATER) continue;
  39.       if (flowDown(sourceBlock, pendingChanges)) continue;
  40.       int sourceLevel = getWaterLevel(sourceBlock, pendingChanges);
  41.       if (sourceLevel == 1 && drainPuddle(sourceBlock, pendingChanges)) continue;
  42.       if (sourceLevel > 1) {
  43.         equalizePressure(sourceBlock, pendingChanges);
  44.       }
  45.     }
  46.     if (!pendingChanges.isEmpty()) {
  47.       Bukkit.getScheduler().runTask(plugin, () -> applyChanges(pendingChanges));
  48.     }
  49.   }
  50.  
  51.   private void applyChanges(Map<Block, Integer> changes) {
  52.     Set<Block> neighborsToWake = new HashSet<>();
  53.     for (Map.Entry<Block, Integer> entry : changes.entrySet()) {
  54.       Block block = entry.getKey();
  55.       int newLevel = entry.getValue();
  56.       if (block.getChunk().isLoaded() && getWaterLevelFromWorld(block) != newLevel) {
  57.         setWaterLevelInWorld(block, newLevel);
  58.         collectNeighborsToWake(block, neighborsToWake);
  59.       }
  60.     }
  61.     for (Block neighbor : neighborsToWake) {
  62.       if (neighbor.getChunk().isLoaded() && neighbor.getType() == Material.WATER) {
  63.         addActiveBlock(neighbor);
  64.       }
  65.     }
  66.   }
  67.  
  68.   private boolean equalizePressure(Block sourceBlock, Map<Block, Integer> changes) {
  69.     int sourceLevel = getWaterLevel(sourceBlock, changes);
  70.     Block bestTarget = findBestLocalTarget(sourceBlock, sourceLevel, changes);
  71.  
  72.     if (bestTarget != null) {
  73.       int lowestLevel = getWaterLevel(bestTarget, changes);
  74.       if (sourceLevel > lowestLevel) {
  75.         applyDynamicAdditiveFlow(sourceBlock, bestTarget, changes);
  76.         return true;
  77.       }
  78.     }
  79.     return wideSearch(sourceBlock, sourceLevel, changes);
  80.   }
  81.  
  82.   private Block findBestLocalTarget(Block sourceBlock, int sourceLevel, Map<Block, Integer> changes) {
  83.     Block bestTarget = null;
  84.     int lowestLevel = sourceLevel;
  85.     for (BlockFace face : HORIZONTAL_FACES) {
  86.       Block neighbor = sourceBlock.getRelative(face);
  87.       if (!neighbor.getChunk().isLoaded()) continue;
  88.       if (canFlowInto(neighbor, changes) || neighbor.getType() == Material.WATER) {
  89.         int neighborLevel = getWaterLevel(neighbor, changes);
  90.         if (neighborLevel < lowestLevel) {
  91.           lowestLevel = neighborLevel;
  92.           bestTarget = neighbor;
  93.         }
  94.       }
  95.     }
  96.     return bestTarget;
  97.   }
  98.  
  99.   private boolean wideSearch(Block sourceBlock, int sourceLevel, Map<Block, Integer> changes) {
  100.     Queue<Block> openSet = new LinkedList<>();
  101.     Set<Block> closedSet = new HashSet<>();
  102.     closedSet.add(sourceBlock);
  103.     for (BlockFace face : HORIZONTAL_FACES) openSet.add(sourceBlock.getRelative(face));
  104.     Block bestTarget = null;
  105.     int lowestLevel = sourceLevel;
  106.     int searchedBlocks = 0;
  107.     while (!openSet.isEmpty() && searchedBlocks < PRESSURE_SEARCH_LIMIT) {
  108.       Block currentBlock = openSet.poll();
  109.       if (!closedSet.add(currentBlock) || !currentBlock.getChunk().isLoaded()) continue;
  110.       searchedBlocks++;
  111.       if (currentBlock.getType() != Material.WATER && !canFlowInto(currentBlock, changes)) continue;
  112.       int currentLevel = getWaterLevel(currentBlock, changes);
  113.       if (currentLevel < lowestLevel) {
  114.         lowestLevel = currentLevel;
  115.         bestTarget = currentBlock;
  116.       }
  117.       for (BlockFace face : HORIZONTAL_FACES) {
  118.         Block neighbor = currentBlock.getRelative(face);
  119.         if (!closedSet.contains(neighbor)) openSet.add(neighbor);
  120.       }
  121.     }
  122.     if (bestTarget != null && sourceLevel > lowestLevel) {
  123.       applyDynamicAdditiveFlow(sourceBlock, bestTarget, changes);
  124.       return true;
  125.     }
  126.     return false;
  127.   }
  128.  
  129.   private void applyDynamicAdditiveFlow(Block source, Block target, Map<Block, Integer> changes) {
  130.     int sourceLevel = getWaterLevel(source, changes);
  131.     int targetLevel = getWaterLevel(target, changes);
  132.  
  133.     if (changes.containsKey(source) && changes.get(source) != sourceLevel) {
  134.       return;
  135.     }
  136.  
  137.     int difference = sourceLevel - targetLevel;
  138.  
  139.     int flowAmount = Math.max(1, difference / 3);
  140.  
  141.     changes.put(source, sourceLevel - flowAmount);
  142.  
  143.     int originalTargetLevel = getWaterLevelFromWorld(target);
  144.     int plannedTargetLevel = changes.getOrDefault(target, originalTargetLevel);
  145.  
  146.     changes.put(target, Math.min(8, plannedTargetLevel + flowAmount));
  147.   }
  148.  
  149.   private boolean drainPuddle(Block b, Map<Block, Integer> c) {
  150.     if (getWaterLevel(b.getRelative(BlockFace.DOWN), c) > 0) return false;
  151.     Queue<Block> q = new LinkedList<>();
  152.     Set<Block> v = new HashSet<>();
  153.     for (BlockFace f : HORIZONTAL_FACES) {
  154.       Block n = b.getRelative(f);
  155.       if (canFlowInto(n, c)) {
  156.         q.add(n);
  157.         v.add(n);
  158.       }
  159.     }
  160.     int r = 1;
  161.     while (!q.isEmpty() && r <= 2) {
  162.       int s = q.size();
  163.       for (int i = 0; i < s; i++) {
  164.         Block p = q.poll();
  165.         Block l = p.getRelative(BlockFace.DOWN);
  166.         if (canFlowInto(l, c) || getWaterLevel(l, c) > 0) {
  167.           int t = getWaterLevel(l, c);
  168.           if (t < 8) {
  169.             c.put(b, 0);
  170.             c.put(l, t + 1);
  171.             return true;
  172.           }
  173.         }
  174.         if (r < 2) {
  175.           for (BlockFace f : HORIZONTAL_FACES) {
  176.             Block next = p.getRelative(f);
  177.             if (!v.contains(next) && canFlowInto(next, c)) {
  178.               v.add(next);
  179.               q.add(next);
  180.             }
  181.           }
  182.         }
  183.       }
  184.       r++;
  185.     }
  186.     return false;
  187.   }
  188.  
  189.   private boolean flowDown(Block b, Map<Block, Integer> c) {
  190.     Block d = b.getRelative(BlockFace.DOWN);
  191.     int s = getWaterLevel(b, c);
  192.     if (canFlowInto(d, c)) {
  193.       c.put(b, 0);
  194.       c.put(d, s);
  195.       return true;
  196.     }
  197.     if (d.getType() == Material.WATER) {
  198.       int n = getWaterLevel(d, c);
  199.       if (n < 8) {
  200.         int a = 8 - n;
  201.         int f = Math.min(s, a);
  202.         if (f > 0) {
  203.           c.put(d, n + f);
  204.           c.put(b, s - f);
  205.           return true;
  206.         }
  207.       }
  208.     }
  209.     return false;
  210.   }
  211.  
  212.   private int getWaterLevel(Block b, Map<Block, Integer> c) {
  213.     return c.getOrDefault(b, getWaterLevelFromWorld(b));
  214.   }
  215.  
  216.   private int getWaterLevelFromWorld(Block b) {
  217.     if (b.getType() == Material.WATER) {
  218.       Levelled d = (Levelled) b.getBlockData();
  219.       return d.getLevel() == 0 ? 8 : 8 - d.getLevel();
  220.     }
  221.     return 0;
  222.   }
  223.  
  224.   private void setWaterLevelInWorld(Block b, int l) {
  225.     l = Math.min(l, 8);
  226.     if (l <= 0) {
  227.       if (b.getType() == Material.WATER) b.setType(Material.AIR, false);
  228.     } else {
  229.       b.setType(Material.WATER, false);
  230.       Levelled d = (Levelled) b.getBlockData();
  231.       d.setLevel(l == 8 ? 0 : 8 - l);
  232.       b.setBlockData(d, false);
  233.     }
  234.   }
  235.  
  236.   private void collectNeighborsToWake(Block b, Set<Block> n) {
  237.     for (int x = -1; x <= 1; x++) {
  238.       for (int y = -1; y <= 1; y++) {
  239.         for (int z = -1; z <= 1; z++) {
  240.           if (x == 0 && y == 0 && z == 0) continue;
  241.           n.add(b.getRelative(x, y, z));
  242.         }
  243.       }
  244.     }
  245.   }
  246.  
  247.   private boolean canFlowInto(Block b, Map<Block, Integer> c) {
  248.     if (c.containsKey(b) && c.get(b) > 0) return false;
  249.     return b.isPassable() && !b.isLiquid();
  250.   }
  251. }
  252.  
Advertisement
Add Comment
Please, Sign In to add comment