22
33import java .text .ParseException ;
44import java .util .ArrayList ;
5- import java .util .Arrays ;
65import java .util .Collection ;
76import java .util .Collections ;
87import java .util .EnumMap ;
8+ import java .util .HashMap ;
99import java .util .HashSet ;
1010import java .util .Iterator ;
1111import java .util .List ;
1212import java .util .Map ;
13+ import java .util .Objects ;
1314import java .util .Queue ;
1415import java .util .Set ;
1516import java .util .UUID ;
3132import org .bukkit .block .ShulkerBox ;
3233import org .bukkit .block .data .BlockData ;
3334import org .bukkit .block .data .type .Slab ;
35+ import org .bukkit .entity .EntityType ;
3436import org .bukkit .inventory .ItemStack ;
3537import org .bukkit .inventory .meta .BlockStateMeta ;
36- import org .bukkit .scheduler .BukkitTask ;
3738
3839import com .bgsoftware .wildstacker .api .WildStackerAPI ;
3940import com .bgsoftware .wildstacker .api .objects .StackedBarrel ;
5556public class IslandLevelCalculator {
5657 private static final String LINE_BREAK = "==================================" ;
5758 public static final long MAX_AMOUNT = 10000000 ;
58- private static final List <Material > CHESTS = Arrays .asList (Material .CHEST , Material .CHEST_MINECART ,
59- Material .TRAPPED_CHEST , Material .SHULKER_BOX , Material .BLACK_SHULKER_BOX , Material .BLUE_SHULKER_BOX ,
60- Material .BROWN_SHULKER_BOX , Material .CYAN_SHULKER_BOX , Material .GRAY_SHULKER_BOX ,
61- Material .GREEN_SHULKER_BOX , Material .LIGHT_BLUE_SHULKER_BOX , Material .LIGHT_GRAY_SHULKER_BOX ,
62- Material .LIME_SHULKER_BOX , Material .MAGENTA_SHULKER_BOX , Material .ORANGE_SHULKER_BOX ,
63- Material .PINK_SHULKER_BOX , Material .PURPLE_SHULKER_BOX , Material .RED_SHULKER_BOX , Material .RED_SHULKER_BOX ,
64- Material .WHITE_SHULKER_BOX , Material .YELLOW_SHULKER_BOX , Material .COMPOSTER , Material .BARREL ,
65- Material .DISPENSER , Material .DROPPER , Material .SMOKER , Material .BLAST_FURNACE , Material .BUNDLE ,
66- Material .RED_BUNDLE , Material .BLACK_BUNDLE , Material .BLUE_BUNDLE , Material .BROWN_BUNDLE ,
67- Material .CYAN_BUNDLE , Material .GRAY_BUNDLE , Material .GREEN_BUNDLE , Material .LIGHT_BLUE_BUNDLE ,
68- Material .LIGHT_GRAY_BUNDLE , Material .LIME_BUNDLE , Material .MAGENTA_BUNDLE , Material .ORANGE_BUNDLE ,
69- Material .PINK_BUNDLE , Material .PURPLE_BUNDLE , Material .RED_BUNDLE , Material .WHITE_BUNDLE ,
70- Material .YELLOW_BUNDLE );
7159 private static final int CHUNKS_TO_SCAN = 100 ;
7260 private final Level addon ;
7361 private final Queue <Pair <Integer , Integer >> chunksToCheck ;
@@ -82,7 +70,7 @@ public class IslandLevelCalculator {
8270 private final int seaHeight ;
8371 private final List <Location > stackedBlocks = new ArrayList <>();
8472 private final Set <Chunk > chestBlocks = new HashSet <>();
85- private BukkitTask finishTask ;
73+ private final Map < Location , Boolean > spawners = new HashMap <>() ;
8674
8775 /**
8876 * Constructor to get the level for an island
@@ -163,6 +151,29 @@ private void checkBlock(Material mat, boolean belowSeaLevel) {
163151 }
164152 }
165153
154+ /**
155+ * Adds value to the results based on the material and whether the block is
156+ * below sea level or not
157+ *
158+ * @param mat - material of the block
159+ * @param belowSeaLevel - true if below sea level
160+ */
161+ private void checkSpawner (EntityType et , boolean belowSeaLevel ) {
162+ if (limitCount (Material .SPAWNER ) == 0 ) {
163+ return ;
164+ }
165+ Integer count = addon .getBlockConfig ().getValue (island .getWorld (), et );
166+ if (count != null ) {
167+ if (belowSeaLevel ) {
168+ results .underWaterBlockCount .addAndGet (count );
169+ results .uwCount .add (et );
170+ } else {
171+ results .rawBlockCount .addAndGet (count );
172+ results .mdCount .add (et );
173+ }
174+ }
175+ }
176+
166177 /**
167178 * Get a set of all the chunks in island
168179 *
@@ -231,31 +242,22 @@ private List<String> getReport() {
231242
232243 reportLines .add (
233244 "Blocks not counted because they exceeded limits: " + String .format ("%,d" , results .ofCount .size ()));
234- Iterable <Multiset .Entry <Material >> entriesSortedByCount = results .ofCount .entrySet ();
235- Iterator <Entry <Material >> it = entriesSortedByCount .iterator ();
245+ Iterable <Multiset .Entry <Object >> entriesSortedByCount = results .ofCount .entrySet ();
246+ Iterator <Entry <Object >> it = entriesSortedByCount .iterator ();
236247 while (it .hasNext ()) {
237- Entry <Material > type = it .next ();
238- Integer limit = addon .getBlockConfig ().getBlockLimits ().get (type .getElement ());
248+
249+ Entry <Object > type = it .next ();
250+ Material m = type .getElement () instanceof Material mat ? mat : Material .SPAWNER ;
251+ Integer limit = addon .getBlockConfig ().getBlockLimits ().get (m );
239252 String explain = ")" ;
240253 if (limit == null ) {
241- Material generic = type .getElement ();
242- limit = addon .getBlockConfig ().getBlockLimits ().get (generic );
254+ limit = addon .getBlockConfig ().getBlockLimits ().get (m );
243255 explain = " - All types)" ;
244256 }
245- reportLines .add (type . getElement (). toString ( ) + ": " + String .format ("%,d" , type .getCount ())
257+ reportLines .add (Util . prettifyText ( m . name () ) + ": " + String .format ("%,d" , type .getCount ())
246258 + " blocks (max " + limit + explain );
247259 }
248260 reportLines .add (LINE_BREAK );
249- reportLines .add ("Blocks on island that are not in config.yml" );
250- reportLines .add ("Total number = " + String .format ("%,d" , results .ncCount .size ()));
251- entriesSortedByCount = results .ncCount .entrySet ();
252- it = entriesSortedByCount .iterator ();
253- while (it .hasNext ()) {
254- Entry <Material > type = it .next ();
255- reportLines .add (type .getElement ().toString () + ": " + String .format ("%,d" , type .getCount ()) + " blocks" );
256- }
257- reportLines .add (LINE_BREAK );
258-
259261 return reportLines ;
260262 }
261263
@@ -454,6 +456,8 @@ private void scanAsync(ChunkPair cp) {
454456 BlockData blockData = cp .chunkSnapshot .getBlockData (x , y , z );
455457 Material m = blockData .getMaterial ();
456458 boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight ;
459+ Location loc = new Location (cp .world , (double ) x + cp .chunkSnapshot .getX () * 16 , y ,
460+ (double ) z + cp .chunkSnapshot .getZ () * 16 );
457461 // Slabs can be doubled, so check them twice
458462 if (Tag .SLABS .isTagged (m )) {
459463 Slab slab = (Slab ) blockData ;
@@ -464,22 +468,26 @@ private void scanAsync(ChunkPair cp) {
464468 // Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real
465469 // chunk
466470 if (addon .isStackersEnabled () && (m .equals (Material .CAULDRON ) || m .equals (Material .SPAWNER ))) {
467- stackedBlocks .add (new Location (cp .world , (double ) x + cp .chunkSnapshot .getX () * 16 , y ,
468- (double ) z + cp .chunkSnapshot .getZ () * 16 ));
471+ stackedBlocks .add (loc );
469472 }
470473
471474 if (addon .isUltimateStackerEnabled () && !m .isAir ()) {
472- Location l = new Location (cp .chunk .getWorld (), x , y , z );
473- UltimateStackerCalc .addStackers (m , l , results , belowSeaLevel , limitCount (m ));
475+ UltimateStackerCalc .addStackers (m , loc , results , belowSeaLevel , limitCount (m ));
474476 }
475477
476478 // Scan chests
477- if (addon .getSettings ().isIncludeChests () && CHESTS .contains (m )) {
478-
479+ if (addon .getSettings ().isIncludeChests () && blockData instanceof Container ) {
479480 chestBlocks .add (cp .chunk );
480481 }
481- // Add the value of the block's material
482- checkBlock (m , belowSeaLevel );
482+
483+ // Spawners
484+ if (m == Material .SPAWNER ) {
485+ // Stash the spawner because the type cannot be obtained from the chunk snapshot
486+ this .spawners .put (loc , belowSeaLevel );
487+ } else {
488+ // Add the value of the block's material
489+ checkBlock (m , belowSeaLevel );
490+ }
483491 }
484492 }
485493 }
@@ -520,16 +528,23 @@ public CompletableFuture<Boolean> scanNextChunk() {
520528 return result ;
521529 }
522530
523- private Collection <String > sortedReport (int total , Multiset <Material > materialCount ) {
531+ private Collection <String > sortedReport (int total , Multiset <Object > uwCount ) {
524532 Collection <String > result = new ArrayList <>();
525- Iterable <Multiset .Entry <Material >> entriesSortedByCount = Multisets .copyHighestCountFirst (materialCount )
533+ Iterable <Multiset .Entry <Object >> entriesSortedByCount = Multisets .copyHighestCountFirst (uwCount )
526534 .entrySet ();
527- for (Entry <Material > en : entriesSortedByCount ) {
528- Material type = en .getElement ();
529-
530- int value = getValue (type );
535+ for (Entry <Object > en : entriesSortedByCount ) {
536+
537+ int value = 0 ;
538+ String name = "" ;
539+ if (en .getElement () instanceof Material md ) {
540+ value = Objects .requireNonNullElse (addon .getBlockConfig ().getValue (island .getWorld (), md ), 0 );
541+ name = Util .prettifyText (md .name ());
542+ } else if (en .getElement () instanceof EntityType et ) {
543+ name = Util .prettifyText (et .name ());
544+ value = Objects .requireNonNullElse (addon .getBlockConfig ().getValue (island .getWorld (), et ), 0 );
545+ }
531546
532- result .add (type . toString () + ":" + String .format ("%,d" , en .getCount ()) + " blocks x " + value + " = "
547+ result .add (name + " :" + String .format ("%,d" , en .getCount ()) + " blocks x " + value + " = "
533548 + (value * en .getCount ()));
534549 total += (value * en .getCount ());
535550
@@ -616,15 +631,32 @@ public void scanIsland(Pipeliner pipeliner) {
616631 pipeliner .getInProcessQueue ().remove (this );
617632 BentoBox .getInstance ().log ("Completed Level scan." );
618633 // Chunk finished
619- // This was the last chunk. Handle stacked blocks, then chests and exit
620- handleStackedBlocks ().thenCompose (v -> handleChests ()).thenRun (() -> {
634+ // This was the last chunk. Handle stacked blocks, spawners, chests and exit
635+ handleStackedBlocks ().thenCompose (v -> handleSpawners ()).thenCompose (v -> handleChests ())
636+ .thenRun (() -> {
621637 this .tidyUp ();
622638 this .getR ().complete (getResults ());
623639 });
624640 }
625641 });
626642 }
627643
644+ private CompletableFuture <Void > handleSpawners () {
645+ List <CompletableFuture <Void >> futures = new ArrayList <>();
646+ for (Map .Entry <Location , Boolean > en : this .spawners .entrySet ()) {
647+ CompletableFuture <Void > future = Util .getChunkAtAsync (en .getKey ()).thenAccept (c -> {
648+ if (en .getKey ().getBlock ().getType () == Material .SPAWNER ) {
649+ CreatureSpawner cs = (CreatureSpawner ) en .getKey ().getBlock ().getState ();
650+ checkSpawner (cs .getSpawnedType (), en .getValue ());
651+ }
652+ });
653+ futures .add (future );
654+ }
655+ // Return a CompletableFuture that completes when all futures are done
656+ return CompletableFuture .allOf (futures .toArray (new CompletableFuture [0 ]));
657+
658+ }
659+
628660 private CompletableFuture <Void > handleChests () {
629661 List <CompletableFuture <Void >> futures = new ArrayList <>();
630662 for (Chunk v : chestBlocks ) {
0 commit comments