2021-12-05 04:33:02 +01:00
package org.dynmap.common.chunk ;
2022-03-30 18:37:29 +02:00
import java.util.* ;
2022-03-31 14:04:36 +02:00
import java.util.concurrent.atomic.AtomicInteger ;
2022-03-30 18:37:29 +02:00
import java.util.function.BiConsumer ;
import java.util.function.Consumer ;
import java.util.function.Supplier ;
2021-12-05 04:33:02 +01:00
import org.dynmap.DynmapChunk ;
import org.dynmap.DynmapCore ;
import org.dynmap.DynmapWorld ;
import org.dynmap.common.BiomeMap ;
import org.dynmap.common.chunk.GenericChunkCache.ChunkCacheRec ;
import org.dynmap.hdmap.HDBlockModels ;
import org.dynmap.renderer.DynmapBlockState ;
import org.dynmap.renderer.RenderPatchFactory ;
import org.dynmap.utils.DynIntHashMap ;
import org.dynmap.utils.MapChunkCache ;
import org.dynmap.utils.MapIterator ;
import org.dynmap.utils.BlockStep ;
2021-12-07 07:01:07 +01:00
import org.dynmap.utils.DataBitsPacked ;
2021-12-05 04:33:02 +01:00
import org.dynmap.utils.VisibilityLimit ;
/ * *
* Abstract container for handling map cache and map iterator , using DynmapChunks
* /
public abstract class GenericMapChunkCache extends MapChunkCache {
protected DynmapWorld dw ;
private int nsect ;
private int sectoff ; // Offset for sake of negative section indexes
private List < DynmapChunk > chunks ;
private ListIterator < DynmapChunk > iterator ;
private int x_min , x_max , z_min , z_max ;
private int x_dim ;
private HiddenChunkStyle hidestyle = HiddenChunkStyle . FILL_AIR ;
private List < VisibilityLimit > visible_limits = null ;
private List < VisibilityLimit > hidden_limits = null ;
private boolean isempty = true ;
private int snapcnt ;
private GenericChunk [ ] snaparray ; /* Index = (x-x_min) + ((z-z_min)*x_dim) */
private boolean [ ] [ ] isSectionNotEmpty ; /* Indexed by snapshot index, then by section index */
2022-04-01 14:16:57 +02:00
private AtomicInteger loadingChunks = new AtomicInteger ( 0 ) ; //the amount of threads loading chunks at this moment, used by async loading
2021-12-05 04:33:02 +01:00
private static final BlockStep unstep [ ] = { BlockStep . X_MINUS , BlockStep . Y_MINUS , BlockStep . Z_MINUS ,
BlockStep . X_PLUS , BlockStep . Y_PLUS , BlockStep . Z_PLUS } ;
/ * *
* Iterator for traversing map chunk cache ( base is for non - snapshot )
* /
public class OurMapIterator implements MapIterator {
private int x , y , z , chunkindex , bx , bz ;
private GenericChunk snap ;
private BlockStep laststep ;
private DynmapBlockState blk ;
private final int worldheight ;
private final int ymin ;
2022-10-14 14:37:36 +02:00
OurMapIterator ( int x0 , int y0 , int z0 ) {
2021-12-05 04:33:02 +01:00
initialize ( x0 , y0 , z0 ) ;
worldheight = dw . worldheight ;
ymin = dw . minY ;
}
@Override
public final void initialize ( int x0 , int y0 , int z0 ) {
this . x = x0 ;
this . y = y0 ;
this . z = z0 ;
this . chunkindex = ( ( x > > 4 ) - x_min ) + ( ( ( z > > 4 ) - z_min ) * x_dim ) ;
this . bx = x & 0xF ;
this . bz = z & 0xF ;
if ( ( chunkindex > = snapcnt ) | | ( chunkindex < 0 ) ) {
2021-12-28 21:37:20 +01:00
snap = getEmpty ( ) ;
2021-12-05 04:33:02 +01:00
} else {
snap = snaparray [ chunkindex ] ;
}
laststep = BlockStep . Y_MINUS ;
if ( ( y > = ymin ) & & ( y < worldheight ) ) {
blk = null ;
} else {
blk = DynmapBlockState . AIR ;
}
}
@Override
public int getBlockSkyLight ( ) {
try {
return snap . getBlockSkyLight ( bx , y , bz ) ;
} catch ( ArrayIndexOutOfBoundsException aioobx ) {
return 15 ;
}
}
@Override
public final int getBlockEmittedLight ( ) {
try {
return snap . getBlockEmittedLight ( bx , y , bz ) ;
} catch ( ArrayIndexOutOfBoundsException aioobx ) {
return 0 ;
}
}
2021-12-29 05:14:19 +01:00
@Override
/ * *
* Get block sky and emitted light , relative to current coordinate
* @return ( emitted light * 256 ) + sky light
* /
public final int getBlockLight ( BlockStep step ) {
int emit = 0 , sky = 15 ;
GenericChunkSection sect ;
if ( step . yoff ! = 0 ) { // Y coord - snap is valid already
int ny = y + step . yoff ;
sect = snap . getSection ( ny ) ;
emit = sect . emitted . getLight ( x , ny , z ) ;
sky = sect . sky . getLight ( x , ny , z ) ;
}
else {
int nx = x + step . xoff ;
int nz = z + step . zoff ;
int nchunkindex = ( ( nx > > 4 ) - x_min ) + ( ( ( nz > > 4 ) - z_min ) * x_dim ) ;
if ( ( nchunkindex < snapcnt ) & & ( nchunkindex > = 0 ) ) {
sect = snaparray [ nchunkindex ] . getSection ( y ) ;
emit = sect . emitted . getLight ( nx , y , nz ) ;
sky = sect . sky . getLight ( nx , y , nz ) ;
}
}
return ( emit < < 8 ) + sky ;
}
2021-12-05 04:33:02 +01:00
@Override
2022-01-30 21:23:00 +01:00
/ * *
* Get block sky and emitted light , relative to current coordinate
* @return ( emitted light * 256 ) + sky light
* /
public final int getBlockLight ( int xoff , int yoff , int zoff ) {
int emit = 0 , sky = 15 ;
int nx = x + xoff ;
int ny = y + yoff ;
int nz = z + zoff ;
GenericChunkSection sect ;
int nchunkindex = ( ( nx > > 4 ) - x_min ) + ( ( ( nz > > 4 ) - z_min ) * x_dim ) ;
if ( ( nchunkindex < snapcnt ) & & ( nchunkindex > = 0 ) ) {
sect = snaparray [ nchunkindex ] . getSection ( ny ) ;
emit = sect . emitted . getLight ( nx , ny , nz ) ;
sky = sect . sky . getLight ( nx , ny , nz ) ;
}
return ( emit < < 8 ) + sky ;
}
@Override
2021-12-05 04:33:02 +01:00
public final BiomeMap getBiome ( ) {
try {
2021-12-05 08:57:59 +01:00
return snap . getBiome ( bx , y , bz ) ;
2021-12-05 04:33:02 +01:00
} catch ( Exception ex ) {
return BiomeMap . NULL ;
}
}
2022-10-14 14:37:36 +02:00
private final BiomeMap getBiomeRel ( int dx , int dz ) {
2021-12-05 08:57:59 +01:00
int nx = x + dx ;
int nz = z + dz ;
int nchunkindex = ( ( nx > > 4 ) - x_min ) + ( ( ( nz > > 4 ) - z_min ) * x_dim ) ;
if ( ( nchunkindex > = snapcnt ) | | ( nchunkindex < 0 ) ) {
return BiomeMap . NULL ;
} else {
2021-12-05 19:53:42 +01:00
return snaparray [ nchunkindex ] . getBiome ( nx , y , nz ) ;
2021-12-05 08:57:59 +01:00
}
}
2021-12-05 04:33:02 +01:00
@Override
2022-10-14 14:37:36 +02:00
public final int getSmoothGrassColorMultiplier ( int [ ] colormap ) {
2021-12-05 04:33:02 +01:00
int mult = 0xFFFFFF ;
try {
2021-12-05 08:57:59 +01:00
int raccum = 0 ;
int gaccum = 0 ;
int baccum = 0 ;
int cnt = 0 ;
for ( int dx = - 1 ; dx < = 1 ; dx + + ) {
for ( int dz = - 1 ; dz < = 1 ; dz + + ) {
BiomeMap bm = getBiomeRel ( dx , dz ) ;
2021-12-16 04:08:32 +01:00
if ( bm = = BiomeMap . NULL ) continue ;
2022-10-14 14:37:36 +02:00
int rmult = getGrassColor ( bm , colormap , getX ( ) + dx , getZ ( ) + dz ) ;
2021-12-05 08:57:59 +01:00
raccum + = ( rmult > > 16 ) & 0xFF ;
gaccum + = ( rmult > > 8 ) & 0xFF ;
baccum + = rmult & 0xFF ;
cnt + + ;
2021-12-05 04:33:02 +01:00
}
}
2021-12-16 04:08:32 +01:00
cnt = ( cnt > 0 ) ? cnt : 1 ;
2021-12-05 08:57:59 +01:00
mult = ( ( raccum / cnt ) < < 16 ) | ( ( gaccum / cnt ) < < 8 ) | ( baccum / cnt ) ;
2021-12-05 04:33:02 +01:00
} catch ( Exception x ) {
//Log.info("getSmoothGrassColorMultiplier() error: " + x);
mult = 0xFFFFFF ;
}
//Log.info(String.format("getSmoothGrassColorMultiplier() at %d, %d = %X", x, z, mult));
return mult ;
}
@Override
2022-10-14 14:37:36 +02:00
public final int getSmoothFoliageColorMultiplier ( int [ ] colormap ) {
2021-12-05 04:33:02 +01:00
int mult = 0xFFFFFF ;
try {
2021-12-05 08:57:59 +01:00
int raccum = 0 ;
int gaccum = 0 ;
int baccum = 0 ;
int cnt = 0 ;
for ( int dx = - 1 ; dx < = 1 ; dx + + ) {
for ( int dz = - 1 ; dz < = 1 ; dz + + ) {
BiomeMap bm = getBiomeRel ( dx , dz ) ;
2021-12-16 04:08:32 +01:00
if ( bm = = BiomeMap . NULL ) continue ;
2022-10-14 14:37:36 +02:00
int rmult = getFoliageColor ( bm , colormap , getX ( ) + dx , getZ ( ) + dz ) ;
2021-12-05 08:57:59 +01:00
raccum + = ( rmult > > 16 ) & 0xFF ;
gaccum + = ( rmult > > 8 ) & 0xFF ;
baccum + = rmult & 0xFF ;
cnt + + ;
2021-12-05 04:33:02 +01:00
}
}
2021-12-16 04:08:32 +01:00
cnt = ( cnt > 0 ) ? cnt : 1 ;
2021-12-05 08:57:59 +01:00
mult = ( ( raccum / cnt ) < < 16 ) | ( ( gaccum / cnt ) < < 8 ) | ( baccum / cnt ) ;
2021-12-05 04:33:02 +01:00
} catch ( Exception x ) {
//Log.info("getSmoothFoliageColorMultiplier() error: " + x);
}
//Log.info(String.format("getSmoothFoliageColorMultiplier() at %d, %d = %X", x, z, mult));
return mult ;
}
@Override
public final int getSmoothColorMultiplier ( int [ ] colormap , int [ ] swampmap ) {
int mult = 0xFFFFFF ;
try {
2021-12-05 08:57:59 +01:00
int raccum = 0 ;
int gaccum = 0 ;
int baccum = 0 ;
int cnt = 0 ;
for ( int dx = - 1 ; dx < = 1 ; dx + + ) {
for ( int dz = - 1 ; dz < = 1 ; dz + + ) {
BiomeMap bm = getBiomeRel ( dx , dz ) ;
2021-12-16 04:08:32 +01:00
if ( bm = = BiomeMap . NULL ) continue ;
2021-12-05 08:57:59 +01:00
int rmult ;
if ( bm = = BiomeMap . SWAMPLAND ) {
rmult = swampmap [ bm . biomeLookup ( ) ] ;
} else {
rmult = colormap [ bm . biomeLookup ( ) ] ;
2021-12-05 04:33:02 +01:00
}
2021-12-05 08:57:59 +01:00
raccum + = ( rmult > > 16 ) & 0xFF ;
gaccum + = ( rmult > > 8 ) & 0xFF ;
baccum + = rmult & 0xFF ;
cnt + + ;
2021-12-05 04:33:02 +01:00
}
}
2021-12-16 04:08:32 +01:00
cnt = ( cnt > 0 ) ? cnt : 1 ;
2021-12-05 08:57:59 +01:00
mult = ( ( raccum / cnt ) < < 16 ) | ( ( gaccum / cnt ) < < 8 ) | ( baccum / cnt ) ;
2021-12-05 04:33:02 +01:00
} catch ( Exception x ) {
//Log.info("getSmoothColorMultiplier() error: " + x);
}
//Log.info(String.format("getSmoothColorMultiplier() at %d, %d = %X", x, z, mult));
return mult ;
}
@Override
public final int getSmoothWaterColorMultiplier ( ) {
2021-12-16 04:08:32 +01:00
int multv = 0xFFFFFF ;
2021-12-05 04:33:02 +01:00
try {
2021-12-05 08:57:59 +01:00
int raccum = 0 ;
int gaccum = 0 ;
int baccum = 0 ;
int cnt = 0 ;
for ( int dx = - 1 ; dx < = 1 ; dx + + ) {
for ( int dz = - 1 ; dz < = 1 ; dz + + ) {
BiomeMap bm = getBiomeRel ( dx , dz ) ;
2021-12-16 04:08:32 +01:00
if ( bm = = BiomeMap . NULL ) continue ;
2021-12-05 08:57:59 +01:00
int rmult = bm . getWaterColorMult ( ) ;
raccum + = ( rmult > > 16 ) & 0xFF ;
gaccum + = ( rmult > > 8 ) & 0xFF ;
baccum + = rmult & 0xFF ;
cnt + + ;
2021-12-05 04:33:02 +01:00
}
}
2021-12-16 04:08:32 +01:00
cnt = ( cnt > 0 ) ? cnt : 1 ;
2021-12-05 08:57:59 +01:00
multv = ( ( raccum / cnt ) < < 16 ) | ( ( gaccum / cnt ) < < 8 ) | ( baccum / cnt ) ;
2021-12-05 04:33:02 +01:00
} catch ( Exception x ) {
//Log.info("getSmoothWaterColorMultiplier(nomap) error: " + x);
}
//Log.info(String.format("getSmoothWaterColorMultiplier(nomap) at %d, %d = %X", x, z, multv));
return multv ;
}
@Override
public final int getSmoothWaterColorMultiplier ( int [ ] colormap ) {
int mult = 0xFFFFFF ;
try {
2021-12-05 08:57:59 +01:00
int raccum = 0 ;
int gaccum = 0 ;
int baccum = 0 ;
int cnt = 0 ;
for ( int dx = - 1 ; dx < = 1 ; dx + + ) {
for ( int dz = - 1 ; dz < = 1 ; dz + + ) {
BiomeMap bm = getBiomeRel ( dx , dz ) ;
2021-12-16 04:08:32 +01:00
if ( bm = = BiomeMap . NULL ) continue ;
2021-12-05 08:57:59 +01:00
int rmult = colormap [ bm . biomeLookup ( ) ] ;
raccum + = ( rmult > > 16 ) & 0xFF ;
gaccum + = ( rmult > > 8 ) & 0xFF ;
baccum + = rmult & 0xFF ;
cnt + + ;
2021-12-05 04:33:02 +01:00
}
}
2021-12-16 04:08:32 +01:00
cnt = ( cnt > 0 ) ? cnt : 1 ;
2021-12-05 08:57:59 +01:00
mult = ( ( raccum / cnt ) < < 16 ) | ( ( gaccum / cnt ) < < 8 ) | ( baccum / cnt ) ;
2021-12-05 04:33:02 +01:00
} catch ( Exception x ) {
//Log.info("getSmoothWaterColorMultiplier() error: " + x);
}
//Log.info(String.format("getSmoothWaterColorMultiplier() at %d, %d = %X", x, z, mult));
return mult ;
}
/ * *
* Step current position in given direction
* /
@Override
public final void stepPosition ( BlockStep step ) {
blk = null ;
switch ( step . ordinal ( ) ) {
case 0 :
x + + ;
bx + + ;
if ( bx = = 16 ) /* Next chunk? */
{
bx = 0 ;
chunkindex + + ;
if ( ( chunkindex > = snapcnt ) | | ( chunkindex < 0 ) ) {
2021-12-28 21:37:20 +01:00
snap = getEmpty ( ) ;
2021-12-05 04:33:02 +01:00
} else {
snap = snaparray [ chunkindex ] ;
}
}
break ;
case 1 :
y + + ;
if ( y > = worldheight ) {
blk = DynmapBlockState . AIR ;
}
break ;
case 2 :
z + + ;
bz + + ;
if ( bz = = 16 ) /* Next chunk? */
{
bz = 0 ;
chunkindex + = x_dim ;
if ( ( chunkindex > = snapcnt ) | | ( chunkindex < 0 ) ) {
2021-12-28 21:37:20 +01:00
snap = getEmpty ( ) ;
2021-12-05 04:33:02 +01:00
} else {
snap = snaparray [ chunkindex ] ;
}
}
break ;
case 3 :
x - - ;
bx - - ;
if ( bx = = - 1 ) /* Next chunk? */
{
bx = 15 ;
chunkindex - - ;
if ( ( chunkindex > = snapcnt ) | | ( chunkindex < 0 ) ) {
2021-12-28 21:37:20 +01:00
snap = getEmpty ( ) ;
2021-12-05 04:33:02 +01:00
} else {
snap = snaparray [ chunkindex ] ;
}
}
break ;
case 4 :
y - - ;
if ( y < ymin ) {
blk = DynmapBlockState . AIR ;
}
break ;
case 5 :
z - - ;
bz - - ;
if ( bz = = - 1 ) /* Next chunk? */
{
bz = 15 ;
chunkindex - = x_dim ;
if ( ( chunkindex > = snapcnt ) | | ( chunkindex < 0 ) ) {
2021-12-28 21:37:20 +01:00
snap = getEmpty ( ) ;
2021-12-05 04:33:02 +01:00
} else {
snap = snaparray [ chunkindex ] ;
}
}
break ;
}
laststep = step ;
}
/ * *
* Unstep current position to previous position
* /
@Override
2021-12-07 07:24:05 +01:00
public final BlockStep unstepPosition ( ) {
2021-12-05 04:33:02 +01:00
BlockStep ls = laststep ;
stepPosition ( unstep [ ls . ordinal ( ) ] ) ;
return ls ;
}
/ * *
* Unstep current position in oppisite director of given step
* /
@Override
2021-12-07 07:24:05 +01:00
public final void unstepPosition ( BlockStep s ) {
2021-12-05 04:33:02 +01:00
stepPosition ( unstep [ s . ordinal ( ) ] ) ;
}
@Override
public final void setY ( int y ) {
if ( y > this . y ) {
laststep = BlockStep . Y_PLUS ;
} else {
laststep = BlockStep . Y_MINUS ;
}
this . y = y ;
if ( ( y < ymin ) | | ( y > = worldheight ) ) {
blk = DynmapBlockState . AIR ;
} else {
blk = null ;
}
}
@Override
public final int getX ( ) {
return x ;
}
@Override
public final int getY ( ) {
return y ;
}
@Override
public final int getZ ( ) {
return z ;
}
@Override
public final DynmapBlockState getBlockTypeAt ( BlockStep s ) {
2021-12-19 21:39:53 +01:00
return getBlockTypeAt ( s . xoff , s . yoff , s . zoff ) ;
2021-12-05 04:33:02 +01:00
}
@Override
2021-12-07 07:24:05 +01:00
public final BlockStep getLastStep ( ) {
2021-12-05 04:33:02 +01:00
return laststep ;
}
@Override
2021-12-07 07:24:05 +01:00
public final int getWorldHeight ( ) {
2021-12-05 04:33:02 +01:00
return worldheight ;
}
@Override
2021-12-07 07:24:05 +01:00
public final long getBlockKey ( ) {
2021-12-05 04:33:02 +01:00
return ( ( ( chunkindex * ( worldheight - ymin ) ) + ( y - ymin ) ) < < 8 ) | ( bx < < 4 ) | bz ;
}
@Override
2021-12-07 07:24:05 +01:00
public final RenderPatchFactory getPatchFactory ( ) {
2021-12-05 04:33:02 +01:00
return HDBlockModels . getPatchDefinitionFactory ( ) ;
}
@Override
2021-12-07 07:24:05 +01:00
public final Object getBlockTileEntityField ( String fieldId ) {
2021-12-05 04:33:02 +01:00
// TODO: handle tile entities here
return null ;
}
@Override
2021-12-07 07:24:05 +01:00
public final DynmapBlockState getBlockTypeAt ( int xoff , int yoff , int zoff ) {
2021-12-19 21:39:53 +01:00
int nx = x + xoff ;
int ny = y + yoff ;
int nz = z + zoff ;
int nchunkindex = ( ( nx > > 4 ) - x_min ) + ( ( ( nz > > 4 ) - z_min ) * x_dim ) ;
if ( ( nchunkindex > = snapcnt ) | | ( nchunkindex < 0 ) ) {
2021-12-05 04:33:02 +01:00
return DynmapBlockState . AIR ;
2021-12-19 21:39:53 +01:00
} else {
return snaparray [ nchunkindex ] . getBlockType ( nx & 0xF , ny , nz & 0xF ) ;
2021-12-05 04:33:02 +01:00
}
}
@Override
2021-12-07 07:24:05 +01:00
public final Object getBlockTileEntityFieldAt ( String fieldId , int xoff , int yoff , int zoff ) {
2021-12-05 04:33:02 +01:00
return null ;
}
@Override
2021-12-07 07:24:05 +01:00
public final long getInhabitedTicks ( ) {
2021-12-05 04:33:02 +01:00
try {
return snap . getInhabitedTicks ( ) ;
} catch ( Exception x ) {
return 0 ;
}
}
@Override
2021-12-07 07:24:05 +01:00
public final DynmapBlockState getBlockType ( ) {
2021-12-05 04:33:02 +01:00
if ( blk = = null ) {
blk = snap . getBlockType ( bx , y , bz ) ;
}
return blk ;
}
2021-12-20 04:02:35 +01:00
@Override
public int getDataVersion ( ) {
return ( snap ! = null ) ? snap . dataVersion : 0 ;
}
@Override
public String getChunkStatus ( ) {
return ( snap ! = null ) ? snap . chunkStatus : null ;
}
2021-12-05 04:33:02 +01:00
}
2022-10-14 14:37:36 +02:00
public int getGrassColor ( BiomeMap bm , int [ ] colormap , int x , int z ) {
return bm . getModifiedGrassMultiplier ( colormap [ bm . biomeLookup ( ) ] ) ;
}
public int getFoliageColor ( BiomeMap bm , int [ ] colormap , int x , int z ) {
return bm . getModifiedFoliageMultiplier ( colormap [ bm . biomeLookup ( ) ] ) ;
}
2021-12-05 04:33:02 +01:00
private class OurEndMapIterator extends OurMapIterator {
OurEndMapIterator ( int x0 , int y0 , int z0 ) {
super ( x0 , y0 , z0 ) ;
}
@Override
public final int getBlockSkyLight ( ) {
return 15 ;
}
}
2021-12-28 21:37:20 +01:00
private static final GenericChunkSection STONESECTION = ( new GenericChunkSection . Builder ( ) ) . singleBiome ( BiomeMap . PLAINS ) . singleBlockState ( DynmapBlockState . getBaseStateByName ( DynmapBlockState . STONE_BLOCK ) ) . build ( ) ;
private static final GenericChunkSection WATERSECTION = ( new GenericChunkSection . Builder ( ) ) . singleBiome ( BiomeMap . OCEAN ) . singleBlockState ( DynmapBlockState . getBaseStateByName ( DynmapBlockState . WATER_BLOCK ) ) . build ( ) ;
2021-12-05 04:33:02 +01:00
2021-12-28 21:37:20 +01:00
private GenericChunkCache cache ;
2021-12-05 04:33:02 +01:00
2021-12-28 21:37:20 +01:00
// Lazy generic chunks (tailored to height of world)
private GenericChunk empty_chunk ;
private GenericChunk stone_chunk ;
private GenericChunk ocean_chunk ;
private final GenericChunk getEmpty ( ) {
if ( empty_chunk = = null ) {
empty_chunk = ( new GenericChunk . Builder ( dw . minY , dw . worldheight ) ) . build ( ) ;
2021-12-05 04:33:02 +01:00
}
2021-12-28 21:37:20 +01:00
return empty_chunk ;
}
private final GenericChunk getStone ( ) {
if ( stone_chunk = = null ) {
GenericChunk . Builder bld = new GenericChunk . Builder ( dw . minY , dw . worldheight ) ;
for ( int sy = - sectoff ; sy < 4 ; sy + + ) { bld . addSection ( sy , STONESECTION ) ; }
stone_chunk = bld . build ( ) ;
}
return stone_chunk ;
}
private final GenericChunk getOcean ( ) {
if ( ocean_chunk = = null ) {
GenericChunk . Builder bld = new GenericChunk . Builder ( dw . minY , dw . worldheight ) ;
for ( int sy = - sectoff ; sy < 3 ; sy + + ) { bld . addSection ( sy , STONESECTION ) ; }
bld . addSection ( 3 , WATERSECTION ) ; // Put stone with ocean on top - less expensive render
ocean_chunk = bld . build ( ) ;
}
return ocean_chunk ;
2021-12-05 04:33:02 +01:00
}
/ * *
* Construct empty cache
* /
public GenericMapChunkCache ( GenericChunkCache c ) {
2021-12-28 21:37:20 +01:00
cache = c ; // Save reference to cache
2021-12-05 04:33:02 +01:00
}
public void setChunks ( DynmapWorld dw , List < DynmapChunk > chunks ) {
this . dw = dw ;
nsect = ( dw . worldheight - dw . minY ) > > 4 ;
sectoff = ( - dw . minY ) > > 4 ;
this . chunks = chunks ;
/* Compute range */
if ( chunks . size ( ) = = 0 ) {
this . x_min = 0 ;
this . x_max = 0 ;
this . z_min = 0 ;
this . z_max = 0 ;
x_dim = 1 ;
}
else {
x_min = x_max = chunks . get ( 0 ) . x ;
z_min = z_max = chunks . get ( 0 ) . z ;
for ( DynmapChunk c : chunks ) {
if ( c . x > x_max ) {
x_max = c . x ;
}
if ( c . x < x_min ) {
x_min = c . x ;
}
if ( c . z > z_max ) {
z_max = c . z ;
}
if ( c . z < z_min ) {
z_min = c . z ;
}
}
x_dim = x_max - x_min + 1 ;
}
snapcnt = x_dim * ( z_max - z_min + 1 ) ;
snaparray = new GenericChunk [ snapcnt ] ;
isSectionNotEmpty = new boolean [ snapcnt ] [ ] ;
}
private boolean isChunkVisible ( DynmapChunk chunk ) {
boolean vis = true ;
if ( visible_limits ! = null ) {
vis = false ;
for ( VisibilityLimit limit : visible_limits ) {
if ( limit . doIntersectChunk ( chunk . x , chunk . z ) ) {
vis = true ;
break ;
}
}
}
if ( vis & & ( hidden_limits ! = null ) ) {
for ( VisibilityLimit limit : hidden_limits ) {
if ( limit . doIntersectChunk ( chunk . x , chunk . z ) ) {
vis = false ;
break ;
}
}
}
return vis ;
}
private boolean tryChunkCache ( DynmapChunk chunk , boolean vis ) {
/* Check if cached chunk snapshot found */
GenericChunk ss = null ;
ChunkCacheRec ssr = cache . getSnapshot ( dw . getName ( ) , chunk . x , chunk . z ) ;
if ( ssr ! = null ) {
ss = ssr . ss ;
if ( ! vis ) {
if ( hidestyle = = HiddenChunkStyle . FILL_STONE_PLAIN ) {
2021-12-28 21:37:20 +01:00
ss = getStone ( ) ;
2021-12-05 04:33:02 +01:00
} else if ( hidestyle = = HiddenChunkStyle . FILL_OCEAN ) {
2021-12-28 21:37:20 +01:00
ss = getOcean ( ) ;
2021-12-05 04:33:02 +01:00
} else {
2021-12-28 21:37:20 +01:00
ss = getEmpty ( ) ; ;
2021-12-05 04:33:02 +01:00
}
}
int idx = ( chunk . x - x_min ) + ( chunk . z - z_min ) * x_dim ;
snaparray [ idx ] = ss ;
}
return ( ssr ! = null ) ;
}
// Prep snapshot and add to cache
private void prepChunkSnapshot ( DynmapChunk chunk , GenericChunk ss ) {
DynIntHashMap tileData = new DynIntHashMap ( ) ;
ChunkCacheRec ssr = new ChunkCacheRec ( ) ;
ssr . ss = ss ;
ssr . tileData = tileData ;
cache . putSnapshot ( dw . getName ( ) , chunk . x , chunk . z , ssr ) ;
}
// Load generic chunk from existing and already loaded chunk
protected abstract GenericChunk getLoadedChunk ( DynmapChunk ch ) ;
// Load generic chunk from unloaded chunk
protected abstract GenericChunk loadChunk ( DynmapChunk ch ) ;
2022-03-31 15:18:02 +02:00
// Load generic chunk from existing and already loaded chunk async
2022-03-30 18:37:29 +02:00
protected Supplier < GenericChunk > getLoadedChunkAsync ( DynmapChunk ch ) {
2022-03-31 15:18:02 +02:00
throw new IllegalStateException ( " Not implemeted " ) ;
2022-03-30 18:37:29 +02:00
}
// Load generic chunks from unloaded chunk async
protected Supplier < GenericChunk > loadChunkAsync ( DynmapChunk ch ) {
2022-03-31 15:18:02 +02:00
throw new IllegalStateException ( " Not implemeted " ) ;
2022-03-30 18:37:29 +02:00
}
2021-12-05 04:33:02 +01:00
/ * *
* Read NBT data from loaded chunks - needs to be called from server / world
* thread to be safe
*
* @returns number loaded
* /
public int getLoadedChunks ( ) {
int cnt = 0 ;
if ( ! dw . isLoaded ( ) ) {
isempty = true ;
unloadChunks ( ) ;
return 0 ;
}
ListIterator < DynmapChunk > iter = chunks . listIterator ( ) ;
while ( iter . hasNext ( ) ) {
long startTime = System . nanoTime ( ) ;
DynmapChunk chunk = iter . next ( ) ;
int chunkindex = ( chunk . x - x_min ) + ( chunk . z - z_min ) * x_dim ;
if ( snaparray [ chunkindex ] ! = null )
continue ; // Skip if already processed
boolean vis = isChunkVisible ( chunk ) ;
/* Check if cached chunk snapshot found */
if ( tryChunkCache ( chunk , vis ) ) {
endChunkLoad ( startTime , ChunkStats . CACHED_SNAPSHOT_HIT ) ;
cnt + + ;
}
// If chunk is loaded and not being unloaded, we're grabbing its NBT data
else {
// Get generic chunk from already loaded chunk, if we can
GenericChunk ss = getLoadedChunk ( chunk ) ;
if ( ss ! = null ) {
if ( vis ) { // If visible
prepChunkSnapshot ( chunk , ss ) ;
}
else {
if ( hidestyle = = HiddenChunkStyle . FILL_STONE_PLAIN ) {
2021-12-28 21:37:20 +01:00
ss = getStone ( ) ;
2021-12-05 04:33:02 +01:00
}
else if ( hidestyle = = HiddenChunkStyle . FILL_OCEAN ) {
2021-12-28 21:37:20 +01:00
ss = getOcean ( ) ;
2021-12-05 04:33:02 +01:00
}
else {
2021-12-28 21:37:20 +01:00
ss = getEmpty ( ) ;
2021-12-05 04:33:02 +01:00
}
}
snaparray [ chunkindex ] = ss ;
endChunkLoad ( startTime , ChunkStats . LOADED_CHUNKS ) ;
cnt + + ;
}
}
}
return cnt ;
}
2022-03-31 15:18:02 +02:00
/ * *
2022-07-06 15:28:14 +02:00
* Read NBT data from loaded chunks - do not needs to be called from server / world < p >
2022-03-31 15:18:02 +02:00
* Will throw { @link IllegalStateException } if not supporting
* /
2022-03-30 18:37:29 +02:00
public void getLoadedChunksAsync ( ) {
2022-03-31 15:18:02 +02:00
class SimplePair { //simple pair of the supplier that finishes read async, and a consumer that also finish his work async
2022-03-30 18:37:29 +02:00
final Supplier < GenericChunk > supplier ;
final BiConsumer < GenericChunk , Long > consumer ;
SimplePair ( Supplier < GenericChunk > supplier , BiConsumer < GenericChunk , Long > consumer ) {
this . supplier = supplier ;
this . consumer = consumer ;
}
}
if ( ! dw . isLoaded ( ) ) {
isempty = true ;
unloadChunks ( ) ;
return ;
}
List < SimplePair > lastApply = new ArrayList < > ( ) ;
for ( DynmapChunk dynmapChunk : chunks ) {
long startTime = System . nanoTime ( ) ;
int chunkIndex = ( dynmapChunk . x - x_min ) + ( dynmapChunk . z - z_min ) * x_dim ;
if ( snaparray [ chunkIndex ] ! = null )
continue ; // Skip if already processed
boolean vis = isChunkVisible ( dynmapChunk ) ;
/* Check if cached chunk snapshot found */
if ( tryChunkCache ( dynmapChunk , vis ) ) {
endChunkLoad ( startTime , ChunkStats . CACHED_SNAPSHOT_HIT ) ;
}
// If chunk is loaded and not being unloaded, we're grabbing its NBT data
else {
// Get generic chunk from already loaded chunk, if we can
Supplier < GenericChunk > supplier = getLoadedChunkAsync ( dynmapChunk ) ;
long startPause = System . nanoTime ( ) ;
BiConsumer < GenericChunk , Long > consumer = ( ss , reloadTime ) - > {
if ( ss = = null ) return ;
long pause = reloadTime - startPause ;
if ( vis ) { // If visible
prepChunkSnapshot ( dynmapChunk , ss ) ;
} else {
if ( hidestyle = = HiddenChunkStyle . FILL_STONE_PLAIN ) {
ss = getStone ( ) ;
} else if ( hidestyle = = HiddenChunkStyle . FILL_OCEAN ) {
ss = getOcean ( ) ;
} else {
ss = getEmpty ( ) ;
}
}
snaparray [ chunkIndex ] = ss ;
endChunkLoad ( startTime - pause , ChunkStats . LOADED_CHUNKS ) ;
} ;
lastApply . add ( new SimplePair ( supplier , consumer ) ) ;
}
}
//impact on the main thread should be minimal, so we plan and finish the work after main thread finished it's part
lastApply . forEach ( simplePair - > {
long reloadWork = System . nanoTime ( ) ;
simplePair . consumer . accept ( simplePair . supplier . get ( ) , reloadWork ) ;
} ) ;
}
2021-12-05 04:33:02 +01:00
@Override
public int loadChunks ( int max_to_load ) {
return getLoadedChunks ( ) + readChunks ( max_to_load ) ;
2022-03-30 18:37:29 +02:00
}
2021-12-05 04:33:02 +01:00
2022-03-31 15:18:02 +02:00
/ * *
2022-07-06 15:28:14 +02:00
* Loads all chunks in the world asynchronously .
* < p >
* If it is not supported , it will throw { @link IllegalStateException }
2022-03-31 15:18:02 +02:00
* /
2022-03-30 18:37:29 +02:00
public void loadChunksAsync ( ) {
getLoadedChunksAsync ( ) ;
readChunksAsync ( ) ;
2021-12-05 04:33:02 +01:00
}
public int readChunks ( int max_to_load ) {
if ( ! dw . isLoaded ( ) ) {
isempty = true ;
unloadChunks ( ) ;
return 0 ;
}
int cnt = 0 ;
if ( iterator = = null ) {
iterator = chunks . listIterator ( ) ;
}
DynmapCore . setIgnoreChunkLoads ( true ) ;
// Load the required chunks.
while ( ( cnt < max_to_load ) & & iterator . hasNext ( ) ) {
long startTime = System . nanoTime ( ) ;
DynmapChunk chunk = iterator . next ( ) ;
int chunkindex = ( chunk . x - x_min ) + ( chunk . z - z_min ) * x_dim ;
if ( snaparray [ chunkindex ] ! = null )
continue ; // Skip if already processed
boolean vis = isChunkVisible ( chunk ) ;
/* Check if cached chunk snapshot found */
if ( tryChunkCache ( chunk , vis ) ) {
endChunkLoad ( startTime , ChunkStats . CACHED_SNAPSHOT_HIT ) ;
}
else {
GenericChunk ss = loadChunk ( chunk ) ;
// If read was good
if ( ss ! = null ) {
// If hidden
if ( ! vis ) {
if ( hidestyle = = HiddenChunkStyle . FILL_STONE_PLAIN ) {
2021-12-28 21:37:20 +01:00
ss = getStone ( ) ;
2021-12-05 04:33:02 +01:00
}
else if ( hidestyle = = HiddenChunkStyle . FILL_OCEAN ) {
2021-12-28 21:37:20 +01:00
ss = getOcean ( ) ;
2021-12-05 04:33:02 +01:00
}
else {
2021-12-28 21:37:20 +01:00
ss = getEmpty ( ) ;
2021-12-05 04:33:02 +01:00
}
}
else {
// Prep snapshot
prepChunkSnapshot ( chunk , ss ) ;
}
snaparray [ chunkindex ] = ss ;
endChunkLoad ( startTime , ChunkStats . UNLOADED_CHUNKS ) ;
}
else {
endChunkLoad ( startTime , ChunkStats . UNGENERATED_CHUNKS ) ;
}
}
cnt + + ;
}
DynmapCore . setIgnoreChunkLoads ( false ) ;
if ( iterator . hasNext ( ) = = false ) { /* If we're done */
isempty = true ;
/* Fill missing chunks with empty dummy chunk */
for ( int i = 0 ; i < snaparray . length ; i + + ) {
if ( snaparray [ i ] = = null ) {
2021-12-28 21:37:20 +01:00
snaparray [ i ] = getEmpty ( ) ;
2021-12-05 04:33:02 +01:00
}
2021-12-28 21:37:20 +01:00
else if ( ! snaparray [ i ] . isEmpty ) {
2021-12-05 04:33:02 +01:00
isempty = false ;
}
}
}
return cnt ;
}
2022-07-06 15:28:14 +02:00
/ * *
* It loads chunks from the cache or from the world , and if the chunk is not visible , it fills it with stone , ocean or
* empty chunk
* < p >
* if it ' s not supported , will throw { @link IllegalStateException }
* /
2022-03-30 18:37:29 +02:00
public void readChunksAsync ( ) {
2022-04-01 14:16:57 +02:00
class SimplePair { //pair of the chunk and the data which is readed async
2022-03-31 15:18:02 +02:00
private final Supplier < GenericChunk > supplier ;
private final DynmapChunk chunk ;
SimplePair ( DynmapChunk chunk ) {
this . chunk = chunk ;
this . supplier = loadChunkAsync ( chunk ) ;
}
}
2022-03-30 18:37:29 +02:00
if ( ! dw . isLoaded ( ) ) {
isempty = true ;
unloadChunks ( ) ;
return ;
}
List < DynmapChunk > chunks ;
if ( iterator = = null ) {
iterator = Collections . emptyListIterator ( ) ;
chunks = new ArrayList < > ( this . chunks ) ;
} else {
chunks = new ArrayList < > ( ) ;
iterator . forEachRemaining ( chunks : : add ) ;
}
2022-03-31 14:04:36 +02:00
//if before increent was 0, means that we are the first, so we need to set this
if ( loadingChunks . getAndIncrement ( ) = = 0 ) {
DynmapCore . setIgnoreChunkLoads ( true ) ;
}
2022-03-30 18:37:29 +02:00
2022-03-31 14:04:36 +02:00
try {
List < DynmapChunk > cached = new ArrayList < > ( ) ;
2022-03-31 15:18:02 +02:00
List < SimplePair > notCached = new ArrayList < > ( ) ;
2022-03-30 18:37:29 +02:00
2022-03-31 14:04:36 +02:00
iterator . forEachRemaining ( chunks : : add ) ;
chunks . stream ( )
. filter ( chunk - > snaparray [ ( chunk . x - x_min ) + ( chunk . z - z_min ) * x_dim ] = = null )
. forEach ( chunk - > {
if ( cache . getSnapshot ( dw . getName ( ) , chunk . x , chunk . z ) = = null ) {
2022-03-31 15:18:02 +02:00
notCached . add ( new SimplePair ( chunk ) ) ;
2022-03-31 14:04:36 +02:00
} else {
cached . add ( chunk ) ;
}
} ) ;
2022-03-30 18:37:29 +02:00
2022-03-31 14:04:36 +02:00
cached . forEach ( chunk - > {
long startTime = System . nanoTime ( ) ;
tryChunkCache ( chunk , isChunkVisible ( chunk ) ) ;
endChunkLoad ( startTime , ChunkStats . CACHED_SNAPSHOT_HIT ) ;
} ) ;
notCached . forEach ( chunkSupplier - > {
long startTime = System . nanoTime ( ) ;
2022-03-31 15:18:02 +02:00
GenericChunk chunk = chunkSupplier . supplier . get ( ) ;
DynmapChunk dynmapChunk = chunkSupplier . chunk ;
2022-03-31 14:04:36 +02:00
if ( chunk ! = null ) {
// If hidden
if ( isChunkVisible ( dynmapChunk ) ) {
// Prep snapshot
prepChunkSnapshot ( dynmapChunk , chunk ) ;
2022-03-30 18:37:29 +02:00
} else {
2022-03-31 14:04:36 +02:00
if ( hidestyle = = HiddenChunkStyle . FILL_STONE_PLAIN ) {
chunk = getStone ( ) ;
} else if ( hidestyle = = HiddenChunkStyle . FILL_OCEAN ) {
chunk = getOcean ( ) ;
} else {
chunk = getEmpty ( ) ;
}
2022-03-30 18:37:29 +02:00
}
2022-03-31 14:04:36 +02:00
snaparray [ ( dynmapChunk . x - x_min ) + ( dynmapChunk . z - z_min ) * x_dim ] = chunk ;
endChunkLoad ( startTime , ChunkStats . UNLOADED_CHUNKS ) ;
} else {
endChunkLoad ( startTime , ChunkStats . UNGENERATED_CHUNKS ) ;
2022-03-30 18:37:29 +02:00
}
2022-03-31 14:04:36 +02:00
} ) ;
2022-03-30 18:37:29 +02:00
2022-03-31 14:04:36 +02:00
isempty = true ;
/* Fill missing chunks with empty dummy chunk */
for ( int i = 0 ; i < snaparray . length ; i + + ) {
if ( snaparray [ i ] = = null ) {
snaparray [ i ] = getEmpty ( ) ;
} else if ( ! snaparray [ i ] . isEmpty ) {
isempty = false ;
}
}
} finally {
if ( loadingChunks . decrementAndGet ( ) = = 0 ) {
DynmapCore . setIgnoreChunkLoads ( false ) ;
2022-03-30 18:37:29 +02:00
}
}
}
2021-12-05 04:33:02 +01:00
/ * *
* Test if done loading
* /
public boolean isDoneLoading ( ) {
if ( ! dw . isLoaded ( ) ) {
return true ;
}
if ( iterator ! = null ) {
return ! iterator . hasNext ( ) ;
}
return false ;
}
/ * *
* Test if all empty blocks
* /
public boolean isEmpty ( ) {
return isempty ;
}
/ * *
* Unload chunks
* /
public void unloadChunks ( ) {
if ( snaparray ! = null ) {
for ( int i = 0 ; i < snaparray . length ; i + + ) {
snaparray [ i ] = null ;
}
snaparray = null ;
}
}
private void initSectionData ( int idx ) {
isSectionNotEmpty [ idx ] = new boolean [ nsect + 1 ] ;
2021-12-28 21:37:20 +01:00
if ( ! snaparray [ idx ] . isEmpty ) {
2021-12-05 04:33:02 +01:00
for ( int i = 0 ; i < nsect ; i + + ) {
if ( snaparray [ idx ] . isSectionEmpty ( i - sectoff ) = = false ) {
isSectionNotEmpty [ idx ] [ i ] = true ;
}
}
}
}
public boolean isEmptySection ( int sx , int sy , int sz ) {
int idx = ( sx - x_min ) + ( sz - z_min ) * x_dim ;
boolean [ ] flags = isSectionNotEmpty [ idx ] ;
if ( flags = = null ) {
initSectionData ( idx ) ;
flags = isSectionNotEmpty [ idx ] ;
}
return ! flags [ sy + sectoff ] ;
}
/ * *
* Get cache iterator
* /
public MapIterator getIterator ( int x , int y , int z ) {
if ( dw . getEnvironment ( ) . equals ( " the_end " ) ) {
return new OurEndMapIterator ( x , y , z ) ;
}
return new OurMapIterator ( x , y , z ) ;
}
/ * *
* Set hidden chunk style ( default is FILL_AIR )
* /
public void setHiddenFillStyle ( HiddenChunkStyle style ) {
this . hidestyle = style ;
}
/ * *
* Add visible area limit - can be called more than once Needs to be set before
* chunks are loaded Coordinates are block coordinates
* /
public void setVisibleRange ( VisibilityLimit lim ) {
if ( visible_limits = = null )
visible_limits = new ArrayList < VisibilityLimit > ( ) ;
visible_limits . add ( lim ) ;
}
/ * *
* Add hidden area limit - can be called more than once Needs to be set before
* chunks are loaded Coordinates are block coordinates
* /
public void setHiddenRange ( VisibilityLimit lim ) {
if ( hidden_limits = = null )
hidden_limits = new ArrayList < VisibilityLimit > ( ) ;
hidden_limits . add ( lim ) ;
}
@Override
public DynmapWorld getWorld ( ) {
return dw ;
}
@Override
public boolean setChunkDataTypes ( boolean blockdata , boolean biome , boolean highestblocky , boolean rawbiome ) {
return true ;
}
2021-12-16 06:34:53 +01:00
private static final String litStates [ ] = { " light " , " spawn " , " heightmaps " , " full " } ;
2021-12-20 04:02:35 +01:00
public GenericChunk parseChunkFromNBT ( GenericNBTCompound orignbt ) {
GenericNBTCompound nbt = orignbt ;
if ( ( nbt ! = null ) & & nbt . contains ( " Level " , GenericNBTCompound . TAG_COMPOUND ) ) {
2021-12-07 07:01:07 +01:00
nbt = nbt . getCompound ( " Level " ) ;
}
if ( nbt = = null ) return null ;
2021-12-16 06:34:53 +01:00
String status = nbt . getString ( " Status " ) ;
2021-12-20 04:02:35 +01:00
int version = orignbt . getInt ( " DataVersion " ) ;
2021-12-26 05:39:06 +01:00
boolean lit = nbt . getBoolean ( " isLightOn " ) ;
2021-12-16 19:32:10 +01:00
boolean hasLitState = false ;
2021-12-16 06:34:53 +01:00
if ( status ! = null ) {
for ( int i = 0 ; i < litStates . length ; i + + ) {
2021-12-16 19:32:10 +01:00
if ( status . equals ( litStates [ i ] ) ) { hasLitState = true ; }
2021-12-16 06:34:53 +01:00
}
}
2021-12-21 01:07:54 +01:00
boolean hasLight = false ; // pessimistic: only has light if we see it, due to WB and other flawed chunk generation hasLitState; // Assume good light in a lit state
2021-12-16 19:32:10 +01:00
2021-12-07 07:01:07 +01:00
// Start generic chunk builder
GenericChunk . Builder bld = new GenericChunk . Builder ( dw . minY , dw . worldheight ) ;
2021-12-15 06:58:01 +01:00
int x = nbt . getInt ( " xPos " ) ;
int z = nbt . getInt ( " zPos " ) ;
2021-12-26 05:39:06 +01:00
2021-12-20 04:02:35 +01:00
// Set chunk info
bld . coords ( x , z ) . chunkStatus ( status ) . dataVersion ( version ) ;
2021-12-07 07:01:07 +01:00
if ( nbt . contains ( " InhabitedTime " ) ) {
bld . inhabitedTicks ( nbt . getLong ( " InhabitedTime " ) ) ;
}
// Check for 2D or old 3D biome data from chunk level: need these when we build old sections
List < BiomeMap [ ] > old3d = null ; // By section, then YZX list
BiomeMap [ ] old2d = null ;
if ( nbt . contains ( " Biomes " ) ) {
int [ ] bb = nbt . getIntArray ( " Biomes " ) ;
if ( bb ! = null ) {
// If v1.15+ format
if ( bb . length > 256 ) {
old3d = new ArrayList < BiomeMap [ ] > ( ) ;
// Get 4 x 4 x 4 list for each section
for ( int sect = 0 ; sect < ( bb . length / 64 ) ; sect + + ) {
BiomeMap smap [ ] = new BiomeMap [ 64 ] ;
for ( int i = 0 ; i < 64 ; i + + ) {
smap [ i ] = BiomeMap . byBiomeID ( bb [ sect * 64 + i ] ) ;
}
old3d . add ( smap ) ;
}
}
else { // Else, older chunks
old2d = new BiomeMap [ 256 ] ;
for ( int i = 0 ; i < bb . length ; i + + ) {
old2d [ i ] = BiomeMap . byBiomeID ( bb [ i ] ) ;
}
}
}
}
// Start section builder
GenericChunkSection . Builder sbld = new GenericChunkSection . Builder ( ) ;
/* Get sections */
GenericNBTList sect = nbt . contains ( " sections " ) ? nbt . getList ( " sections " , 10 ) : nbt . getList ( " Sections " , 10 ) ;
2021-12-16 06:34:53 +01:00
// And process sections
2021-12-07 07:01:07 +01:00
for ( int i = 0 ; i < sect . size ( ) ; i + + ) {
GenericNBTCompound sec = sect . getCompound ( i ) ;
int secnum = sec . getByte ( " Y " ) ;
DynmapBlockState [ ] palette = null ;
// If we've got palette and block states list, process non-empty section
if ( sec . contains ( " Palette " , 9 ) & & sec . contains ( " BlockStates " , 12 ) ) {
GenericNBTList plist = sec . getList ( " Palette " , 10 ) ;
long [ ] statelist = sec . getLongArray ( " BlockStates " ) ;
palette = new DynmapBlockState [ plist . size ( ) ] ;
for ( int pi = 0 ; pi < plist . size ( ) ; pi + + ) {
GenericNBTCompound tc = plist . getCompound ( pi ) ;
String pname = tc . getString ( " Name " ) ;
if ( tc . contains ( " Properties " ) ) {
StringBuilder statestr = new StringBuilder ( ) ;
GenericNBTCompound prop = tc . getCompound ( " Properties " ) ;
for ( String pid : prop . getAllKeys ( ) ) {
if ( statestr . length ( ) > 0 ) statestr . append ( ',' ) ;
statestr . append ( pid ) . append ( '=' ) . append ( prop . getAsString ( pid ) ) ;
}
palette [ pi ] = DynmapBlockState . getStateByNameAndState ( pname , statestr . toString ( ) ) ;
}
if ( palette [ pi ] = = null ) {
palette [ pi ] = DynmapBlockState . getBaseStateByName ( pname ) ;
}
if ( palette [ pi ] = = null ) {
palette [ pi ] = DynmapBlockState . AIR ;
}
}
int recsperblock = ( 4096 + statelist . length - 1 ) / statelist . length ;
int bitsperblock = 64 / recsperblock ;
GenericBitStorage db = null ;
DataBitsPacked dbp = null ;
try {
db = nbt . makeBitStorage ( bitsperblock , 4096 , statelist ) ;
2021-12-15 06:58:01 +01:00
} catch ( Exception ex ) { // Handle legacy encoded
2021-12-07 07:01:07 +01:00
bitsperblock = ( statelist . length * 64 ) / 4096 ;
dbp = new DataBitsPacked ( bitsperblock , 4096 , statelist ) ;
}
if ( bitsperblock > 8 ) { // Not palette
for ( int j = 0 ; j < 4096 ; j + + ) {
int v = ( dbp ! = null ) ? dbp . getAt ( j ) : db . get ( j ) ;
sbld . xyzBlockState ( j & 0xF , ( j & 0xF00 ) > > 8 , ( j & 0xF0 ) > > 4 , DynmapBlockState . getStateByGlobalIndex ( v ) ) ;
}
}
else {
2021-12-29 05:14:19 +01:00
sbld . xyzBlockStatePalette ( palette ) ; // Set palette
for ( int j = 0 ; j < 4096 ; j + + ) {
int v = db ! = null ? db . get ( j ) : dbp . getAt ( j ) ;
sbld . xyzBlockStateInPalette ( j & 0xF , ( j & 0xF00 ) > > 8 , ( j & 0xF0 ) > > 4 , ( short ) v ) ;
}
2021-12-07 07:01:07 +01:00
}
}
2021-12-15 06:58:01 +01:00
else if ( sec . contains ( " block_states " , GenericNBTCompound . TAG_COMPOUND ) ) { // 1.18
2021-12-07 07:01:07 +01:00
GenericNBTCompound block_states = sec . getCompound ( " block_states " ) ;
2021-12-15 06:58:01 +01:00
// If we've got palette, process non-empty section
if ( block_states . contains ( " palette " , GenericNBTCompound . TAG_LIST ) ) {
long [ ] statelist = block_states . contains ( " data " , GenericNBTCompound . TAG_LONG_ARRAY ) ? block_states . getLongArray ( " data " ) : new long [ 4096 / 64 ] ; // Handle zero bit palette (all same)
GenericNBTList plist = block_states . getList ( " palette " , GenericNBTCompound . TAG_COMPOUND ) ;
2021-12-07 07:01:07 +01:00
palette = new DynmapBlockState [ plist . size ( ) ] ;
for ( int pi = 0 ; pi < plist . size ( ) ; pi + + ) {
GenericNBTCompound tc = plist . getCompound ( pi ) ;
String pname = tc . getString ( " Name " ) ;
if ( tc . contains ( " Properties " ) ) {
StringBuilder statestr = new StringBuilder ( ) ;
GenericNBTCompound prop = tc . getCompound ( " Properties " ) ;
for ( String pid : prop . getAllKeys ( ) ) {
if ( statestr . length ( ) > 0 ) statestr . append ( ',' ) ;
statestr . append ( pid ) . append ( '=' ) . append ( prop . getAsString ( pid ) ) ;
}
palette [ pi ] = DynmapBlockState . getStateByNameAndState ( pname , statestr . toString ( ) ) ;
}
if ( palette [ pi ] = = null ) {
palette [ pi ] = DynmapBlockState . getBaseStateByName ( pname ) ;
}
if ( palette [ pi ] = = null ) {
palette [ pi ] = DynmapBlockState . AIR ;
}
}
GenericBitStorage db = null ;
DataBitsPacked dbp = null ;
int bitsperblock = ( statelist . length * 64 ) / 4096 ;
int expectedStatelistLength = ( 4096 + ( 64 / bitsperblock ) - 1 ) / ( 64 / bitsperblock ) ;
if ( statelist . length = = expectedStatelistLength ) {
db = nbt . makeBitStorage ( bitsperblock , 4096 , statelist ) ;
}
else {
bitsperblock = ( statelist . length * 64 ) / 4096 ;
dbp = new DataBitsPacked ( bitsperblock , 4096 , statelist ) ;
}
if ( bitsperblock > 8 ) { // Not palette
for ( int j = 0 ; j < 4096 ; j + + ) {
int v = db ! = null ? db . get ( j ) : dbp . getAt ( j ) ;
sbld . xyzBlockState ( j & 0xF , ( j & 0xF00 ) > > 8 , ( j & 0xF0 ) > > 4 , DynmapBlockState . getStateByGlobalIndex ( v ) ) ;
}
}
else {
2021-12-29 05:14:19 +01:00
sbld . xyzBlockStatePalette ( palette ) ; // Set palette
2021-12-07 07:01:07 +01:00
for ( int j = 0 ; j < 4096 ; j + + ) {
int v = db ! = null ? db . get ( j ) : dbp . getAt ( j ) ;
2021-12-29 05:14:19 +01:00
sbld . xyzBlockStateInPalette ( j & 0xF , ( j & 0xF00 ) > > 8 , ( j & 0xF0 ) > > 4 , ( short ) v ) ;
2021-12-07 07:01:07 +01:00
}
}
}
}
if ( sec . contains ( " BlockLight " ) ) {
sbld . emittedLight ( sec . getByteArray ( " BlockLight " ) ) ;
}
if ( sec . contains ( " SkyLight " ) ) {
sbld . skyLight ( sec . getByteArray ( " SkyLight " ) ) ;
2021-12-24 16:22:32 +01:00
hasLight = true ;
2021-12-07 07:01:07 +01:00
}
// If section biome palette
if ( sec . contains ( " biomes " ) ) {
GenericNBTCompound nbtbiomes = sec . getCompound ( " biomes " ) ;
long [ ] bdataPacked = nbtbiomes . getLongArray ( " data " ) ;
GenericNBTList bpalette = nbtbiomes . getList ( " palette " , 8 ) ;
GenericBitStorage bdata = null ;
2022-08-07 00:48:27 +02:00
if ( bdataPacked . length > 0 ) {
int valsPerLong = ( 64 / bdataPacked . length ) ;
bdata = nbt . makeBitStorage ( ( 64 + valsPerLong - 1 ) / valsPerLong , 64 , bdataPacked ) ;
}
2021-12-07 07:01:07 +01:00
for ( int j = 0 ; j < 64 ; j + + ) {
int b = bdata ! = null ? bdata . get ( j ) : 0 ;
sbld . xyzBiome ( j & 0x3 , ( j & 0x30 ) > > 4 , ( j & 0xC ) > > 2 , BiomeMap . byBiomeResourceLocation ( bpalette . getString ( b ) ) ) ;
}
}
else { // Else, apply legacy biomes
if ( old3d ! = null ) {
BiomeMap m [ ] = old3d . get ( ( secnum > 0 ) ? ( ( secnum < old3d . size ( ) ) ? secnum : old3d . size ( ) - 1 ) : 0 ) ;
if ( m ! = null ) {
for ( int j = 0 ; j < 64 ; j + + ) {
sbld . xyzBiome ( j & 0x3 , ( j & 0x30 ) > > 4 , ( j & 0xC ) > > 2 , m [ j ] ) ;
}
}
}
else if ( old2d ! = null ) {
for ( int j = 0 ; j < 256 ; j + + ) {
sbld . xzBiome ( j & 0xF , ( j & 0xF0 ) > > 4 , old2d [ j ] ) ;
}
}
}
// Finish and add section
bld . addSection ( secnum , sbld . build ( ) ) ;
sbld . reset ( ) ;
}
2021-12-24 16:22:32 +01:00
// Assume skylight is only trustworthy in a lit state
2021-12-26 05:39:06 +01:00
if ( ( ! hasLitState ) | | ( ! lit ) ) {
2021-12-16 19:32:10 +01:00
hasLight = false ;
}
2021-12-16 06:34:53 +01:00
// If no light, do simple generate
if ( ! hasLight ) {
2021-12-16 21:07:50 +01:00
//Log.info(String.format("generateSky(%d,%d)", x, z));
2021-12-16 06:34:53 +01:00
bld . generateSky ( ) ;
}
return bld . build ( ) ;
2021-12-07 07:01:07 +01:00
}
2021-12-05 04:33:02 +01:00
}