From b803d3356c9b09cfba38f3695347eef9b89f5ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Eisenbl=C3=A4tter?= Date: Sun, 4 Oct 2020 02:48:29 +0200 Subject: [PATCH] Async cache/protocollib (#51) * feat: WIP - async obfuscation * feat: WIP - async ChunkSerializer + ChunkCache * feat: async ChunkCache * feat: improve AsyncChunkSerializer (task grouping) * feat: improve AsyncChunkSerializer efficiency * fix: memory-leak on reload in ProximityHider * feat: ofc thread group + thread names * feat: added new config options for new async cache --- .../orebfuscator/config/CacheConfig.java | 10 ++ .../orebfuscator/util/ChunkPosition.java | 8 +- .../nms/AbstractRegionFileCache.java | 4 +- .../main/resources/resources/config-1.10.yml | 4 +- .../main/resources/resources/config-1.11.yml | 4 +- .../main/resources/resources/config-1.12.yml | 4 +- .../main/resources/resources/config-1.13.yml | 4 +- .../main/resources/resources/config-1.14.yml | 4 +- .../main/resources/resources/config-1.15.yml | 4 +- .../main/resources/resources/config-1.16.yml | 4 +- .../main/resources/resources/config-1.9.yml | 4 +- .../net/imprex/orebfuscator/Orebfuscator.java | 14 +- .../cache/AsyncChunkSerializer.java | 160 ++++++++++++++++++ .../imprex/orebfuscator/cache/ChunkCache.java | 114 +++++++------ .../orebfuscator/cache/ChunkCacheRequest.java | 44 +++++ ...heSerializer.java => ChunkSerializer.java} | 17 +- .../orebfuscator/chunk/ChunkStruct.java | 1 - .../config/OrebfuscatorCacheConfig.java | 63 +++++-- .../config/OrebfuscatorConfig.java | 1 - .../config/OrebfuscatorProximityConfig.java | 8 +- .../ObfuscatedChunk.java} | 8 +- .../obfuscation/ObfuscationListener.java | 3 +- .../orebfuscator/obfuscation/Obfuscator.java | 39 +++-- .../obfuscation/PacketListener.java | 54 ++++-- .../proximityhider/ProximityHider.java | 6 +- .../proximityhider/ProximityQueue.java | 14 +- .../proximityhider/ProximityThread.java | 103 +++++------ pom.xml | 2 +- 28 files changed, 528 insertions(+), 177 deletions(-) create mode 100644 orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/AsyncChunkSerializer.java create mode 100644 orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCacheRequest.java rename orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/{ChunkCacheSerializer.java => ChunkSerializer.java} (79%) rename orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/{cache/ChunkCacheEntry.java => obfuscation/ObfuscatedChunk.java} (80%) diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/CacheConfig.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/CacheConfig.java index ebdea60e..8c7eec1f 100644 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/CacheConfig.java +++ b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/CacheConfig.java @@ -41,4 +41,14 @@ public interface CacheConfig { * @throws IllegalArgumentException When the expire value is lower than one */ void expireAfterAccess(long expire); + + int maximumTaskQueueSize(); + /** + * @param size + * @throws IllegalArgumentException When the expire value is lower than one + */ + void maximumTaskQueueSize(int size); + + int protocolLibThreads(); + void protocolLibThreads(int threads); } diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkPosition.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkPosition.java index 0f909ec4..e5930b1a 100644 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkPosition.java +++ b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkPosition.java @@ -2,19 +2,21 @@ import java.util.Objects; +import org.bukkit.World; + public class ChunkPosition { - private final String world; + private final World world; private final int x; private final int z; - public ChunkPosition(String world, int x, int z) { + public ChunkPosition(World world, int x, int z) { this.world = Objects.requireNonNull(world); this.x = x; this.z = z; } - public String getWorld() { + public World getWorld() { return this.world; } diff --git a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractRegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractRegionFileCache.java index 7e7ea30b..44b97405 100644 --- a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractRegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractRegionFileCache.java @@ -85,11 +85,9 @@ protected final T get(Path path) throws IOException { } public final void close(Path path) throws IOException { - T t = null; - this.lock.writeLock().lock(); try { - t = this.regionFiles.remove(path); + T t = this.regionFiles.remove(path); if (t != null) { this.closeRegionFile(t); } diff --git a/orebfuscator-nms/orebfuscator-nms-v1_10_R1/src/main/resources/resources/config-1.10.yml b/orebfuscator-nms/orebfuscator-nms-v1_10_R1/src/main/resources/resources/config-1.10.yml index 080f7dcb..ecff454f 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_10_R1/src/main/resources/resources/config-1.10.yml +++ b/orebfuscator-nms/orebfuscator-nms-v1_10_R1/src/main/resources/resources/config-1.10.yml @@ -10,8 +10,10 @@ cache: baseDirectory: 'orebfuscator_cache/' maximumOpenRegionFiles: 256 deleteRegionFilesAfterAccess: 172800000‬ - maximumSize: 4096 + maximumSize: 8192 expireAfterAccess: 30000 + maximumTaskQueueSize: 32768 + protocolLibThreads: -1 world: - worlds: - world diff --git a/orebfuscator-nms/orebfuscator-nms-v1_11_R1/src/main/resources/resources/config-1.11.yml b/orebfuscator-nms/orebfuscator-nms-v1_11_R1/src/main/resources/resources/config-1.11.yml index d2894b12..a1a22415 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_11_R1/src/main/resources/resources/config-1.11.yml +++ b/orebfuscator-nms/orebfuscator-nms-v1_11_R1/src/main/resources/resources/config-1.11.yml @@ -10,8 +10,10 @@ cache: baseDirectory: 'orebfuscator_cache/' maximumOpenRegionFiles: 256 deleteRegionFilesAfterAccess: 172800000‬ - maximumSize: 4096 + maximumSize: 8192 expireAfterAccess: 30000 + maximumTaskQueueSize: 32768 + protocolLibThreads: -1 world: - worlds: - world diff --git a/orebfuscator-nms/orebfuscator-nms-v1_12_R1/src/main/resources/resources/config-1.12.yml b/orebfuscator-nms/orebfuscator-nms-v1_12_R1/src/main/resources/resources/config-1.12.yml index d2894b12..a1a22415 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_12_R1/src/main/resources/resources/config-1.12.yml +++ b/orebfuscator-nms/orebfuscator-nms-v1_12_R1/src/main/resources/resources/config-1.12.yml @@ -10,8 +10,10 @@ cache: baseDirectory: 'orebfuscator_cache/' maximumOpenRegionFiles: 256 deleteRegionFilesAfterAccess: 172800000‬ - maximumSize: 4096 + maximumSize: 8192 expireAfterAccess: 30000 + maximumTaskQueueSize: 32768 + protocolLibThreads: -1 world: - worlds: - world diff --git a/orebfuscator-nms/orebfuscator-nms-v1_13_R1/src/main/resources/resources/config-1.13.yml b/orebfuscator-nms/orebfuscator-nms-v1_13_R1/src/main/resources/resources/config-1.13.yml index 263923c1..53d21a72 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_13_R1/src/main/resources/resources/config-1.13.yml +++ b/orebfuscator-nms/orebfuscator-nms-v1_13_R1/src/main/resources/resources/config-1.13.yml @@ -10,8 +10,10 @@ cache: baseDirectory: 'orebfuscator_cache/' maximumOpenRegionFiles: 256 deleteRegionFilesAfterAccess: 172800000‬ - maximumSize: 4096 + maximumSize: 8192 expireAfterAccess: 30000 + maximumTaskQueueSize: 32768 + protocolLibThreads: -1 world: - worlds: - world diff --git a/orebfuscator-nms/orebfuscator-nms-v1_14_R1/src/main/resources/resources/config-1.14.yml b/orebfuscator-nms/orebfuscator-nms-v1_14_R1/src/main/resources/resources/config-1.14.yml index 2ed82d60..2b64ee8f 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_14_R1/src/main/resources/resources/config-1.14.yml +++ b/orebfuscator-nms/orebfuscator-nms-v1_14_R1/src/main/resources/resources/config-1.14.yml @@ -10,8 +10,10 @@ cache: baseDirectory: 'orebfuscator_cache/' maximumOpenRegionFiles: 256 deleteRegionFilesAfterAccess: 172800000‬ - maximumSize: 4096 + maximumSize: 8192 expireAfterAccess: 30000 + maximumTaskQueueSize: 32768 + protocolLibThreads: -1 world: - worlds: - world diff --git a/orebfuscator-nms/orebfuscator-nms-v1_15_R1/src/main/resources/resources/config-1.15.yml b/orebfuscator-nms/orebfuscator-nms-v1_15_R1/src/main/resources/resources/config-1.15.yml index 9cf336a5..9286b528 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_15_R1/src/main/resources/resources/config-1.15.yml +++ b/orebfuscator-nms/orebfuscator-nms-v1_15_R1/src/main/resources/resources/config-1.15.yml @@ -10,8 +10,10 @@ cache: baseDirectory: 'orebfuscator_cache/' maximumOpenRegionFiles: 256 deleteRegionFilesAfterAccess: 172800000‬ - maximumSize: 4096 + maximumSize: 8192 expireAfterAccess: 30000 + maximumTaskQueueSize: 32768 + protocolLibThreads: -1 world: - worlds: - world diff --git a/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/resources/resources/config-1.16.yml b/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/resources/resources/config-1.16.yml index 67b6eeeb..aa69c20c 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/resources/resources/config-1.16.yml +++ b/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/resources/resources/config-1.16.yml @@ -10,8 +10,10 @@ cache: baseDirectory: 'orebfuscator_cache/' maximumOpenRegionFiles: 256 deleteRegionFilesAfterAccess: 172800000‬ - maximumSize: 4096 + maximumSize: 8192 expireAfterAccess: 30000 + maximumTaskQueueSize: 32768 + protocolLibThreads: -1 world: - worlds: - world diff --git a/orebfuscator-nms/orebfuscator-nms-v1_9_R2/src/main/resources/resources/config-1.9.yml b/orebfuscator-nms/orebfuscator-nms-v1_9_R2/src/main/resources/resources/config-1.9.yml index 080f7dcb..ecff454f 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_9_R2/src/main/resources/resources/config-1.9.yml +++ b/orebfuscator-nms/orebfuscator-nms-v1_9_R2/src/main/resources/resources/config-1.9.yml @@ -10,8 +10,10 @@ cache: baseDirectory: 'orebfuscator_cache/' maximumOpenRegionFiles: 256 deleteRegionFilesAfterAccess: 172800000‬ - maximumSize: 4096 + maximumSize: 8192 expireAfterAccess: 30000 + maximumTaskQueueSize: 32768 + protocolLibThreads: -1 world: - worlds: - world diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/Orebfuscator.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/Orebfuscator.java index 00413fe1..568f37bb 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/Orebfuscator.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/Orebfuscator.java @@ -22,6 +22,10 @@ public class Orebfuscator extends JavaPlugin implements Listener { + public static final ThreadGroup THREAD_GROUP = new ThreadGroup("ofc"); + + private final Thread mainThread = Thread.currentThread(); + private OrebfuscatorConfig config; private ChunkCache chunkCache; private Obfuscator obfuscator; @@ -71,8 +75,8 @@ public void onEnable() { @Override public void onDisable() { - this.chunkCache.invalidateAll(true); - NmsInstance.close(); + this.chunkCache.close(); + this.config.store(); this.packetListener.unregister(); @@ -83,6 +87,8 @@ public void onDisable() { this.getServer().getScheduler().cancelTasks(this); + + NmsInstance.close(); this.config = null; } @@ -95,6 +101,10 @@ public void onEnableFailed(Listener listener, Event event) { } } + public boolean isMainThread() { + return Thread.currentThread() == this.mainThread; + } + public OrebfuscatorConfig getOrebfuscatorConfig() { return this.config; } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/AsyncChunkSerializer.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/AsyncChunkSerializer.java new file mode 100644 index 00000000..510b3e7e --- /dev/null +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/AsyncChunkSerializer.java @@ -0,0 +1,160 @@ +package net.imprex.orebfuscator.cache; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import net.imprex.orebfuscator.Orebfuscator; +import net.imprex.orebfuscator.obfuscation.ObfuscatedChunk; +import net.imprex.orebfuscator.util.ChunkPosition; + +public class AsyncChunkSerializer implements Runnable { + + private final Lock lock = new ReentrantLock(); + private final Condition notFull = lock.newCondition(); + private final Condition notEmpty = lock.newCondition(); + + private final Map tasks = new HashMap<>(); + private final Queue positions = new LinkedList<>(); + private final int maxTaskQueueSize; + + private final Thread thread; + private volatile boolean running = true; + + public AsyncChunkSerializer(Orebfuscator orebfuscator) { + this.maxTaskQueueSize = orebfuscator.getOrebfuscatorConfig().cache().maximumTaskQueueSize(); + + this.thread = new Thread(Orebfuscator.THREAD_GROUP, this, "ofc-chunk-serializer"); + this.thread.setDaemon(true); + this.thread.start(); + } + + public CompletableFuture read(ChunkPosition position) { + this.lock.lock(); + try { + Runnable task = this.tasks.get(position); + if (task instanceof WriteTask) { + return CompletableFuture.completedFuture(((WriteTask) task).chunk); + } else if (task instanceof ReadTask) { + return ((ReadTask) task).future; + } else { + CompletableFuture future = new CompletableFuture<>(); + this.queueTask(position, new ReadTask(position, future)); + return future; + } + } finally { + this.lock.unlock(); + } + } + + public void write(ChunkPosition position, ObfuscatedChunk chunk) { + this.lock.lock(); + try { + Runnable prevTask = this.queueTask(position, new WriteTask(position, chunk)); + if (prevTask instanceof ReadTask) { + ((ReadTask) prevTask).future.complete(chunk); + } + } finally { + this.lock.unlock(); + } + } + + private Runnable queueTask(ChunkPosition position, Runnable nextTask) { + while (this.positions.size() >= this.maxTaskQueueSize) { + this.notFull.awaitUninterruptibly(); + } + + if (!this.running) { + throw new IllegalStateException("AsyncChunkSerializer already closed"); + } + + Runnable prevTask = this.tasks.put(position, nextTask); + if (prevTask == null) { + this.positions.offer(position); + } + + this.notEmpty.signal(); + return prevTask; + } + + @Override + public void run() { + while (this.running) { + this.lock.lock(); + try { + if (this.positions.isEmpty()) { + this.notEmpty.await(); + } + + this.tasks.remove(this.positions.poll()).run(); + + this.notFull.signal(); + } catch (InterruptedException e) { + break; + } finally { + this.lock.unlock(); + } + } + } + + public void close() { + this.lock.lock(); + try { + this.running = false; + this.thread.interrupt(); + + while (!this.positions.isEmpty()) { + Runnable task = this.tasks.remove(this.positions.poll()); + if (task instanceof WriteTask) { + task.run(); + } + } + } finally { + this.lock.unlock(); + } + } + + private class WriteTask implements Runnable { + private final ChunkPosition position; + private final ObfuscatedChunk chunk; + + public WriteTask(ChunkPosition position, ObfuscatedChunk chunk) { + this.position = position; + this.chunk = chunk; + } + + @Override + public void run() { + try { + ChunkSerializer.write(position, chunk); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private class ReadTask implements Runnable { + private final ChunkPosition position; + private final CompletableFuture future; + + public ReadTask(ChunkPosition position, CompletableFuture future) { + this.position = position; + this.future = future; + } + + @Override + public void run() { + try { + future.complete(ChunkSerializer.read(position)); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCache.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCache.java index d3208867..15b62ec4 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCache.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCache.java @@ -1,10 +1,11 @@ package net.imprex.orebfuscator.cache; -import java.io.IOException; -import java.util.Arrays; import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import org.bukkit.Bukkit; @@ -17,6 +18,7 @@ import net.imprex.orebfuscator.Orebfuscator; import net.imprex.orebfuscator.config.CacheConfig; +import net.imprex.orebfuscator.obfuscation.ObfuscatedChunk; import net.imprex.orebfuscator.util.ChunkPosition; public class ChunkCache { @@ -30,19 +32,30 @@ public static final byte[] hash(byte[] configHash, byte[] chunkData) { return hasher.hash().asBytes(); } + private final Orebfuscator orebfuscator; private final CacheConfig cacheConfig; - private final Cache cache; - private final ChunkCacheSerializer serializer; + private final Cache cache; + private final AsyncChunkSerializer serializer; + + private final ExecutorService cacheExecutor; public ChunkCache(Orebfuscator orebfuscator) { + this.orebfuscator = orebfuscator; this.cacheConfig = orebfuscator.getOrebfuscatorConfig().cache(); + this.cacheExecutor = new ForkJoinPool(Runtime.getRuntime().availableProcessors(), + pool -> { + ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + worker.setName("ofc-cache-pool-" + worker.getPoolIndex()); + return worker; + }, null, true); + this.cache = CacheBuilder.newBuilder().maximumSize(this.cacheConfig.maximumSize()) .expireAfterAccess(this.cacheConfig.expireAfterAccess(), TimeUnit.MILLISECONDS) .removalListener(this::onRemoval).build(); - this.serializer = new ChunkCacheSerializer(); + this.serializer = new AsyncChunkSerializer(orebfuscator); if (this.cacheConfig.enabled() && this.cacheConfig.deleteRegionFilesAfterAccess() > 0) { Bukkit.getScheduler().runTaskTimerAsynchronously(orebfuscator, new CacheCleanTask(orebfuscator), 0, @@ -50,69 +63,58 @@ public ChunkCache(Orebfuscator orebfuscator) { } } - private void onRemoval(RemovalNotification notification) { + private void onRemoval(RemovalNotification notification) { if (notification.wasEvicted()) { - try { - this.serializer.write(notification.getKey(), notification.getValue()); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private ChunkCacheEntry load(ChunkPosition key) { - try { - return this.serializer.read(key); - } catch (IOException e) { - e.printStackTrace(); + this.serializer.write(notification.getKey(), notification.getValue()); } - return null; } - public ChunkCacheEntry get(ChunkPosition key, byte[] hash, - Function mappingFunction) { - Objects.requireNonNull(mappingFunction); + public CompletableFuture get(ChunkCacheRequest request) { + CompletableFuture future = new CompletableFuture<>(); + this.cacheExecutor.execute(() -> { + ChunkPosition key = request.getKey(); - // check if live cache entry is present and valid - ChunkCacheEntry cacheEntry = this.cache.getIfPresent(key); - if (cacheEntry != null && Arrays.equals(cacheEntry.getHash(), hash)) { - return cacheEntry; - } + ObfuscatedChunk cacheChunk = this.cache.getIfPresent(key); + if (request.isValid(cacheChunk)) { + future.complete(cacheChunk); + return; + } - // check if disk cache entry is present and valid - cacheEntry = this.load(key); - if (cacheEntry != null && Arrays.equals(cacheEntry.getHash(), hash)) { - this.cache.put(key, Objects.requireNonNull(cacheEntry)); - return cacheEntry; - } + // check if disk cache entry is present and valid + this.serializer.read(key).thenAcceptAsync(diskChunk -> { + if (request.isValid(diskChunk)) { + this.cache.put(key, diskChunk); + future.complete(diskChunk); + return; + } - // create new entry no valid ones found - cacheEntry = mappingFunction.apply(key); - this.cache.put(key, Objects.requireNonNull(cacheEntry)); - return cacheEntry; + // create new entry no valid ones found + request.obfuscate().thenAcceptAsync(chunk -> { + this.cache.put(key, Objects.requireNonNull(chunk)); + future.complete(chunk); + }, this.cacheExecutor); + }, this.cacheExecutor); + }); + return future; } public void invalidate(ChunkPosition key) { - this.cache.invalidate(key); - try { + if (this.orebfuscator.isMainThread()) { + this.cacheExecutor.execute(() -> { + this.invalidate(key); + }); + } else { + this.cache.invalidate(key); this.serializer.write(key, null); - } catch (IOException e) { - e.printStackTrace(); } } - public void invalidateAll(boolean save) { - if (save) { - this.cache.asMap().entrySet().removeIf(entry -> { - try { - this.serializer.write(entry.getKey(), entry.getValue()); - } catch (IOException e) { - e.printStackTrace(); - } - return true; - }); - } else { - this.cache.invalidateAll(); - } + public void close() { + this.cache.asMap().entrySet().removeIf(entry -> { + this.serializer.write(entry.getKey(), entry.getValue()); + return true; + }); + this.cacheExecutor.shutdown(); + this.serializer.close(); } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCacheRequest.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCacheRequest.java new file mode 100644 index 00000000..1aa33922 --- /dev/null +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCacheRequest.java @@ -0,0 +1,44 @@ +package net.imprex.orebfuscator.cache; + +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; + +import net.imprex.orebfuscator.chunk.ChunkStruct; +import net.imprex.orebfuscator.obfuscation.ObfuscatedChunk; +import net.imprex.orebfuscator.obfuscation.Obfuscator; +import net.imprex.orebfuscator.util.ChunkPosition; + +public class ChunkCacheRequest { + + private final Obfuscator obfuscator; + private final ChunkPosition key; + private final byte[] hash; + private final ChunkStruct chunkStruct; + + public ChunkCacheRequest(Obfuscator obfuscator, ChunkPosition key, byte[] hash, ChunkStruct chunkStruct) { + this.obfuscator = obfuscator; + this.key = key; + this.hash = hash; + this.chunkStruct = chunkStruct; + } + + public CompletableFuture obfuscate() { + return this.obfuscator.obfuscate(this); + } + + public boolean isValid(ObfuscatedChunk chunk) { + return chunk != null && Arrays.equals(chunk.getHash(), this.hash); + } + + public ChunkPosition getKey() { + return key; + } + + public byte[] getHash() { + return hash; + } + + public ChunkStruct getChunkStruct() { + return chunkStruct; + } +} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCacheSerializer.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkSerializer.java similarity index 79% rename from orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCacheSerializer.java rename to orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkSerializer.java index 47452328..31ac16c1 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkCacheSerializer.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkSerializer.java @@ -6,23 +6,24 @@ import java.util.Collection; import net.imprex.orebfuscator.NmsInstance; +import net.imprex.orebfuscator.obfuscation.ObfuscatedChunk; import net.imprex.orebfuscator.util.BlockCoords; import net.imprex.orebfuscator.util.ChunkPosition; -public class ChunkCacheSerializer { +public class ChunkSerializer { private static final int CACHE_VERSION = 1; - private DataInputStream createInputStream(ChunkPosition key) throws IOException { + private static DataInputStream createInputStream(ChunkPosition key) throws IOException { return NmsInstance.getRegionFileCache().createInputStream(key); } - private DataOutputStream createOutputStream(ChunkPosition key) throws IOException { + private static DataOutputStream createOutputStream(ChunkPosition key) throws IOException { return NmsInstance.getRegionFileCache().createOutputStream(key); } - public ChunkCacheEntry read(ChunkPosition key) throws IOException { - try (DataInputStream dataInputStream = this.createInputStream(key)) { + public static ObfuscatedChunk read(ChunkPosition key) throws IOException { + try (DataInputStream dataInputStream = createInputStream(key)) { if (dataInputStream != null) { // check if cache entry has right version and if chunk is present if (dataInputStream.readInt() != CACHE_VERSION || !dataInputStream.readBoolean()) { @@ -35,7 +36,7 @@ public ChunkCacheEntry read(ChunkPosition key) throws IOException { byte[] data = new byte[dataInputStream.readInt()]; dataInputStream.readFully(data); - ChunkCacheEntry chunkCacheEntry = new ChunkCacheEntry(hash, data); + ObfuscatedChunk chunkCacheEntry = new ObfuscatedChunk(hash, data); Collection proximityBlocks = chunkCacheEntry.getProximityBlocks(); for (int i = dataInputStream.readInt(); i > 0; i--) { @@ -56,8 +57,8 @@ public ChunkCacheEntry read(ChunkPosition key) throws IOException { } // TODO consider size limit for cache since RegionFile before 1.14 have a hard limit of 256 * 4kb - public void write(ChunkPosition key, ChunkCacheEntry value) throws IOException { - try (DataOutputStream dataOutputStream = this.createOutputStream(key)) { + public static void write(ChunkPosition key, ObfuscatedChunk value) throws IOException { + try (DataOutputStream dataOutputStream = createOutputStream(key)) { dataOutputStream.writeInt(CACHE_VERSION); if (value != null) { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkStruct.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkStruct.java index 4686f770..43b4e0bd 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkStruct.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkStruct.java @@ -9,5 +9,4 @@ public class ChunkStruct { public int primaryBitMask; public byte[] data; - } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java index 52cf6c64..27b687dd 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java @@ -21,9 +21,13 @@ public class OrebfuscatorCacheConfig implements CacheConfig { private int maximumOpenRegionFiles = 256; private long deleteRegionFilesAfterAccess = TimeUnit.DAYS.toMillis(2); - private int maximumSize = 4096; + private int maximumSize = 8192; private long expireAfterAccess = TimeUnit.SECONDS.toMillis(30); + private int maximumTaskQueueSize = 32768; + private int protocolLibThreads = -1; + private boolean protocolLibThreadsSet = false; + public void serialize(ConfigurationSection section) { this.enabled(section.getBoolean("enabled", true)); this.serializeBaseDirectory(section, "orebfuscator_cache/"); @@ -31,8 +35,11 @@ public void serialize(ConfigurationSection section) { this.maximumOpenRegionFiles(section.getInt("maximumOpenRegionFiles", 256)); this.deleteRegionFilesAfterAccess(section.getLong("deleteRegionFilesAfterAccess", TimeUnit.DAYS.toMillis(2))); - this.maximumSize(section.getInt("maximumSize", 4096)); + this.maximumSize(section.getInt("maximumSize", 8192)); this.expireAfterAccess(section.getLong("expireAfterAccess", TimeUnit.SECONDS.toMillis(30))); + + this.maximumTaskQueueSize(section.getInt("maximumTaskQueueSize", 32768)); + this.protocolLibThreads(section.getInt("protocolLibThreads", -1)); } public void deserialize(ConfigurationSection section) { @@ -45,6 +52,9 @@ public void deserialize(ConfigurationSection section) { section.set("maximumSize", this.maximumSize); section.set("expireAfterAccess", this.expireAfterAccess); + + section.set("maximumTaskQueueSize", this.maximumTaskQueueSize); + section.set("protocolLibThreads", this.protocolLibThreadsSet ? this.protocolLibThreads : -1); } private void serializeBaseDirectory(ConfigurationSection section, String defaultPath) { @@ -54,15 +64,15 @@ private void serializeBaseDirectory(ConfigurationSection section, String default try { this.baseDirectory = worldPath.resolve(baseDirectory).normalize(); } catch (InvalidPathException e) { - OFCLogger - .log(Level.WARNING, "config path '" + section.getCurrentPath() + ".baseDirectory' contains malformed path '" + OFCLogger.log(Level.WARNING, + "config path '" + section.getCurrentPath() + ".baseDirectory' contains malformed path '" + baseDirectory + "', using default path '" + defaultPath + "'"); this.baseDirectory = worldPath.resolve(defaultPath).normalize(); } if (!this.baseDirectory.startsWith(worldPath)) { - OFCLogger - .log(Level.WARNING, "config path '" + section.getCurrentPath() + ".baseDirectory' is no child directory of '" + OFCLogger.log(Level.WARNING, + "config path '" + section.getCurrentPath() + ".baseDirectory' is no child directory of '" + worldPath + "', using default path: '" + defaultPath + "'"); this.baseDirectory = worldPath.resolve(defaultPath).normalize(); } @@ -100,7 +110,7 @@ public void baseDirectory(Path path) { @Override public Path regionFile(ChunkPosition key) { - return this.baseDirectory.resolve(key.getWorld()) + return this.baseDirectory.resolve(key.getWorld().getName()) .resolve("r." + (key.getX() >> 5) + "." + (key.getZ() >> 5) + ".mca"); } @@ -112,7 +122,7 @@ public int maximumOpenRegionFiles() { @Override public void maximumOpenRegionFiles(int count) { if (count < 1) { - throw new IllegalArgumentException("maximum open region files is lower than one"); + throw new IllegalArgumentException("cache.maximumOpenRegionFiles is lower than one"); } this.maximumOpenRegionFiles = count; } @@ -125,7 +135,7 @@ public long deleteRegionFilesAfterAccess() { @Override public void deleteRegionFilesAfterAccess(long expire) { if (expire < 1) { - throw new IllegalArgumentException("delete region files after access is lower than one"); + throw new IllegalArgumentException("cache.deleteRegionFilesAfterAccess is lower than one"); } this.deleteRegionFilesAfterAccess = expire; } @@ -138,7 +148,7 @@ public int maximumSize() { @Override public void maximumSize(int size) { if (size < 1) { - throw new IllegalArgumentException("maximum size is lower than one"); + throw new IllegalArgumentException("cache.maximumSize is lower than one"); } this.maximumSize = size; } @@ -151,8 +161,39 @@ public long expireAfterAccess() { @Override public void expireAfterAccess(long expire) { if (expire < 1) { - throw new IllegalArgumentException("expire is lower than one"); + throw new IllegalArgumentException("cache.expireAfterAccess is lower than one"); } this.expireAfterAccess = expire; } + + @Override + public int maximumTaskQueueSize() { + return this.maximumTaskQueueSize; + } + + @Override + public void maximumTaskQueueSize(int size) { + if (size < 1) { + throw new IllegalArgumentException("cache.maximumTaskQueueSize is lower than one"); + } + this.maximumTaskQueueSize = size; + } + + @Override + public int protocolLibThreads() { + return this.protocolLibThreads; + } + + @Override + public void protocolLibThreads(int threads) { + if (threads < 1) { + this.protocolLibThreads = Runtime.getRuntime().availableProcessors(); + OFCLogger.info("cache.protocolLibThreads is less than one, choosing processor count as value = " + + this.protocolLibThreads); + this.protocolLibThreadsSet = false; + } else { + this.protocolLibThreads = threads; + this.protocolLibThreadsSet = true; + } + } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorConfig.java index 0dd1b314..88b4a4f8 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorConfig.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorConfig.java @@ -63,7 +63,6 @@ public void store() { } this.deserialize(section); this.plugin.saveConfig(); - this.reload(); } private void createConfigIfNotExist() { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java index a7c2cb05..d42570e8 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java @@ -119,8 +119,12 @@ private void deserializeHiddenBlocks(ConfigurationSection section, Map proximityBlocks; private final Set removedTileEntities; - public ChunkCacheEntry(byte[] hash, byte[] data) { + public ObfuscatedChunk(byte[] hash, byte[] data) { this(hash, data, new HashSet<>(), new HashSet<>()); } - public ChunkCacheEntry(byte[] hash, byte[] data, Set proximityBlocks, + public ObfuscatedChunk(byte[] hash, byte[] data, Set proximityBlocks, Set removedTileEntities) { this.hash = hash; this.data = data; diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationListener.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationListener.java index 7fb64c54..db6c7c0f 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationListener.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationListener.java @@ -63,7 +63,6 @@ private void onUpdate(List blocks) { return; } - String worldName = world.getName(); BlockMask blockMask = this.config.blockMask(world); Set updateBlocks = new HashSet<>(); @@ -79,7 +78,7 @@ private void onUpdate(List blocks) { BlockStateHolder blockState = NmsInstance.getBlockState(world, x, y, z); if (blockState != null) { getAdjacentBlocks(updateBlocks, world, blockMask, blockState, updateRadius); - invalidChunks.add(new ChunkPosition(worldName, x >> 4, z >> 4)); + invalidChunks.add(new ChunkPosition(world, x >> 4, z >> 4)); } } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/Obfuscator.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/Obfuscator.java index f87c56ff..5662d0bb 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/Obfuscator.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/Obfuscator.java @@ -2,13 +2,15 @@ import java.util.HashSet; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import org.bukkit.Bukkit; import org.bukkit.World; import net.imprex.orebfuscator.NmsInstance; import net.imprex.orebfuscator.Orebfuscator; import net.imprex.orebfuscator.cache.ChunkCache; -import net.imprex.orebfuscator.cache.ChunkCacheEntry; +import net.imprex.orebfuscator.cache.ChunkCacheRequest; import net.imprex.orebfuscator.chunk.Chunk; import net.imprex.orebfuscator.chunk.ChunkSection; import net.imprex.orebfuscator.chunk.ChunkStruct; @@ -22,30 +24,45 @@ public class Obfuscator { + private final Orebfuscator orebfuscator; private final OrebfuscatorConfig config; private final ChunkCache chunkCache; public Obfuscator(Orebfuscator orebfuscator) { + this.orebfuscator = orebfuscator; this.config = orebfuscator.getOrebfuscatorConfig(); this.chunkCache = orebfuscator.getChunkCache(); } - public ChunkCacheEntry obfuscateOrUseCache(World world, ChunkStruct chunkStruct) { - if (chunkStruct.primaryBitMask == 0) { - return null; - } - - final ChunkPosition position = new ChunkPosition(world.getName(), chunkStruct.chunkX, chunkStruct.chunkZ); + public CompletableFuture obfuscateOrUseCache(World world, ChunkStruct chunkStruct) { + final ChunkPosition position = new ChunkPosition(world, chunkStruct.chunkX, chunkStruct.chunkZ); final byte[] hash = ChunkCache.hash(this.config.hash(), chunkStruct.data); + final ChunkCacheRequest request = new ChunkCacheRequest(this, position, hash, chunkStruct); if (this.config.cache().enabled()) { - return this.chunkCache.get(position, hash, key -> this.obfuscate(hash, world, chunkStruct)); + return this.chunkCache.get(request); } else { - return this.obfuscate(hash, world, chunkStruct); + return this.obfuscate(request); } } - private ChunkCacheEntry obfuscate(byte[] hash, World world, ChunkStruct chunkStruct) { + public CompletableFuture obfuscate(ChunkCacheRequest request) { + CompletableFuture future = new CompletableFuture<>(); + if (this.orebfuscator.isMainThread()) { + future.complete(this.obfuscateNow(request)); + } else { + Bukkit.getScheduler().runTask(this.orebfuscator, () -> { + future.complete(this.obfuscateNow(request)); + }); + } + return future; + } + + private ObfuscatedChunk obfuscateNow(ChunkCacheRequest request) { + World world = request.getKey().getWorld(); + byte[] hash = request.getHash(); + ChunkStruct chunkStruct = request.getChunkStruct(); + BlockMask blockMask = this.config.blockMask(world); WorldConfig worldConfig = this.config.world(world); ProximityConfig proximityConfig = this.config.proximity(world); @@ -117,7 +134,7 @@ private ChunkCacheEntry obfuscate(byte[] hash, World world, ChunkStruct chunkStr byte[] data = chunk.finalizeOutput(); - return new ChunkCacheEntry(hash, data, proximityBlocks, removedTileEntities); + return new ObfuscatedChunk(hash, data, proximityBlocks, removedTileEntities); } catch (Exception e) { e.printStackTrace(); throw new Error(e); diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/PacketListener.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/PacketListener.java index 0603c997..5f89018d 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/PacketListener.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/PacketListener.java @@ -7,9 +7,11 @@ import org.bukkit.World; import org.bukkit.entity.Player; +import com.comphenix.protocol.AsynchronousManager; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.async.AsyncListenerHandler; import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; @@ -18,7 +20,6 @@ import com.comphenix.protocol.wrappers.nbt.NbtCompound; import net.imprex.orebfuscator.Orebfuscator; -import net.imprex.orebfuscator.cache.ChunkCacheEntry; import net.imprex.orebfuscator.chunk.ChunkStruct; import net.imprex.orebfuscator.config.OrebfuscatorConfig; import net.imprex.orebfuscator.proximityhider.ProximityHider; @@ -28,6 +29,9 @@ public class PacketListener extends PacketAdapter { private final ProtocolManager protocolManager; + private final AsynchronousManager asynchronousManager; + private final AsyncListenerHandler asyncListenerHandler; + private final boolean async; private final OrebfuscatorConfig config; private final Obfuscator obfuscator; @@ -38,8 +42,17 @@ public PacketListener(Orebfuscator orebfuscator) { super(orebfuscator, PacketType.Play.Server.MAP_CHUNK); this.protocolManager = ProtocolLibrary.getProtocolManager(); - // TODO async - this.protocolManager.addPacketListener(this); + this.asynchronousManager = this.protocolManager.getAsynchronousManager(); + + if (orebfuscator.getOrebfuscatorConfig().cache().enabled()) { + this.asyncListenerHandler = this.asynchronousManager.registerAsyncHandler(this); + this.asyncListenerHandler.start(orebfuscator.getOrebfuscatorConfig().cache().protocolLibThreads()); + this.async = true; + } else { + this.async = false; + this.asyncListenerHandler = null; + this.protocolManager.addPacketListener(this); + } this.config = orebfuscator.getOrebfuscatorConfig(); this.obfuscator = orebfuscator.getObfuscator(); @@ -47,7 +60,11 @@ public PacketListener(Orebfuscator orebfuscator) { } public void unregister() { - this.protocolManager.removePacketListener(this); + if (this.asyncListenerHandler != null) { + this.asynchronousManager.unregisterAsyncHandler(this.asyncListenerHandler); + } else { + this.protocolManager.removePacketListener(this); + } } @Override @@ -76,17 +93,30 @@ public void onPacketSending(PacketEvent event) { chunkStruct.data = byteArray.read(0); chunkStruct.isOverworld = world.getEnvironment() == World.Environment.NORMAL; - ChunkCacheEntry chunkEntry = this.obfuscator.obfuscateOrUseCache(world, chunkStruct); - if (chunkEntry != null) { - byteArray.write(0, chunkEntry.getData()); + if (chunkStruct.primaryBitMask == 0) { + return; + } + + if (this.async) { + event.getAsyncMarker().incrementProcessingDelay(); + } + + this.obfuscator.obfuscateOrUseCache(world, chunkStruct).thenAccept(chunk -> { + if (chunk != null) { + byteArray.write(0, chunk.getData()); + + if (tileEntityList != null) { + PacketListener.removeBlockEntities(tileEntityList, chunk.getRemovedTileEntities()); + nbtList.write(0, tileEntityList); + } - if (tileEntityList != null) { - PacketListener.removeBlockEntities(tileEntityList, chunkEntry.getRemovedTileEntities()); - nbtList.write(0, tileEntityList); + this.proximityHider.addProximityBlocks(player, chunkStruct.chunkX, chunkStruct.chunkZ, chunk.getProximityBlocks()); } - this.proximityHider.addProximityBlocks(player, chunkStruct.chunkX, chunkStruct.chunkZ, chunkEntry.getProximityBlocks()); - } + if (this.async) { + this.asynchronousManager.signalPacketTransmission(event); + } + }); } private static void removeBlockEntities(List> tileEntityList, Set removedTileEntities) { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityHider.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityHider.java index b7be9c7b..ba08fce4 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityHider.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityHider.java @@ -51,14 +51,13 @@ public void start() { if (this.queueThreads[i] == null) { ProximityThread thread = new ProximityThread(this, this.orebfuscator); thread.setDaemon(true); - thread.setName("OFC - ProximityHider Thread - #" + i); thread.start(); this.queueThreads[i] = thread; } } } - public Player pollPlayer() { + public Player pollPlayer() throws InterruptedException { return this.queue.poll(); } @@ -116,6 +115,9 @@ public void destroy() { throw new IllegalStateException("proximity hider isn't running"); } + this.queue.clear(); + this.playerData.invalidateAll(); + for (ProximityThread thread : this.queueThreads) { if (thread != null) { // TODO set thread null diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityQueue.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityQueue.java index 709aafa6..392c280b 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityQueue.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityQueue.java @@ -36,11 +36,11 @@ public void offerAndLock(Player player) { } } - public Player poll() { + public Player poll() throws InterruptedException { lock.lock(); try { if (this.queue.isEmpty()) { - this.notEmpty.awaitUninterruptibly(); + this.notEmpty.await(); } return this.queue.poll(); } finally { @@ -67,4 +67,14 @@ public void remove(Player player) { lock.unlock(); } } + + public void clear() { + lock.lock(); + try { + this.lockedPlayer.clear(); + this.queue.clear(); + } finally { + lock.unlock(); + } + } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityThread.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityThread.java index 1f448925..cd82f869 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityThread.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximityhider/ProximityThread.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -17,10 +18,11 @@ import net.imprex.orebfuscator.config.ProximityConfig; import net.imprex.orebfuscator.util.BlockCoords; import net.imprex.orebfuscator.util.MathUtil; -import net.imprex.orebfuscator.util.OFCLogger; public class ProximityThread extends Thread { + private static final AtomicInteger NEXT_ID = new AtomicInteger(); + private final Orebfuscator orebfuscator; private final OrebfuscatorConfig config; @@ -28,6 +30,7 @@ public class ProximityThread extends Thread { private final AtomicBoolean running = new AtomicBoolean(true); public ProximityThread(ProximityHider proximityHider, Orebfuscator orebfuscator) { + super(Orebfuscator.THREAD_GROUP, "ofc-proximity-hider-" + NEXT_ID.getAndIncrement()); this.proximityHider = proximityHider; this.orebfuscator = orebfuscator; this.config = orebfuscator.getOrebfuscatorConfig(); @@ -36,77 +39,81 @@ public ProximityThread(ProximityHider proximityHider, Orebfuscator orebfuscator) @Override public void run() { while (this.running.get()) { - Player player = this.proximityHider.pollPlayer(); - try { - if (player == null || !player.isOnline()) { - continue; - } + Player player = this.proximityHider.pollPlayer(); + try { + if (player == null || !player.isOnline()) { + continue; + } - Location location = player.getLocation(); - World world = location.getWorld(); + Location location = player.getLocation(); + World world = location.getWorld(); - ProximityConfig proximityConfig = this.config.proximity(world); - ProximityPlayerData proximityPlayer = this.proximityHider.getPlayer(player); - if (proximityPlayer == null || proximityConfig == null || !proximityConfig.enabled() || !proximityPlayer.getWorld().equals(world)) { - continue; - } + ProximityConfig proximityConfig = this.config.proximity(world); + ProximityPlayerData proximityPlayer = this.proximityHider.getPlayer(player); + if (proximityPlayer == null || proximityConfig == null || !proximityConfig.enabled() || !proximityPlayer.getWorld().equals(world)) { + continue; + } - int distance = proximityConfig.distance(); - int distanceSquared = proximityConfig.distanceSquared(); + int distance = proximityConfig.distance(); + int distanceSquared = proximityConfig.distanceSquared(); - List updateBlocks = new ArrayList<>(); - Location eyeLocation = player.getEyeLocation(); + List updateBlocks = new ArrayList<>(); + Location eyeLocation = player.getEyeLocation(); - int minChunkX = (location.getBlockX() - distance) >> 4; - int maxChunkX = (location.getBlockX() + distance) >> 4; - int minChunkZ = (location.getBlockZ() - distance) >> 4; - int maxChunkZ = (location.getBlockZ() + distance) >> 4; + int minChunkX = (location.getBlockX() - distance) >> 4; + int maxChunkX = (location.getBlockX() + distance) >> 4; + int minChunkZ = (location.getBlockZ() - distance) >> 4; + int maxChunkZ = (location.getBlockZ() + distance) >> 4; - for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { - for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { - Set blocks = proximityPlayer.getBlocks(chunkX, chunkZ); + for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { + for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { + Set blocks = proximityPlayer.getBlocks(chunkX, chunkZ); - if (blocks == null) { - continue; - } + if (blocks == null) { + continue; + } - for (Iterator iterator = blocks.iterator(); iterator.hasNext(); ) { - BlockCoords blockCoords = iterator.next(); - Location blockLocation = new Location(world, blockCoords.x, blockCoords.y, blockCoords.z); + for (Iterator iterator = blocks.iterator(); iterator.hasNext(); ) { + BlockCoords blockCoords = iterator.next(); + Location blockLocation = new Location(world, blockCoords.x, blockCoords.y, blockCoords.z); - if (location.distanceSquared(blockLocation) < distanceSquared) { - if (!proximityConfig.useFastGazeCheck() || MathUtil.doFastCheck(blockLocation, eyeLocation, world)) { - iterator.remove(); - updateBlocks.add(blockCoords); + if (location.distanceSquared(blockLocation) < distanceSquared) { + if (!proximityConfig.useFastGazeCheck() || MathUtil.doFastCheck(blockLocation, eyeLocation, world)) { + iterator.remove(); + updateBlocks.add(blockCoords); + } } } - } - if (blocks.isEmpty()) { - proximityPlayer.removeChunk(chunkX, chunkZ); + if (blocks.isEmpty()) { + proximityPlayer.removeChunk(chunkX, chunkZ); + } } } - } - Bukkit.getScheduler().runTask(this.orebfuscator, () -> { - if (player.isOnline()) { - for (BlockCoords blockCoords : updateBlocks) { - if (NmsInstance.sendBlockChange(player, blockCoords)) { - NmsInstance.updateBlockTileEntity(player, blockCoords); + Bukkit.getScheduler().runTask(this.orebfuscator, () -> { + if (player.isOnline()) { + for (BlockCoords blockCoords : updateBlocks) { + if (NmsInstance.sendBlockChange(player, blockCoords)) { + NmsInstance.updateBlockTileEntity(player, blockCoords); + } } } - } - }); + }); + } finally { + this.proximityHider.unlockPlayer(player); + } + } catch (InterruptedException e) { + break; } catch (Exception e) { - OFCLogger.err(e); - } finally { - this.proximityHider.unlockPlayer(player); + e.printStackTrace(); } } } public void destroy() { this.running.set(false); + this.interrupt(); } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9662fef4..35d6cfb7 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ pom - 5.0.0 + 5.1.0 4.1.20.Final 1.15.1-R0.1-SNAPSHOT