1+ package me .abhimaanst .fastcane ;
2+
3+ import org .bukkit .*;
4+ import org .bukkit .block .*;
5+ import org .bukkit .configuration .file .FileConfiguration ;
6+ import org .bukkit .entity .Player ;
7+ import org .bukkit .plugin .java .JavaPlugin ;
8+ import org .bukkit .scheduler .BukkitRunnable ;
9+
10+ import java .util .*;
11+ import java .util .concurrent .ConcurrentHashMap ;
12+
13+ public class FastGrowPlugin extends JavaPlugin {
14+
15+ private double growthInterval ;
16+ private int maxCaneHeight ;
17+ private int checkRadius ;
18+ private int growthAmount ;
19+ private final Map <Location , Long > lastProcessed = new ConcurrentHashMap <>();
20+ private final int PROCESS_COOLDOWN = 500 ;
21+
22+ @ Override
23+ public void onEnable () {
24+ saveDefaultConfig ();
25+ reloadConfigValues ();
26+ getLogger ().info (String .format (
27+ "FastCane enabled! Settings: interval=%.1fs, amount=%d, max-height=%d, radius=%d" ,
28+ growthInterval , growthAmount , maxCaneHeight , checkRadius
29+ ));
30+ startGrowthScheduler ();
31+ }
32+
33+ private void reloadConfigValues () {
34+ FileConfiguration config = getConfig ();
35+ growthInterval = Math .max (0.05 , config .getDouble ("growth-interval" , 0.5 ));
36+ checkRadius = Math .min (256 , Math .max (16 , config .getInt ("check-radius" , 64 )));
37+ maxCaneHeight = Math .min (255 , Math .max (1 , config .getInt ("max-height" , 3 )));
38+ growthAmount = Math .min (maxCaneHeight , Math .max (1 , config .getInt ("growth-amount" , 1 )));
39+ }
40+
41+ private void startGrowthScheduler () {
42+ new BukkitRunnable () {
43+ @ Override
44+ public void run () {
45+ Bukkit .getScheduler ().runTaskAsynchronously (FastGrowPlugin .this , () -> {
46+ long currentTime = System .currentTimeMillis ();
47+ Set <Block > blocksToGrow = new HashSet <>();
48+
49+ for (Player player : Bukkit .getOnlinePlayers ()) {
50+ Location loc = player .getLocation ();
51+ World world = loc .getWorld ();
52+
53+
54+ int minX = loc .getBlockX () - checkRadius ;
55+ int maxX = loc .getBlockX () + checkRadius ;
56+ int minZ = loc .getBlockZ () - checkRadius ;
57+ int maxZ = loc .getBlockZ () + checkRadius ;
58+
59+ // Check blocks in cuboid
60+ for (int x = minX ; x <= maxX ; x ++) {
61+ for (int z = minZ ; z <= maxZ ; z ++) {
62+
63+ for (int y = world .getMaxHeight (); y >= 0 ; y --) {
64+ Block block = world .getBlockAt (x , y , z );
65+
66+ if (block .getType () == Material .SUGAR_CANE_BLOCK ) {
67+ Block base = findBaseBlock (block );
68+ Location baseLoc = base .getLocation ();
69+
70+
71+ Long lastProcessedTime = lastProcessed .get (baseLoc );
72+ if (lastProcessedTime == null ||
73+ currentTime - lastProcessedTime > PROCESS_COOLDOWN ) {
74+
75+ int currentHeight = getCaneHeight (base );
76+ if (currentHeight < maxCaneHeight ) {
77+ Block top = getTopBlock (base );
78+ Block above = top .getRelative (BlockFace .UP );
79+
80+ if (above .getType () == Material .AIR && canGrow (top )) {
81+ blocksToGrow .add (above );
82+ lastProcessed .put (baseLoc , currentTime );
83+ }
84+ }
85+ }
86+ }
87+ }
88+ }
89+ }
90+ }
91+
92+ if (!blocksToGrow .isEmpty ()) {
93+ Bukkit .getScheduler ().runTask (FastGrowPlugin .this , () -> {
94+ for (Block block : blocksToGrow ) {
95+ int remainingGrowth = Math .min (growthAmount ,
96+ maxCaneHeight - getCaneHeight (block .getRelative (BlockFace .DOWN )));
97+
98+ for (int i = 0 ; i < remainingGrowth ; i ++) {
99+ Block current = block .getRelative (0 , i , 0 );
100+ if (current .getType () == Material .AIR &&
101+ canGrow (current .getRelative (BlockFace .DOWN ))) {
102+ current .setType (Material .SUGAR_CANE_BLOCK );
103+ }
104+ }
105+ }
106+ });
107+ }
108+ });
109+ }
110+ }.runTaskTimer (this , 20L , (long ) (growthInterval * 20L ));
111+ }
112+
113+ private Block findBaseBlock (Block block ) {
114+ while (block .getRelative (BlockFace .DOWN ).getType () == Material .SUGAR_CANE_BLOCK ) {
115+ block = block .getRelative (BlockFace .DOWN );
116+ }
117+ return block ;
118+ }
119+
120+ private Block getTopBlock (Block base ) {
121+ Block top = base ;
122+ while (top .getRelative (BlockFace .UP ).getType () == Material .SUGAR_CANE_BLOCK ) {
123+ top = top .getRelative (BlockFace .UP );
124+ }
125+ return top ;
126+ }
127+
128+ private int getCaneHeight (Block base ) {
129+ int height = 1 ;
130+ Block current = base ;
131+ while ((current = current .getRelative (BlockFace .UP )).getType () == Material .SUGAR_CANE_BLOCK ) {
132+ height ++;
133+ }
134+ return height ;
135+ }
136+
137+ private boolean canGrow (Block sugarCane ) {
138+ Block below = sugarCane .getRelative (BlockFace .DOWN );
139+ Material belowType = below .getType ();
140+ return belowType == Material .SUGAR_CANE_BLOCK ||
141+ belowType == Material .GRASS ||
142+ belowType == Material .DIRT ||
143+ belowType == Material .SAND ;
144+ }
145+ }
0 commit comments