1010import org .allaymc .api .world .biome .BiomeType ;
1111import org .allaymc .api .world .biome .BiomeTypes ;
1212import org .allaymc .api .world .chunk .Chunk ;
13+ import org .allaymc .api .world .chunk .OperationType ;
14+ import org .allaymc .api .world .chunk .UnsafeChunk ;
1315
1416import javax .imageio .ImageIO ;
1517import java .awt .*;
1618import java .awt .image .BufferedImage ;
19+ import java .util .Arrays ;
1720import java .util .Objects ;
1821import java .util .concurrent .CompletableFuture ;
22+ import java .util .concurrent .atomic .AtomicReference ;
1923
2024/**
2125 * MapRenderer handles the rendering of chunks to images.
@@ -106,48 +110,41 @@ private static Color darker(Color source, double factor) {
106110 /**
107111 * Render a single chunk to a 16x16 tile image (1 pixel per block).
108112 */
109- public CompletableFuture <BufferedImage > renderChunk (Dimension dimension , int chunkX , int chunkZ ) {
113+ public CompletableFuture <BufferedImage > renderChunk (Dimension dimension , Chunk chunk ) {
110114 return CompletableFuture .supplyAsync (() -> {
111- Chunk chunk = dimension .getChunkManager ().getChunk (chunkX , chunkZ );
112- if (chunk == null ) {
113- return null ;
114- }
115-
116- int startX = chunkX << 4 ;
117- int startZ = chunkZ << 4 ;
118115 int [] pixels = new int [CHUNK_SIZE * CHUNK_SIZE ];
119116 int [] lastY = new int [CHUNK_SIZE ];
120117
121118 // First pass: get initial heights for z=0
122- for (int x = 0 ; x < CHUNK_SIZE ; x ++) {
123- int worldX = startX + x ;
124- int worldZ = startZ - 1 ;
125- HeightResult hr = getTopBlockHeight (dimension , worldX , worldZ );
126- lastY [x ] = hr != null ? hr .y : SEA_LEVEL ;
119+ var lastChunk = dimension .getChunkManager ().getChunk (chunk .getX (), chunk .getZ () - 1 );
120+ if (lastChunk == null ) {
121+ Arrays .fill (lastY , SEA_LEVEL );
122+ } else {
123+ lastChunk .applyOperation (unsafeChunk -> {
124+ for (int x = 0 ; x < CHUNK_SIZE ; x ++) {
125+ HeightResult hr = getTopBlockHeight (unsafeChunk , x , 15 );
126+ lastY [x ] = hr != null ? hr .y : SEA_LEVEL ;
127+ }
128+ }, OperationType .READ , OperationType .NONE );
127129 }
128130
129- // Main rendering pass
130- for (int z = 0 ; z < CHUNK_SIZE ; z ++) {
131- for (int x = 0 ; x < CHUNK_SIZE ; x ++) {
132- int worldX = startX + x ;
133- int worldZ = startZ + z ;
134-
135- Color color = getMapColor (dimension , worldX , worldZ , lastY [x ]);
136-
137- HeightResult hr = getTopBlockHeight (dimension , worldX , worldZ );
138- if (hr != null ) {
139- lastY [x ] = hr .y ;
131+ chunk .applyOperation (unsafeChunk -> {
132+ // Main rendering pass
133+ for (int z = 0 ; z < CHUNK_SIZE ; z ++) {
134+ for (int x = 0 ; x < CHUNK_SIZE ; x ++) {
135+ Color color = getMapColor (unsafeChunk , x , z , lastY [x ]);
136+ HeightResult hr = getTopBlockHeight (unsafeChunk , x , z );
137+ lastY [x ] = hr != null ? hr .y : SEA_LEVEL ;
138+ pixels [z * CHUNK_SIZE + x ] = color .getRGB ();
140139 }
141-
142- pixels [z * CHUNK_SIZE + x ] = color .getRGB ();
143140 }
144- }
141+ }, OperationType . READ , OperationType . READ );
145142
146143 BufferedImage image = new BufferedImage (CHUNK_TILE_SIZE , CHUNK_TILE_SIZE , BufferedImage .TYPE_INT_ARGB );
147144 image .setRGB (0 , 0 , CHUNK_TILE_SIZE , CHUNK_TILE_SIZE , pixels , 0 , CHUNK_TILE_SIZE );
148145 return image ;
149146 }, Server .getInstance ().getVirtualThreadPool ()).exceptionally (e -> {
150- AllayMap .getInstance ().getPluginLogger ().error ("Error rendering chunk ({}, {})" , chunkX , chunkZ , e );
147+ AllayMap .getInstance ().getPluginLogger ().error ("Error rendering chunk ({}, {})" , chunk . getX (), chunk . getZ () , e );
151148 return createEmptyChunkTile ();
152149 });
153150 }
@@ -167,20 +164,20 @@ private BufferedImage createEmptyChunkTile() {
167164 /**
168165 * Get the map color for a specific position
169166 */
170- private Color getMapColor (Dimension dimension , int x , int z , int lastY ) {
171- HeightResult result = getTopBlockHeight (dimension , x , z );
167+ private Color getMapColor (UnsafeChunk chunk , int x , int z , int lastY ) {
168+ var result = getTopBlockHeight (chunk , x , z );
172169 if (result == null ) {
173170 return UNLOADED_CHUNK_COLOR ;
174171 }
175172
176- BlockState blockState = result .state ;
173+ var blockState = result .state ;
177174 int y = result .y ;
178- BiomeType biome = dimension .getBiome (x , y , z );
175+ var biome = chunk .getBiome (x , y , z );
179176
180- Color color = computeMapColor (blockState , biome );
177+ var color = computeMapColor (blockState , biome );
181178
182179 // Check if block is underwater
183- if (dimension .getBlockState (x , y + 1 , z ).getBlockType ().hasBlockTag (BlockTags .WATER )) {
180+ if (chunk .getBlockState (x , y + 1 , z ).getBlockType ().hasBlockTag (BlockTags .WATER )) {
184181 if (AllayMap .getInstance ().getConfig ().renderUnderwaterBlocks ()) {
185182 color = applyWaterTint (color , y , biome );
186183 } else {
@@ -200,24 +197,14 @@ private Color getMapColor(Dimension dimension, int x, int z, int lastY) {
200197
201198 /**
202199 * Find the top renderable block at a position.
203- * Only uses already-loaded chunks, does NOT trigger chunk loading.
204200 */
205- private HeightResult getTopBlockHeight (Dimension dimension , int x , int z ) {
206- // Only get the chunk if it's already loaded - do NOT load it
207- Chunk chunk = dimension .getChunkManager ().getChunk (x >> 4 , z >> 4 );
208- if (chunk == null ) {
209- return null ;
210- }
211-
212- int chunkX = x & 0xF ;
213- int chunkZ = z & 0xF ;
214-
215- var dimensionInfo = dimension .getDimensionInfo ();
216- int height = AllayMap .getInstance ().getConfig ().ignoreWorldHeightMap () ? dimensionInfo .maxHeight () : chunk .getHeight (chunkX , chunkZ );
201+ private HeightResult getTopBlockHeight (UnsafeChunk chunk , int x , int z ) {
202+ var dimensionInfo = chunk .getDimensionInfo ();
217203 int minHeight = dimensionInfo .minHeight ();
218204
205+ int height = AllayMap .getInstance ().getConfig ().ignoreWorldHeightMap () ? dimensionInfo .maxHeight () : chunk .getHeight (x , z );
219206 while (height >= minHeight ) {
220- BlockState state = chunk .getBlockState (chunkX , height , chunkZ );
207+ BlockState state = chunk .getBlockState (x , height , z );
221208 var mapColor = state .getBlockStateData ().mapColor ();
222209 var tintMethod = state .getBlockStateData ().tintMethod ();
223210
0 commit comments