diff --git a/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch b/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch
index cb800d00d8..7a8708ac6e 100644
--- a/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch
+++ b/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch
@@ -1587,11 +1587,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // apply fixes
+
+ try {
-+ if (chunkData.poiData != null) {
-+ chunkData.poiData = chunkData.poiData.clone(); // clone data for safety, file IO thread does not clone
-+ }
+ chunkData.chunkData = chunkManager.getChunkData(this.world.getWorldProvider().getDimensionManager(),
-+ chunkManager.getWorldPersistentDataSupplier(), chunkData.chunkData.clone(), chunkPos, this.world); // clone data for safety, file IO thread does not clone
++ chunkManager.getWorldPersistentDataSupplier(), chunkData.chunkData, chunkPos, this.world); // clone data for safety, file IO thread does not clone
+ } catch (final Throwable ex) {
+ PaperFileIOThread.LOGGER.error("Could not apply datafixers for chunk task: " + this.toString(), ex);
+ this.complete(ChunkLoadTask.createEmptyHolder());
diff --git a/Spigot-Server-Patches/Chunk-debug-command.patch b/Spigot-Server-Patches/Chunk-debug-command.patch
index 4e2f4cab62..a8e50f79e7 100644
--- a/Spigot-Server-Patches/Chunk-debug-command.patch
+++ b/Spigot-Server-Patches/Chunk-debug-command.patch
@@ -226,7 +226,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
- import java.util.concurrent.Executor;
+ import java.util.concurrent.LinkedBlockingQueue;
@@ -0,0 +0,0 @@ public final class MCUtil {
return null;
diff --git a/Spigot-Server-Patches/Hook-into-CB-plugin-rewrites.patch b/Spigot-Server-Patches/Hook-into-CB-plugin-rewrites.patch
index 1cd9e4ce08..6431b24064 100644
--- a/Spigot-Server-Patches/Hook-into-CB-plugin-rewrites.patch
+++ b/Spigot-Server-Patches/Hook-into-CB-plugin-rewrites.patch
@@ -19,7 +19,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ 8.0.1
compile
-
+
diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
diff --git a/Spigot-Server-Patches/MC-Utils.patch b/Spigot-Server-Patches/MC-Utils.patch
index f79591b4be..be8d0831f0 100644
--- a/Spigot-Server-Patches/MC-Utils.patch
+++ b/Spigot-Server-Patches/MC-Utils.patch
@@ -2083,84 +2083,167 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@@ -0,0 +0,0 @@
+package com.destroystokyo.paper.util.pooled;
+
++import net.minecraft.server.MCUtil;
+import org.apache.commons.lang3.mutable.MutableInt;
++
+import java.util.ArrayDeque;
+import java.util.concurrent.ThreadLocalRandom;
++import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.ReentrantLock;
++import java.util.function.Consumer;
+
++/**
++ * Object pooling with thread safe, low contention design. Pooled objects have no additional object overhead
++ * due to usage of ArrayDeque per insertion/removal unless a resizing is needed in the buckets.
++ * Supports up to bucket size (default 8) threads concurrently accessing if all buckets have a value.
++ * Releasing may conditionally have contention if multiple buckets have same current size, but randomization will be used.
++ *
++ * Original interface API by Spottedleaf
++ * Implementation by Aikar
++ * @license MIT
++ */
+public final class PooledObjects {
+
-+ public static final PooledObjects POOLED_MUTABLE_INTEGERS = new PooledObjects<>(MutableInt::new, 200, -1);
++ /**
++ * Wrapper for an object that will be have a cleaner registered for it, and may be automatically returned to pool.
++ */
++ public class AutoReleased {
++ private final E object;
++ private final Runnable cleaner;
++
++ public AutoReleased(E object, Runnable cleaner) {
++ this.object = object;
++ this.cleaner = cleaner;
++ }
++
++ public final E getObject() {
++ return object;
++ }
++
++ public final Runnable getCleaner() {
++ return cleaner;
++ }
++ }
++
++ public static final PooledObjects POOLED_MUTABLE_INTEGERS = new PooledObjects<>(MutableInt::new, 1024, 16);
+
+ private final PooledObjectHandler handler;
-+ private final int maxPoolSize;
-+ private final int expectingThreads;
++ private final int bucketCount;
++ private final int bucketSize;
++ private final ArrayDeque[] buckets;
++ private final ReentrantLock[] locks;
++ private final AtomicLong bucketIdCounter = new AtomicLong(0);
+
-+ private final IsolatedPool mainPool;
-+ // use these under contention
-+ private final IsolatedPool[] contendedPools;
-+
-+ public PooledObjects(final PooledObjectHandler handler, final int maxPoolSize, int expectingThreads) {
++ public PooledObjects(final PooledObjectHandler handler, int maxPoolSize) {
++ this(handler, maxPoolSize, 8);
++ }
++ public PooledObjects(final PooledObjectHandler handler, int maxPoolSize, int bucketCount) {
+ if (handler == null) {
+ throw new NullPointerException("Handler must not be null");
+ }
+ if (maxPoolSize <= 0) {
+ throw new IllegalArgumentException("Max pool size must be greater-than 0");
+ }
-+ if (expectingThreads <= 0) {
-+ expectingThreads = Runtime.getRuntime().availableProcessors();
++ if (bucketCount < 1) {
++ throw new IllegalArgumentException("Bucket count must be greater-than 0");
+ }
-+
++ int remainder = maxPoolSize % bucketCount;
++ if (remainder > 0) {
++ // Auto adjust up to the next bucket divisible size
++ maxPoolSize = maxPoolSize - remainder + bucketCount;
++ }
++ //noinspection unchecked
++ this.buckets = new ArrayDeque[bucketCount];
++ this.locks = new ReentrantLock[bucketCount];
++ this.bucketCount = bucketCount;
+ this.handler = handler;
-+ this.maxPoolSize = maxPoolSize;
-+ this.expectingThreads = expectingThreads;
-+ this.mainPool = new IsolatedPool<>(handler, maxPoolSize);
-+ final IsolatedPool[] contendedPools = new IsolatedPool[2 * expectingThreads];
++ this.bucketSize = maxPoolSize / bucketCount;
++ for (int i = 0; i < bucketCount; i++) {
++ this.buckets[i] = new ArrayDeque<>(bucketSize / 4);
++ this.locks[i] = new ReentrantLock();
++ }
++ }
+
-+ for (int i = 0; i < contendedPools.length; ++i) {
-+ contendedPools[i] = new IsolatedPool<>(handler, Math.max(1, maxPoolSize / 2));
++ public AutoReleased acquireCleaner(Object holder) {
++ return acquireCleaner(holder, this::release);
++ }
++
++ public AutoReleased acquireCleaner(Object holder, Consumer releaser) {
++ E resource = acquire();
++ Runnable cleaner = MCUtil.registerCleaner(holder, resource, releaser);
++ return new AutoReleased(resource, cleaner);
++ }
++
++
++ public long size() {
++ long size = 0;
++ for (int i = 0; i < bucketCount; i++) {
++ size += this.buckets[i].size();
+ }
+
-+ this.contendedPools = contendedPools;
++ return size;
+ }
-+
-+ // Taken from
-+ // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
-+ // https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2016/06/25/fastrange.c
-+ // Original license is public domain
-+ public static int fastRandomBounded(final long randomInteger, final long limit) {
-+ // randomInteger must be [0, pow(2, 32))
-+ // limit must be [0, pow(2, 32))
-+ return (int)((randomInteger * limit) >>> 32);
-+ }
-+
+ public E acquire() {
-+ E ret;
-+ PooledObjects.IsolatedPool pooled = this.mainPool;
-+ int lastIndex = -1;
-+ while ((ret = pooled.tryAcquireUncontended()) == null) {
-+ int index;
-+ while (lastIndex == (index = fastRandomBounded(ThreadLocalRandom.current().nextInt() & 0xFFFFFFFFL, this.contendedPools.length)));
-+ lastIndex = index;
-+ pooled = this.contendedPools[index];
++ for (int base = (int) (this.bucketIdCounter.getAndIncrement() % bucketCount), i = 0; i < bucketCount; i++ ) {
++ int bucketId = (base + i) % bucketCount;
++ if (this.buckets[bucketId].isEmpty()) continue;
++ // lock will alloc an object if blocked, so spinwait instead since lock duration is super fast
++ lockBucket(bucketId);
++ E value = this.buckets[bucketId].poll();
++ this.locks[bucketId].unlock();
++ if (value != null) {
++ this.handler.onAcquire(value);
++ return value;
++ }
+ }
++ return this.handler.createNew();
++ }
+
-+ return ret;
++ private void lockBucket(int bucketId) {
++ // lock will alloc an object if blocked, try to avoid unless 2 failures
++ ReentrantLock lock = this.locks[bucketId];
++ if (!lock.tryLock()) {
++ Thread.yield();
++ } else {
++ return;
++ }
++ if (!lock.tryLock()) {
++ Thread.yield();
++ lock.lock();
++ }
+ }
+
+ public void release(final E value) {
-+ PooledObjects.IsolatedPool pooled = this.mainPool;
-+ int lastIndex = -1;
-+ while (!pooled.tryReleaseUncontended(value)) {
-+ int index;
-+ while (lastIndex == (index = fastRandomBounded(ThreadLocalRandom.current().nextInt() & 0xFFFFFFFFL, this.contendedPools.length)));
-+ lastIndex = index;
-+ pooled = this.contendedPools[index];
-+ }
++ int attempts = 3; // cap on contention
++ do {
++ // find least filled bucket before locking
++ int smallestIdx = -1;
++ int smallest = Integer.MAX_VALUE;
++ for (int i = 0; i < bucketCount; i++ ) {
++ ArrayDeque bucket = this.buckets[i];
++ int size = bucket.size();
++ if (size < this.bucketSize && (smallestIdx == -1 || size < smallest || (size == smallest && ThreadLocalRandom.current().nextBoolean()))) {
++ smallestIdx = i;
++ smallest = size;
++ }
++ }
++ if (smallestIdx == -1) return; // Can not find a bucket to fill
++
++ lockBucket(smallestIdx);
++ ArrayDeque bucket = this.buckets[smallestIdx];
++ if (bucket.size() < this.bucketSize) {
++ this.handler.onRelease(value);
++ bucket.push(value);
++ this.locks[smallestIdx].unlock();
++ return;
++ } else {
++ this.locks[smallestIdx].unlock();
++ }
++ } while (attempts-- > 0);
+ }
+
+ /** This object is restricted from interacting with any pool */
-+ public static interface PooledObjectHandler {
++ public interface PooledObjectHandler {
+
+ /**
+ * Must return a non-null object
@@ -2171,89 +2254,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
+ default void onRelease(final E value) {}
+ }
-+
-+ protected static class IsolatedPool {
-+
-+ protected final PooledObjectHandler handler;
-+
-+ // We use arraydeque as it doesn't create garbage per element...
-+ protected final ArrayDeque pool;
-+ protected final int maxPoolSize;
-+
-+ protected final ReentrantLock lock = new ReentrantLock();
-+
-+ public IsolatedPool(final PooledObjectHandler handler, final int maxPoolSize) {
-+ this.handler = handler;
-+ this.pool = new ArrayDeque<>();
-+ this.maxPoolSize = maxPoolSize;
-+ }
-+
-+ protected E acquireOrCreateNoLock() {
-+ E ret;
-+
-+ ret = this.pool.poll();
-+
-+ if (ret == null) {
-+ ret = this.handler.createNew();
-+ }
-+ this.handler.onAcquire(ret);
-+
-+ return ret;
-+ }
-+
-+ public E tryAcquireUncontended() {
-+ if (!this.lock.tryLock()) {
-+ return null;
-+ }
-+ try {
-+ return this.acquireOrCreateNoLock();
-+ } finally {
-+ this.lock.unlock();
-+ }
-+ }
-+
-+ public E acquire() {
-+ this.lock.lock();
-+ try {
-+ return this.acquireOrCreateNoLock();
-+ } finally {
-+ this.lock.unlock();
-+ }
-+ }
-+
-+ protected void releaseNoLock(final E value) {
-+ if (this.pool.size() >= this.maxPoolSize) {
-+ this.handler.onRelease(value);
-+ return; // can't accept, we're at capacity
-+ }
-+
-+ this.pool.add(value);
-+ this.handler.onRelease(value);
-+ }
-+
-+ public boolean tryReleaseUncontended(final E value) {
-+ if (!this.lock.tryLock()) {
-+ return false;
-+ }
-+
-+ try {
-+ this.releaseNoLock(value);
-+ } finally {
-+ this.lock.unlock();
-+ }
-+
-+ return true;
-+ }
-+
-+ public void release(final E value) {
-+ this.lock.lock();
-+ try {
-+ this.releaseNoLock(value);
-+ } finally {
-+ this.lock.unlock();
-+ }
-+ }
-+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java
new file mode 100644
@@ -3355,21 +3355,80 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import java.util.Queue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
-+import java.util.concurrent.Executor;
-+import java.util.concurrent.Executors;
++import java.util.concurrent.LinkedBlockingQueue;
++import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
++import java.util.concurrent.atomic.AtomicBoolean;
++import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public final class MCUtil {
-+ private static final Executor asyncExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Paper Async Task Handler Thread - %1$d").build());
++ public static final ThreadPoolExecutor asyncExecutor = new ThreadPoolExecutor(
++ 0, 2, 60L, TimeUnit.SECONDS,
++ new LinkedBlockingQueue(),
++ new ThreadFactoryBuilder().setNameFormat("Paper Async Task Handler Thread - %1$d").build()
++ );
++ public static final ThreadPoolExecutor cleanerExecutor = new ThreadPoolExecutor(
++ 1, 1, 0L, TimeUnit.SECONDS,
++ new LinkedBlockingQueue(),
++ new ThreadFactoryBuilder().setNameFormat("Paper Object Cleaner").build()
++ );
+
+ public static final long INVALID_CHUNK_KEY = getCoordinateKey(Integer.MAX_VALUE, Integer.MAX_VALUE);
+
-+ public static void ensureTickThread(final String reason) {
-+ if (MinecraftServer.getServer().serverThread != Thread.currentThread()) {
-+ throw new IllegalStateException(reason);
-+ }
++
++ public static Runnable once(Runnable run) {
++ AtomicBoolean ran = new AtomicBoolean(false);
++ return () -> {
++ if (ran.compareAndSet(false, true)) {
++ run.run();
++ }
++ };
++ }
++
++ private static Runnable makeCleanerCallback(Runnable run) {
++ return once(() -> cleanerExecutor.execute(run));
++ }
++
++ /**
++ * DANGER WILL ROBINSON: Be sure you do not use a lambda that lives in the object being monitored, or leaky leaky!
++ * @param obj
++ * @param run
++ * @return
++ */
++ public static Runnable registerCleaner(Object obj, Runnable run) {
++ // Wrap callback in its own method above or the lambda will leak object
++ Runnable cleaner = makeCleanerCallback(run);
++ co.aikar.cleaner.Cleaner.register(obj, cleaner);
++ return cleaner;
++ }
++
++ /**
++ * DANGER WILL ROBINSON: Be sure you do not use a lambda that lives in the object being monitored, or leaky leaky!
++ * @param obj
++ * @param list
++ * @param cleaner
++ * @param
++ * @return
++ */
++ public static Runnable registerListCleaner(Object obj, List list, Consumer cleaner) {
++ return registerCleaner(obj, () -> {
++ list.forEach(cleaner);
++ list.clear();
++ });
++ }
++
++ /**
++ * DANGER WILL ROBINSON: Be sure you do not use a lambda that lives in the object being monitored, or leaky leaky!
++ * @param obj
++ * @param resource
++ * @param cleaner
++ * @param
++ * @return
++ */
++ public static Runnable registerCleaner(Object obj, T resource, java.util.function.Consumer cleaner) {
++ return registerCleaner(obj, () -> cleaner.accept(resource));
+ }
+
+ public static List getSpiralOutChunks(BlockPosition blockposition, int radius) {
@@ -3753,6 +3812,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ }
+}
+diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/server/MinecraftServer.java
++++ b/src/main/java/net/minecraft/server/MinecraftServer.java
+@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant buildExtraPackets(Packet packet) {
+ java.util.List extra = packet.getExtraPackets();
@@ -95,9 +101,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper start - handle oversized packets better
+ boolean connected = this.isConnected();
+ if (!connected && !preparing) {
-+ packet.onPacketDone();
+ return; // Do nothing
+ }
++ packet.onPacketDispatch(getPlayer());
+ if (connected && (InnerUtil.canSendImmediate(this, packet) || (
+ MCUtil.isMainThread() && packet.isReady() && this.packetQueue.isEmpty() &&
+ (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())
@@ -113,13 +119,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } else {
+ java.util.List packets = new java.util.ArrayList<>(1 + extraPackets.size());
+ packets.add(new NetworkManager.QueuedPacket(packet, null)); // delay the future listener until the end of the extra packets
-+
+
+ for (int i = 0, len = extraPackets.size(); i < len;) {
+ Packet extra = extraPackets.get(i);
+ boolean end = ++i == len;
+ packets.add(new NetworkManager.QueuedPacket(extra, end ? genericfuturelistener : null)); // append listener to the end
+ }
-
++
+ this.packetQueue.addAll(packets); // atomic
+ }
+ this.sendPacketQueue();
@@ -128,6 +134,30 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private void dispatchPacket(Packet> packet, @Nullable GenericFutureListener extends Future super Void>> genericFutureListener) { this.b(packet, genericFutureListener); } // Paper - OBFHELPER
@@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler> {
+ if (genericfuturelistener != null) {
+ channelfuture.addListener(genericfuturelistener);
+ }
++ // Paper start
++ if (packet.hasFinishListener()) {
++ channelfuture.addListener((ChannelFutureListener) channelFuture -> packet.onPacketDispatchFinish(getPlayer(), channelFuture));
++ }
++ // Paper end
+
+ channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
+ } else {
+@@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler> {
+ if (genericfuturelistener != null) {
+ channelfuture1.addListener(genericfuturelistener);
+ }
++ // Paper start
++ if (packet.hasFinishListener()) {
++ channelfuture1.addListener((ChannelFutureListener) channelFuture -> packet.onPacketDispatchFinish(getPlayer(), channelFuture));
++ }
++ // Paper end
+
+ channelfuture1.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
+ });
+@@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler> {
}
@@ -188,7 +218,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return this.socketAddress;
}
-+ public void clearPacketQueue() { QueuedPacket packet; while ((packet = packetQueue.poll()) != null) packet.getPacket().onPacketDone(); } // Paper
++ // Paper start
++ public void clearPacketQueue() {
++ EntityPlayer player = getPlayer();
++ packetQueue.forEach(queuedPacket -> {
++ Packet> packet = queuedPacket.getPacket();
++ if (packet.hasFinishListener()) {
++ packet.onPacketDispatchFinish(player, null);
++ }
++ });
++ packetQueue.clear();
++ } // Paper end
public void close(IChatBaseComponent ichatbasecomponent) {
// Spigot Start
this.preparing = false;
@@ -213,25 +253,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
void a(T t0);
// Paper start
-+ default void onPacketDone() {}
++
++ /**
++ * @param player Null if not at PLAY stage yet
++ */
++ default void onPacketDispatch(@javax.annotation.Nullable EntityPlayer player) {}
++
++ /**
++ * @param player Null if not at PLAY stage yet
++ * @param future Can be null if packet was cancelled
++ */
++ default void onPacketDispatchFinish(@javax.annotation.Nullable EntityPlayer player, @javax.annotation.Nullable io.netty.channel.ChannelFuture future) {}
++ default boolean hasFinishListener() { return false; }
+ default boolean isReady() { return true; }
+ default java.util.List getExtraPackets() { return null; }
default boolean packetTooLarge(NetworkManager manager) {
return false;
}
-diff --git a/src/main/java/net/minecraft/server/PacketEncoder.java b/src/main/java/net/minecraft/server/PacketEncoder.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/PacketEncoder.java
-+++ b/src/main/java/net/minecraft/server/PacketEncoder.java
-@@ -0,0 +0,0 @@ public class PacketEncoder extends MessageToByteEncoder> {
- } else {
- throw throwable;
- }
-- }
-+ } finally { try { packet.onPacketDone(); } catch (Exception e) { e.printStackTrace(); } ; } // Paper
-
- // Paper start
- int packetLength = bytebuf.readableBytes();
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/PlayerList.java
diff --git a/Spigot-Server-Patches/Optimize-UserCache-Thread-Safe.patch b/Spigot-Server-Patches/Optimize-UserCache-Thread-Safe.patch
index cb833b0668..0d51432747 100644
--- a/Spigot-Server-Patches/Optimize-UserCache-Thread-Safe.patch
+++ b/Spigot-Server-Patches/Optimize-UserCache-Thread-Safe.patch
@@ -14,7 +14,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrantcompile
@@ -0,0 +0,0 @@
+ 7.3.1
+ compile
+
++
++
++ co.aikar
++ cleaner
++ 1.0-SNAPSHOT
++
+
+
+ com.googlecode.json-simple
+@@ -0,0 +0,0 @@