11package net .superkat .tidal .water ;
22
33import it .unimi .dsi .fastutil .longs .Long2IntOpenHashMap ;
4- import it .unimi .dsi .fastutil .longs .Long2ObjectLinkedOpenHashMap ;
4+ import it .unimi .dsi .fastutil .longs .Long2ObjectOpenHashMap ;
5+ import it .unimi .dsi .fastutil .longs .LongArrayList ;
56import it .unimi .dsi .fastutil .objects .Object2ObjectOpenHashMap ;
67import it .unimi .dsi .fastutil .objects .ObjectArrayFIFOQueue ;
78import it .unimi .dsi .fastutil .objects .ObjectOpenHashSet ;
2627
2728import java .awt .*;
2829import java .util .Collection ;
29- import java .util .Iterator ;
3030import java .util .List ;
3131import java .util .Map ;
3232import java .util .Set ;
@@ -72,22 +72,25 @@ public class WaterHandler {
7272
7373 //Chunk scanner for each chunk - SCANNER VALUE SET TO NULL ONCE SCANNER IS DONE!!
7474 //The chunk is not removed from this list until it is unloaded!
75- public Long2ObjectLinkedOpenHashMap <ChunkScanner > scanners = new Long2ObjectLinkedOpenHashMap <>(81 , 0.25f );
75+ public Long2ObjectOpenHashMap <ChunkScanner > scanners = new Long2ObjectOpenHashMap <>(81 , 0.25f );
7676
7777 //Keep track of how many block updates have happened in a chunk - used to rescan chunks after enough(configurable) updates
7878 public Long2IntOpenHashMap chunkUpdates = new Long2IntOpenHashMap (81 , 0.25f );
7979
8080 //Set of water blocks waiting to have their closest site found
81- public Long2ObjectLinkedOpenHashMap <ObjectArrayFIFOQueue <BlockPos >> waitingWaterBlocks = new Long2ObjectLinkedOpenHashMap <>(81 , 0.25f );
81+ public Long2ObjectOpenHashMap <ObjectArrayFIFOQueue <BlockPos >> waitingWaterBlocks = new Long2ObjectOpenHashMap <>(81 , 0.25f );
8282
8383 //Set of shoreline sites, used to determine angle of area
84- public Long2ObjectLinkedOpenHashMap <Set <SitePos >> sites = new Long2ObjectLinkedOpenHashMap <>(81 , 0.25f );
84+ public Long2ObjectOpenHashMap <ObjectOpenHashSet <SitePos >> sites = new Long2ObjectOpenHashMap <>(81 , 0.25f );
85+
86+ //Caches all the sites into one set, not split by chunk.
87+ public ObjectOpenHashSet <SitePos > cachedSiteSet = new ObjectOpenHashSet <>();
8588
8689 //Keep track of which SitePos is closest to all scanned water blocks
87- public Long2ObjectLinkedOpenHashMap <Object2ObjectOpenHashMap <BlockPos , SitePos >> siteCache = new Long2ObjectLinkedOpenHashMap <>();
90+ public Long2ObjectOpenHashMap <Object2ObjectOpenHashMap <BlockPos , SitePos >> siteCache = new Long2ObjectOpenHashMap <>();
8891
8992 //All scanned shoreline blocks
90- public Long2ObjectLinkedOpenHashMap <Set <BlockPos >> shoreBlocks = new Long2ObjectLinkedOpenHashMap <>(81 , 0.25f );
93+ public Long2ObjectOpenHashMap <Set <BlockPos >> shoreBlocks = new Long2ObjectOpenHashMap <>(81 , 0.25f );
9194
9295 //boolean for if the initial joining/chunk reloading build is finished or not
9396 public boolean built = false ;
@@ -120,8 +123,8 @@ public void tick() {
120123
121124 if (built ) {
122125 boolean noMoreWaitingBlocks = tickWaitingWaterBlocks (false );
123- if (noMoreWaitingBlocks && recalcSiteCenters ) {
124- this .calcSiteCenters ();
126+ if (noMoreWaitingBlocks && recalcSiteCenters ) { //failsafe I guess?
127+ this .calcAllSiteCenters ();
125128 this .recalcSiteCenters = false ;
126129 }
127130 }
@@ -137,24 +140,17 @@ public void tick() {
137140 * @param pos The BlockPos to use for finding the closest SitePos
138141 * @return The closest SitePos, or null if no SitePos' are currently stored.
139142 */
140- @ Nullable
143+ @ Nullable //FIXME - optimize this(Hama said it should be easy)
141144 public SitePos findClosestSite (BlockPos pos ) {
142145 if (this .sites .isEmpty ()) return null ;
143146
144- Set <SitePos > siteSet ;
145- long chunkPosL = new ChunkPos (pos ).toLong ();
146- if (this .sites .get (chunkPosL ) != null ) {
147- //FIXME - this can cause a bug where, if there is a SitePos in the same chunk, but pretty much across the entire chunk,
148- // and there's a SitePos closer, but in the neighbouring chunk, it is assumed to be closer to the SitePos
149- // in the same chunk instead of knowing there's one closer block-wise, but in the neighbouring chunk.
150- siteSet = this .sites .get (chunkPosL );
151- } else {
152- siteSet = this .sites .values ().stream ().flatMap (Collection ::stream ).collect (Collectors .toSet ());
147+ if (this .cachedSiteSet == null || this .cachedSiteSet .isEmpty ()) {
148+ this .cacheSiteSet ();
153149 }
154150
155151 double distance = 0 ;
156152 SitePos closest = null ;
157- for (SitePos site : siteSet ) {
153+ for (SitePos site : this . cachedSiteSet ) {
158154 double dx = pos .getX () + 0.5 - site .getX ();
159155 double dz = pos .getZ () + 0.5 - site .getZ ();
160156 double dist = dx * dx + dz * dz ;
@@ -254,6 +250,9 @@ public boolean build() {
254250
255251 long scannerTime = Util .getMeasuringTimeMs ();
256252 int chunksScanned = 0 ;
253+
254+ //I believe doing it this way is faster than an iterator
255+ // LongArrayList finishedScanners = new LongArrayList();
257256 for (ChunkScanner scanner : this .scanners .values ()) {
258257 if (scanner == null ) continue ;
259258// long scannerStartTime = Util.getMeasuringTimeMs();
@@ -262,12 +261,14 @@ public boolean build() {
262261 while (!scanner .isFinished ()) {
263262 scanner .tick ();
264263 if (scanner .isFinished ()) {
265- scanners .put (scanner .chunkPos .toLong (), null );
264+ // finishedScanners.add(scanner.chunkPos.toLong());
265+ this .scanners .put (scanner .chunkPos .toLong (), null );
266266 MinecraftClient .getInstance ().player .playSound (SoundEvents .ITEM_TRIDENT_HIT_GROUND , 0.05f , 1f );
267267// Tidal.LOGGER.info("Scanner time: {} ms", Util.getMeasuringTimeMs() - scannerStartTime);
268268 }
269269 }
270270 }
271+ // finishedScanners.forEach(scannerPosL -> this.scanners.remove(scannerPosL));
271272
272273 Tidal .LOGGER .info ("Total scan time: {} ms" , Util .getMeasuringTimeMs () - scannerTime );
273274 Tidal .LOGGER .info ("Chunks scanned: {}" , chunksScanned );
@@ -280,7 +281,7 @@ public boolean build() {
280281 Tidal .LOGGER .info ("Site cache time: {} ms" , Util .getMeasuringTimeMs () - siteCacheTime );
281282
282283 long siteCenterCalcTime = Util .getMeasuringTimeMs ();
283- this .calcSiteCenters ();
284+ this .calcAllSiteCenters ();
284285 Tidal .LOGGER .info ("Site center calc time: {} ms" , Util .getMeasuringTimeMs () - siteCenterCalcTime );
285286
286287 Tidal .LOGGER .info ("Total Chunk Build time: {} ms" , Util .getMeasuringTimeMs () - overallStartTime );
@@ -295,7 +296,12 @@ public boolean build() {
295296 public void rebuild () {
296297 this .clear (); //clear all data(ticking scanners -> null, sites/shoreblocks/waterblocks all cleared)
297298
298- for (Map .Entry <Long , ChunkScanner > entry : this .scanners .sequencedEntrySet ()) {
299+ // for (ChunkPos chunkPos : this.tidalWaveHandler.getNearbyChunkPos()) {
300+ // long chunkPosL = chunkPos.toLong();
301+ // this.scanners.put(chunkPosL, new ChunkScanner(this, this.world, chunkPos));
302+ // }
303+
304+ for (Map .Entry <Long , ChunkScanner > entry : this .scanners .long2ObjectEntrySet ()) {
299305 ChunkPos chunkPos = new ChunkPos (entry .getKey ());
300306 long pos = chunkPos .toLong ();
301307 this .scanners .put (pos , new ChunkScanner (this , this .world , chunkPos ));
@@ -312,7 +318,10 @@ public void rebuild() {
312318 public void tickScheduledScanners (ClientPlayerEntity player ) {
313319 BlockPos playerPos = player .getBlockPos ();
314320 int chunkRadius = this .tidalWaveHandler .getChunkRadius (); //caching this call might help?
315- for (Map .Entry <Long , ChunkScanner > entry : this .scanners .sequencedEntrySet ()) {
321+ // Iterator<Map.Entry<Long, ChunkScanner>> iterator = this.scanners.sequencedEntrySet().iterator();
322+
323+ // LongArrayList finishedScanners = new LongArrayList();
324+ for (Map .Entry <Long , ChunkScanner > entry : this .scanners .long2ObjectEntrySet ()) {
316325 if (entry .getValue () == null ) continue ;
317326
318327 long chunkPosL = entry .getKey ();
@@ -324,10 +333,12 @@ public void tickScheduledScanners(ClientPlayerEntity player) {
324333 }
325334
326335 if (scanner .isFinished ()) {
336+ // finishedScanners.add(chunkPosL);
327337 scanners .put (chunkPosL , null );
328338 MinecraftClient .getInstance ().player .playSound (SoundEvents .ITEM_TRIDENT_HIT_GROUND , 0.25f , 1f );
329339 }
330340 }
341+ // finishedScanners.forEach(scannerPosL -> this.scanners.remove(scannerPosL));
331342 }
332343
333344 /**
@@ -339,33 +350,34 @@ public void tickScheduledScanners(ClientPlayerEntity player) {
339350 * @return True if there are no more waiting water blocks
340351 */
341352 public boolean tickWaitingWaterBlocks (boolean assumeFullScan ) {
342- Iterator <Map .Entry <Long , ObjectArrayFIFOQueue <BlockPos >>> iterator = this .waitingWaterBlocks .sequencedEntrySet ().iterator ();
343-
344- while (iterator .hasNext ()) {
345- Map .Entry <Long , ObjectArrayFIFOQueue <BlockPos >> entry = iterator .next ();
346- if (entry .getValue () == null ) continue ;
353+ LongArrayList finishedBlocks = new LongArrayList ();
354+ for (Map .Entry <Long , ObjectArrayFIFOQueue <BlockPos >> entry : this .waitingWaterBlocks .long2ObjectEntrySet ()) {
355+ if (entry .getValue () == null ) continue ;
347356 long chunkPosL = entry .getKey ();
348357 //the nearbyScannersFinished call could probably be optimized?
349358 if (!assumeFullScan && !nearbyScannersFinished (chunkPosL )) continue ;
350359
351360 ObjectArrayFIFOQueue <BlockPos > queue = entry .getValue ();
352361 boolean finished = false ;
353362 for (int i = 0 ; i < 10 ; i ++) {
354- if (queue .size () <= 0 ) {
363+ if (queue .size () <= 0 ) {
355364 finished = true ;
356365 break ;
357366 }
358367 BlockPos pos = queue .dequeue ();
359368 getSiteForPos (pos ); //caches pos' closest site
360369 }
361370
362- if (finished || queue .size () <= 0 ) {
363- iterator . remove ( );
371+ if (finished || queue .size () <= 0 ) {
372+ finishedBlocks . add ( chunkPosL );
364373 recalcSiteCenters = true ;
374+ this .calcSiteCenter (chunkPosL );
365375 MinecraftClient .getInstance ().player .playSound (SoundEvents .BLOCK_VAULT_ACTIVATE , 0.1f , 1f );
366376 }
367377 }
368378
379+ finishedBlocks .forEach (chunkPosL -> this .waitingWaterBlocks .remove (chunkPosL ));
380+
369381 return this .waitingWaterBlocks .isEmpty ();
370382 }
371383
@@ -376,7 +388,7 @@ public boolean tickWaitingWaterBlocks(boolean assumeFullScan) {
376388 * @return If the nearby ChunkScanners are finished.
377389 */
378390 public boolean nearbyScannersFinished (long chunkPosL ) {
379- int radius = 3 ; //will be configurable later
391+ int radius = 1 ; //will be configurable later
380392 ChunkPos pos = new ChunkPos (chunkPosL );
381393 ChunkPos start = new ChunkPos (pos .x + radius , pos .z + radius );
382394 ChunkPos end = new ChunkPos (pos .x - radius , pos .z - radius );
@@ -392,8 +404,16 @@ public boolean nearbyScannersFinished(long chunkPosL) {
392404 return true ;
393405 }
394406
407+ public void calcSiteCenter (long chunkPosL ) {
408+ if (!this .sites .containsKey (chunkPosL )) return ;
409+ for (SitePos site : this .sites .get (chunkPosL )) {
410+ site .updateCenter ();
411+ }
412+ MinecraftClient .getInstance ().player .playSound (SoundEvents .BLOCK_BEEHIVE_ENTER , 0.3f , 1f );
413+ }
414+
395415 //I feel comfortable doing this because this calculation is usually only taken 1-3ms for me
396- public void calcSiteCenters () {
416+ public void calcAllSiteCenters () {
397417 for (SitePos site : this .sites .values ().stream ().flatMap (Collection ::stream ).toList ()) {
398418 site .updateCenter ();
399419 }
@@ -416,6 +436,11 @@ public boolean scannerInDistance(BlockPos playerBlockPos, long chunkPosL, int ch
416436 return distance < chunkRadius ;
417437 }
418438
439+ public void cacheSiteSet () {
440+ //caching this list saved ~200ms during a build/rebuild
441+ this .cachedSiteSet = new ObjectOpenHashSet <>(this .sites .values ().stream ().flatMap (Collection ::stream ).collect (Collectors .toSet ()));
442+ }
443+
419444 /**
420445 * Schedules a chunk to be scanned water blocks, shoreblocks, sites, etc. Called when a new chunk is loaded.
421446 *
@@ -471,6 +496,7 @@ public void createSitePos(BlockPos pos) {
471496 long chunkPosL = new ChunkPos (pos ).toLong ();
472497 SitePos site = new SitePos (pos );
473498 sites .computeIfAbsent (chunkPosL , aLong -> new ObjectOpenHashSet <>()).add (site );
499+ this .cachedSiteSet .add (site );
474500 }
475501
476502 /**
@@ -506,12 +532,14 @@ public void removeChunkFromTrackers(long chunkPosL) {
506532 this .shoreBlocks .remove (chunkPosL );
507533 this .siteCache .remove (chunkPosL );
508534 this .sites .remove (chunkPosL );
535+ this .cachedSiteSet .clear (); //resets it
509536 }
510537
511538 public void clear () {
512539 this .waitingWaterBlocks .clear ();
513540 this .shoreBlocks .clear ();
514541 this .sites .clear ();
515542 this .siteCache .clear ();
543+ this .cachedSiteSet .clear ();
516544 }
517545}
0 commit comments