@@ -63,6 +63,11 @@ public class TuffX extends JavaPlugin implements Listener, PluginMessageListener
6363
6464 private ExecutorService chunkProcessorPool ;
6565
66+ private final ThreadLocal <Map <BlockData , int []>> threadLocalConversionCache = ThreadLocal .withInitial (HashMap ::new );
67+ private final ThreadLocal <short []> threadLocalBlockArray = ThreadLocal .withInitial (() -> new short [4096 ]);
68+ private final ThreadLocal <byte []> threadLocalLightArray = ThreadLocal .withInitial (() -> new byte [4096 ]);
69+ private final ThreadLocal <ByteArrayOutputStream > threadLocalOutStream = ThreadLocal .withInitial (() -> new ByteArrayOutputStream (8256 ));
70+
6671 private void logDebug (String message ) {
6772 if (debug ) getLogger ().log (Level .INFO , "[TuffX-Debug] " + message );
6873 }
@@ -239,48 +244,27 @@ public void run() {
239244
240245 for (int i = 0 ; i < CHUNKS_PER_TICK && !queue .isEmpty (); i ++) {
241246 Vector vec = queue .poll ();
242- if (vec != null ) {
243- World world = player .getWorld ();
244- WorldChunkKey key = new WorldChunkKey (world .getName (), vec .getBlockX (), vec .getBlockZ ());
245-
246- List <byte []> cachedData = chunkPayloadCache .getIfPresent (key );
247-
248- if (cachedData != null ) {
249- logDebug ("Cache HIT for chunk: " + key );
250- sendPayloadsToPlayer (player , cachedData );
251- checkIfInitialLoadComplete (player );
252- } else {
253- logDebug ("Cache MISS for chunk: " + key + ". Generating..." );
254- new BukkitRunnable () {
255- @ Override
256- public void run () {
257- if (world .isChunkLoaded (key .x (), key .z ())) {
258- processAndSendChunk (player , world .getChunkAt (key .x (), key .z ()));
259- } else {
260- world .loadChunk (key .x (), key .z (), true );
261- processAndSendChunk (player , world .getChunkAt (key .x (), key .z ()));
262- }
263- }
264- }.runTaskAsynchronously (TuffX .this );
265- }
247+ if (vec == null ) continue ;
248+
249+ World world = player .getWorld ();
250+ WorldChunkKey key = new WorldChunkKey (world .getName (), vec .getBlockX (), vec .getBlockZ ());
251+
252+ List <byte []> cachedData = chunkPayloadCache .getIfPresent (key );
253+ if (cachedData != null ) {
254+ sendPayloadsToPlayer (player , cachedData );
255+ checkIfInitialLoadComplete (player );
256+ continue ;
266257 }
267- }
268- }
269- }
270- }.runTaskTimer (this , 0L , 1L );
271- }
272258
273- private void sendPayloadsToPlayer (Player player , List <byte []> payloads ) {
274- new BukkitRunnable () {
275- @ Override
276- public void run () {
277- if (player .isOnline ()) {
278- for (byte [] payload : payloads ) {
279- player .sendPluginMessage (TuffX .this , CHANNEL , payload );
259+ if (world .isChunkLoaded (key .x (), key .z ())) {
260+ processAndSendChunk (player , world .getChunkAt (key .x (), key .z ()));
261+ } else {
262+ queue .add (vec );
263+ }
280264 }
281265 }
282266 }
283- }.runTask (this );
267+ }.runTaskTimer (this , 0L , 1L );
284268 }
285269
286270 @ EventHandler (priority = EventPriority .MONITOR )
@@ -315,7 +299,8 @@ private void processAndSendChunk(final Player player, final Chunk chunk) {
315299 chunkProcessorPool .submit (() -> {
316300 final List <byte []> processedPayloads = new ArrayList <>();
317301 final ChunkSnapshot snapshot = chunk .getChunkSnapshot (true , false , false );
318- final Map <BlockData , int []> conversionCache = new HashMap <>();
302+ final Map <BlockData , int []> conversionCache = threadLocalConversionCache .get ();
303+ conversionCache .clear ();
319304
320305 for (int sectionY = -4 ; sectionY < 0 ; sectionY ++) {
321306 if (!player .isOnline ()) {
@@ -347,6 +332,19 @@ public void run() {
347332 });
348333 }
349334
335+ private void sendPayloadsToPlayer (Player player , List <byte []> payloads ) {
336+ new BukkitRunnable () {
337+ @ Override
338+ public void run () {
339+ if (player .isOnline ()) {
340+ for (byte [] payload : payloads ) {
341+ player .sendPluginMessage (TuffX .this , CHANNEL , payload );
342+ }
343+ }
344+ }
345+ }.runTask (this );
346+ }
347+
350348 private void invalidateChunkCache (World world , int blockX , int blockZ ) {
351349 WorldChunkKey key = new WorldChunkKey (world .getName (), blockX >> 4 , blockZ >> 4 );
352350 chunkPayloadCache .invalidate (key );
@@ -404,8 +402,8 @@ private byte[] createWelcomePayload(String message, int someNumber) {
404402 }
405403
406404 private byte [] createSectionPayload (ChunkSnapshot snapshot , int cx , int cz , int sectionY , Map <BlockData , int []> cache ) throws IOException {
407- short [] blockDataArray = new short [ 4096 ] ;
408- byte [] lightDataArray = new byte [ 4096 ] ;
405+ short [] blockDataArray = threadLocalBlockArray . get () ;
406+ byte [] lightDataArray = threadLocalLightArray . get () ;
409407
410408 boolean hasAnythingToSend = false ;
411409 int baseY = sectionY * 16 ;
@@ -436,10 +434,11 @@ private byte[] createSectionPayload(ChunkSnapshot snapshot, int cx, int cz, int
436434 if (!hasAnythingToSend ) {
437435 return null ;
438436 }
437+
438+ ByteArrayOutputStream bout = threadLocalOutStream .get ();
439+ bout .reset ();
439440
440- try (ByteArrayOutputStream bout = new ByteArrayOutputStream (8256 );
441- DataOutputStream out = new DataOutputStream (bout )) {
442-
441+ try (DataOutputStream out = new DataOutputStream (bout )) {
443442 out .writeUTF ("chunk_data" );
444443 out .writeInt (cx );
445444 out .writeInt (cz );
0 commit comments