9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-23 17:09:29 +00:00
Files
Leaf/patches/server/0054-Hearse-Fix-some-concurrent-problems.patch
2023-01-13 00:15:47 -05:00

1910 lines
80 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 9 Jan 2023 18:43:19 +0800
Subject: [PATCH] Hearse: Fix some concurrent problems
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/com/destroystokyo/paper/util/maplist/IBlockDataList.java b/src/main/java/com/destroystokyo/paper/util/maplist/IBlockDataList.java
index 277cfd9d1e8fff5d9b5e534b75c3c5162d58b0b7..07247f11b079bfb631010ff06fe353d3dcc0a0f6 100644
--- a/src/main/java/com/destroystokyo/paper/util/maplist/IBlockDataList.java
+++ b/src/main/java/com/destroystokyo/paper/util/maplist/IBlockDataList.java
@@ -53,7 +53,7 @@ public final class IBlockDataList {
return this.add(getLocationKey(x, y, z), data);
}
- public long add(final int location, final BlockState data) {
+ public synchronized long add(final int location, final BlockState data) {
final long curr = this.map.get((short)location);
if (curr == Long.MAX_VALUE) {
@@ -81,7 +81,7 @@ public final class IBlockDataList {
return this.remove(getLocationKey(x, y, z));
}
- public long remove(final int location) {
+ public synchronized long remove(final int location) {
final long ret = this.map.remove((short)location);
final int index = getIndexFromRaw(ret);
if (ret == Long.MAX_VALUE) {
@@ -101,11 +101,11 @@ public final class IBlockDataList {
return ret;
}
- public int size() {
+ public synchronized int size() {
return this.size;
}
- public long getRaw(final int index) {
+ public synchronized long getRaw(final int index) {
return this.byIndex[index];
}
@@ -117,12 +117,12 @@ public final class IBlockDataList {
return getBlockDataFromRaw(this.getRaw(index));
}
- public void clear() {
+ public synchronized void clear() {
this.size = 0;
this.map.clear();
}
- public LongIterator getRawIterator() {
+ public synchronized LongIterator getRawIterator() {
return this.map.values().iterator();
}
}
diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
index 62a74cbdb7f04b652dddac9e9c6191d5b86c3323..028b23f5c23bbfd83498c3e06a56079ceb0798ad 100644
--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
+++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
@@ -7,9 +7,9 @@ import io.papermc.paper.util.CoordinateUtils;
import io.papermc.paper.util.IntervalledCounter;
import io.papermc.paper.util.TickThread;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
-import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap;
-import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongSet;
+import it.unimi.dsi.fastutil.longs.LongSets;
+import it.unimi.dsi.fastutil.objects.*;
import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket;
import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket;
import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket;
@@ -24,9 +24,9 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.util.*;
+import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.StampedLock;
public final class PlayerChunkLoader {
@@ -37,11 +37,11 @@ public final class PlayerChunkLoader {
public static final int LOADED_TICKET_LEVEL = 33;
public static int getTickViewDistance(final Player player) {
- return getTickViewDistance(((CraftPlayer) player).getHandle());
+ return getTickViewDistance(((CraftPlayer)player).getHandle());
}
public static int getTickViewDistance(final ServerPlayer player) {
- final ServerLevel level = (ServerLevel) player.level;
+ final ServerLevel level = (ServerLevel)player.level;
final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player);
if (data == null) {
return level.chunkSource.chunkMap.playerChunkManager.getTargetTickViewDistance();
@@ -50,11 +50,11 @@ public final class PlayerChunkLoader {
}
public static int getLoadViewDistance(final Player player) {
- return getLoadViewDistance(((CraftPlayer) player).getHandle());
+ return getLoadViewDistance(((CraftPlayer)player).getHandle());
}
public static int getLoadViewDistance(final ServerPlayer player) {
- final ServerLevel level = (ServerLevel) player.level;
+ final ServerLevel level = (ServerLevel)player.level;
final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player);
if (data == null) {
return level.chunkSource.chunkMap.playerChunkManager.getLoadDistance();
@@ -63,11 +63,11 @@ public final class PlayerChunkLoader {
}
public static int getSendViewDistance(final Player player) {
- return getSendViewDistance(((CraftPlayer) player).getHandle());
+ return getSendViewDistance(((CraftPlayer)player).getHandle());
}
public static int getSendViewDistance(final ServerPlayer player) {
- final ServerLevel level = (ServerLevel) player.level;
+ final ServerLevel level = (ServerLevel)player.level;
final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player);
if (data == null) {
return level.chunkSource.chunkMap.playerChunkManager.getTargetSendDistance();
@@ -76,10 +76,10 @@ public final class PlayerChunkLoader {
}
protected final ChunkMap chunkMap;
- protected final Reference2ObjectLinkedOpenHashMap<ServerPlayer, PlayerLoaderData> playerMap = new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f);
- protected final ReferenceLinkedOpenHashSet<PlayerLoaderData> chunkSendQueue = new ReferenceLinkedOpenHashSet<>(512, 0.7f);
+ protected final Reference2ObjectMap<ServerPlayer, PlayerLoaderData> playerMap = Reference2ObjectMaps.synchronize(new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f));
+ protected final Deque<PlayerLoaderData> chunkSendQueue = new ConcurrentLinkedDeque<>();
- protected final TreeSet<PlayerLoaderData> chunkLoadQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
+ protected final NavigableSet<PlayerLoaderData> chunkLoadQueue = new ConcurrentSkipListSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
if (p1 == p2) {
return 0;
}
@@ -308,8 +308,8 @@ public final class PlayerChunkLoader {
});
}
- protected final LongOpenHashSet isTargetedForPlayerLoad = new LongOpenHashSet();
- protected final LongOpenHashSet chunkTicketTracker = new LongOpenHashSet();
+ protected final LongSet isTargetedForPlayerLoad = LongSets.synchronize(new LongOpenHashSet());
+ protected final LongSet chunkTicketTracker = LongSets.synchronize(new LongOpenHashSet());
public boolean isChunkNearPlayers(final int chunkX, final int chunkZ) {
final PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInSendRange = this.broadcastMap.getObjectsInRange(chunkX, chunkZ);
@@ -383,15 +383,15 @@ public final class PlayerChunkLoader {
protected int getMaxChunkLoads() {
double config = GlobalConfiguration.get().chunkLoading.playerMaxConcurrentLoads;
double max = GlobalConfiguration.get().chunkLoading.globalMaxConcurrentLoads;
- return (int) Math.ceil(Math.min(config * MinecraftServer.getServer().getPlayerCount(), max <= 1.0 ? Double.MAX_VALUE : max));
+ return (int)Math.ceil(Math.min(config * MinecraftServer.getServer().getPlayerCount(), max <= 1.0 ? Double.MAX_VALUE : max));
}
protected long getTargetSendPerPlayerAddend() {
- return GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate <= 1.0 ? 0L : (long) Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate);
+ return GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate);
}
protected long getMaxSendAddend() {
- return GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate <= 1.0 ? 0L : (long) Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate);
+ return GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate);
}
public void onChunkPlayerTickReady(final int chunkX, final int chunkZ) {
@@ -413,7 +413,7 @@ public final class PlayerChunkLoader {
if (!(raw instanceof ServerPlayer)) {
continue;
}
- this.onChunkSendReady((ServerPlayer) raw, chunkX, chunkZ);
+ this.onChunkSendReady((ServerPlayer)raw, chunkX, chunkZ);
}
}
@@ -481,11 +481,8 @@ public final class PlayerChunkLoader {
return;
}
loaderData.remove();
-
this.chunkLoadQueue.remove(loaderData);
-
this.chunkSendQueue.remove(loaderData);
-
this.chunkSendWaitQueue.remove(loaderData);
synchronized (this.sendingChunkCounts) {
final int count = this.sendingChunkCounts.removeInt(loaderData);
@@ -521,23 +518,21 @@ public final class PlayerChunkLoader {
protected static final AtomicInteger concurrentChunkSends = new AtomicInteger();
protected final Reference2IntOpenHashMap<PlayerLoaderData> sendingChunkCounts = new Reference2IntOpenHashMap<>();
private static long nextChunkSend;
-
private void trySendChunks() {
final long time = System.nanoTime();
if (time < nextChunkSend) {
return;
}
+ PlayerLoaderData data1;
// drain entries from wait queue
- while (!this.chunkSendWaitQueue.isEmpty()) {
- final PlayerLoaderData data = this.chunkSendWaitQueue.first();
-
- if (data.nextChunkSendTarget > time) {
+ while ((data1 = this.chunkSendWaitQueue.pollFirst())!=null) {
+ if (data1.nextChunkSendTarget > time) {
break;
}
this.chunkSendWaitQueue.pollFirst();
- this.chunkSendQueue.add(data);
+ this.chunkSendQueue.add(data1);
}
if (this.chunkSendQueue.isEmpty()) {
@@ -546,11 +541,9 @@ public final class PlayerChunkLoader {
final int maxSends = this.getMaxConcurrentChunkSends();
final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time;
- for (; ; ) {
- if (this.chunkSendQueue.isEmpty()) {
- break;
- }
-
+ final Deque<PlayerLoaderData> tempCopy = new ArrayDeque<>(this.chunkSendQueue);
+ PlayerLoaderData data;
+ while ((data = tempCopy.pollFirst())!=null) {
final int currSends = concurrentChunkSends.get();
if (currSends >= maxSends) {
break;
@@ -559,19 +552,12 @@ public final class PlayerChunkLoader {
if (!concurrentChunkSends.compareAndSet(currSends, currSends + 1)) {
continue;
}
-
// send chunk
-
- PlayerLoaderData data = this.chunkSendQueue.removeFirst();
-
+ this.chunkSendQueue.remove(data);
final ChunkPriorityHolder queuedSend = data.sendQueue.pollFirst();
if (queuedSend == null) {
concurrentChunkSends.getAndDecrement(); // we never sent, so decrease
// stop iterating over players who have nothing to send
- if (this.chunkSendQueue.isEmpty()) {
- // nothing left
- break;
- }
continue;
}
@@ -580,22 +566,24 @@ public final class PlayerChunkLoader {
}
data.nextChunkSendTarget = nextPlayerDeadline;
+ this.chunkSendWaitQueue.add(data);
synchronized (this.sendingChunkCounts) {
this.sendingChunkCounts.addTo(data, 1);
}
+ final PlayerLoaderData finalData = data;
data.sendChunk(queuedSend.chunkX, queuedSend.chunkZ, () -> {
synchronized (this.sendingChunkCounts) {
- final int count = this.sendingChunkCounts.getInt(data);
+ final int count = this.sendingChunkCounts.getInt(finalData);
if (count == 0) {
// disconnected, so we don't need to decrement: it will be decremented for us
return;
}
if (count == 1) {
- this.sendingChunkCounts.removeInt(data);
+ this.sendingChunkCounts.removeInt(finalData);
} else {
- this.sendingChunkCounts.put(data, count - 1);
+ this.sendingChunkCounts.put(finalData, count - 1);
}
}
@@ -611,10 +599,9 @@ public final class PlayerChunkLoader {
protected int concurrentChunkLoads;
// this interval prevents bursting a lot of chunk loads
- protected static final IntervalledCounter TICKET_ADDITION_COUNTER_SHORT = new IntervalledCounter((long) (1.0e6 * 50.0)); // 50ms
+ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_SHORT = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms
// this interval ensures the rate is kept between ticks correctly
- protected static final IntervalledCounter TICKET_ADDITION_COUNTER_LONG = new IntervalledCounter((long) (1.0e6 * 1000.0)); // 1000ms
-
+ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_LONG = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms
private void tryLoadChunks() {
if (this.chunkLoadQueue.isEmpty()) {
return;
@@ -623,16 +610,12 @@ public final class PlayerChunkLoader {
final int maxLoads = this.getMaxChunkLoads();
final long time = System.nanoTime();
boolean updatedCounters = false;
- for (; ; ) {
- PlayerLoaderData data = this.chunkLoadQueue.pollFirst();
-
+ PlayerLoaderData data;
+ while ((data = this.chunkLoadQueue.pollFirst())!=null) {
data.lastChunkLoad = time;
final ChunkPriorityHolder queuedLoad = data.loadQueue.peekFirst();
if (queuedLoad == null) {
- if (this.chunkLoadQueue.isEmpty()) {
- break;
- }
continue;
}
@@ -648,6 +631,7 @@ public final class PlayerChunkLoader {
// already loaded!
data.loadQueue.pollFirst(); // already loaded so we just skip
this.chunkLoadQueue.add(data);
+
// ensure the chunk is queued to send
this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ);
continue;
@@ -768,7 +752,7 @@ public final class PlayerChunkLoader {
protected static final double PRIORITISED_DISTANCE = 12.0 * 16.0;
// Player max sprint speed is approximately 8m/s
- protected static final double LOOK_PRIORITY_SPEED_THRESHOLD = (10.0 / 20.0) * (10.0 / 20.0);
+ protected static final double LOOK_PRIORITY_SPEED_THRESHOLD = (10.0/20.0) * (10.0/20.0);
protected static final double LOOK_PRIORITY_YAW_DELTA_RECALC_THRESHOLD = 3.0f;
protected double lastLocX = Double.NEGATIVE_INFINITY;
@@ -790,11 +774,11 @@ public final class PlayerChunkLoader {
// warning: modifications of this field must be aware that the loadQueue inside PlayerChunkLoader uses this field
// in a comparator!
- protected final ArrayDeque<ChunkPriorityHolder> loadQueue = new ArrayDeque<>();
- protected final LongOpenHashSet sentChunks = new LongOpenHashSet();
- protected final LongOpenHashSet chunksToBeSent = new LongOpenHashSet();
+ protected final Deque<ChunkPriorityHolder> loadQueue = new ConcurrentLinkedDeque<>();
+ protected final LongSet sentChunks = LongSets.synchronize(new LongOpenHashSet());
+ protected final LongSet chunksToBeSent = LongSets.synchronize(new LongOpenHashSet());
- protected final TreeSet<ChunkPriorityHolder> sendQueue = new TreeSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> {
+ protected final NavigableSet<ChunkPriorityHolder> sendQueue = new ConcurrentSkipListSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> {
final int distanceCompare = Integer.compare(p1.manhattanDistanceToPlayer, p2.manhattanDistanceToPlayer);
if (distanceCompare != 0) {
return distanceCompare;
@@ -815,9 +799,9 @@ public final class PlayerChunkLoader {
protected long nextChunkSendTarget;
// this interval prevents bursting a lot of chunk loads
- protected final IntervalledCounter ticketAdditionCounterShort = new IntervalledCounter((long) (1.0e6 * 50.0)); // 50ms
+ protected final IntervalledCounter ticketAdditionCounterShort = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms
// this ensures the rate is kept between ticks correctly
- protected final IntervalledCounter ticketAdditionCounterLong = new IntervalledCounter((long) (1.0e6 * 1000.0)); // 1000ms
+ protected final IntervalledCounter ticketAdditionCounterLong = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms
public long lastChunkLoad;
@@ -914,14 +898,14 @@ public final class PlayerChunkLoader {
// b = ((p3z - p1z)(targetX - p3x) + (p1x - p3x)(targetZ - p3z)) / d
// c = 1.0 - a - b
- final double d = (p2z - p3z) * (p1x - p3x) + (p3x - p2x) * (p1z - p3z);
- final double a = ((p2z - p3z) * (targetX - p3x) + (p3x - p2x) * (targetZ - p3z)) / d;
+ final double d = (p2z - p3z)*(p1x - p3x) + (p3x - p2x)*(p1z - p3z);
+ final double a = ((p2z - p3z)*(targetX - p3x) + (p3x - p2x)*(targetZ - p3z)) / d;
if (a < 0.0 || a > 1.0) {
return false;
}
- final double b = ((p3z - p1z) * (targetX - p3x) + (p1x - p3x) * (targetZ - p3z)) / d;
+ final double b = ((p3z - p1z)*(targetX - p3x) + (p1x - p3x)*(targetZ - p3z)) / d;
if (b < 0.0 || b > 1.0) {
return false;
}
@@ -1024,15 +1008,15 @@ public final class PlayerChunkLoader {
final double p1z = posZ;
// to the left of the looking direction
- final double p2x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw + (double) (FOV / 2.0))) // calculate rotated vector
+ final double p2x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector
+ p1x; // offset vector
- final double p2z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw + (double) (FOV / 2.0))) // calculate rotated vector
+ final double p2z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector
+ p1z; // offset vector
// to the right of the looking direction
- final double p3x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw - (double) (FOV / 2.0))) // calculate rotated vector
+ final double p3x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector
+ p1x; // offset vector
- final double p3z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw - (double) (FOV / 2.0))) // calculate rotated vector
+ final double p3z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector
+ p1z; // offset vector
// now that we have all of our points, we can recalculate the load queue
@@ -1070,7 +1054,7 @@ public final class PlayerChunkLoader {
p1x, p1z, p2x, p2z, p3x, p3z,
// center of chunk
- (double) ((chunkX << 4) | 8), (double) ((chunkZ << 4) | 8)
+ (double)((chunkX << 4) | 8), (double)((chunkZ << 4) | 8)
);
final int manhattanDistance = Math.abs(dx) + Math.abs(dz);
@@ -1085,9 +1069,9 @@ public final class PlayerChunkLoader {
if (prioritised) {
// we don't prioritise these chunks above others because we also want to make sure some chunks
// will be loaded if the player changes direction
- priority = (double) manhattanDistance / 6.0;
+ priority = (double)manhattanDistance / 6.0;
} else {
- priority = (double) manhattanDistance;
+ priority = (double)manhattanDistance;
}
}
diff --git a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
index 26e1b4060f2a93cb659170f83e6ce64086e0eb0c..d6951b05128fea7eb5f1b40837cea77e0c209165 100644
--- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
+++ b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
@@ -423,8 +423,10 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ);
final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
- if (!old.removeEntity(entity, entity.sectionY)) {
- LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section");
+ if (old!=null){
+ if (!old.removeEntity(entity, entity.sectionY)) {
+ LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section");
+ }
}
if (!slices.addEntity(entity, newSectionY)) {
diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java
index 470402573bc31106d5a63e415b958fb7f9c36aa9..762f09c8f374fbccc9f5be985401ad334e1655a0 100644
--- a/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java
+++ b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java
@@ -94,24 +94,24 @@ public final class Delayed26WayDistancePropagator3D {
protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[level];
- queue.queuedCoordinates.enqueue(coordinate);
- queue.queuedLevels.enqueue(level);
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
this.levelIncreaseWorkQueueBitset |= (1L << level);
}
protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[index];
- queue.queuedCoordinates.enqueue(coordinate);
- queue.queuedLevels.enqueue(level);
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
this.levelIncreaseWorkQueueBitset |= (1L << index);
}
protected final void addToRemoveWorkQueue(final long coordinate, final byte level) {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[level];
- queue.queuedCoordinates.enqueue(coordinate);
- queue.queuedLevels.enqueue(level);
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
this.levelRemoveWorkQueueBitset |= (1L << level);
}
@@ -164,8 +164,8 @@ public final class Delayed26WayDistancePropagator3D {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex];
while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirstLong();
- byte level = queue.queuedLevels.removeFirstByte();
+ final long coordinate = queue.queuedCoordinates.removeFirst();
+ byte level = queue.queuedLevels.removeFirst();
final boolean neighbourCheck = level < 0;
@@ -233,8 +233,8 @@ public final class Delayed26WayDistancePropagator3D {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[queueIndex];
while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirstLong();
- final byte level = queue.queuedLevels.removeFirstByte();
+ final long coordinate = queue.queuedCoordinates.removeFirst();
+ final byte level = queue.queuedLevels.removeFirst();
final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level);
if (currentLevel == 0) {
diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java
index 808d1449ac44ae86a650932365081fbaf178d141..8c5a51b5992eccf3627f326e164288b5f6bbcff6 100644
--- a/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java
+++ b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java
@@ -1,12 +1,13 @@
package io.papermc.paper.util.misc;
+import io.papermc.paper.util.MCUtil;
import it.unimi.dsi.fastutil.HashCommon;
-import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue;
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
-import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
-import io.papermc.paper.util.MCUtil;
+
+import java.util.Deque;
+import java.util.concurrent.ConcurrentLinkedDeque;
public final class Delayed8WayDistancePropagator2D {
@@ -356,24 +357,24 @@ public final class Delayed8WayDistancePropagator2D {
protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) {
final WorkQueue queue = this.levelIncreaseWorkQueues[level];
- queue.queuedCoordinates.enqueue(coordinate);
- queue.queuedLevels.enqueue(level);
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
this.levelIncreaseWorkQueueBitset |= (1L << level);
}
protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) {
final WorkQueue queue = this.levelIncreaseWorkQueues[index];
- queue.queuedCoordinates.enqueue(coordinate);
- queue.queuedLevels.enqueue(level);
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
this.levelIncreaseWorkQueueBitset |= (1L << index);
}
protected final void addToRemoveWorkQueue(final long coordinate, final byte level) {
final WorkQueue queue = this.levelRemoveWorkQueues[level];
- queue.queuedCoordinates.enqueue(coordinate);
- queue.queuedLevels.enqueue(level);
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
this.levelRemoveWorkQueueBitset |= (1L << level);
}
@@ -426,8 +427,8 @@ public final class Delayed8WayDistancePropagator2D {
final WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex];
while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirstLong();
- byte level = queue.queuedLevels.removeFirstByte();
+ final long coordinate = queue.queuedCoordinates.removeFirst();
+ byte level = queue.queuedLevels.removeFirst();
final boolean neighbourCheck = level < 0;
@@ -492,8 +493,8 @@ public final class Delayed8WayDistancePropagator2D {
final WorkQueue queue = this.levelRemoveWorkQueues[queueIndex];
while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirstLong();
- final byte level = queue.queuedLevels.removeFirstByte();
+ final long coordinate = queue.queuedCoordinates.removeFirst();
+ final byte level = queue.queuedLevels.removeFirst();
final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level);
if (currentLevel == 0) {
@@ -678,41 +679,8 @@ public final class Delayed8WayDistancePropagator2D {
}
protected static final class WorkQueue {
-
- public final NoResizeLongArrayFIFODeque queuedCoordinates = new NoResizeLongArrayFIFODeque();
- public final NoResizeByteArrayFIFODeque queuedLevels = new NoResizeByteArrayFIFODeque();
-
- }
-
- protected static final class NoResizeLongArrayFIFODeque extends LongArrayFIFOQueue {
-
- /**
- * Assumes non-empty. If empty, undefined behaviour.
- */
- public long removeFirstLong() {
- // copied from superclass
- long t = this.array[this.start];
- if (++this.start == this.length) {
- this.start = 0;
- }
-
- return t;
- }
+ public final Deque<Long> queuedCoordinates = new ConcurrentLinkedDeque<>();
+ public final Deque<Byte> queuedLevels = new ConcurrentLinkedDeque<>();
}
- protected static final class NoResizeByteArrayFIFODeque extends ByteArrayFIFOQueue {
-
- /**
- * Assumes non-empty. If empty, undefined behaviour.
- */
- public byte removeFirstByte() {
- // copied from superclass
- byte t = this.array[this.start];
- if (++this.start == this.length) {
- this.start = 0;
- }
-
- return t;
- }
- }
}
diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java
new file mode 100644
index 0000000000000000000000000000000000000000..22b9d217dc06caaf8fbec21f0e31aa1cd13144ee
--- /dev/null
+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java
@@ -0,0 +1,945 @@
+package net.himeki.mcmtfabric.parallelised;
+
+/*
+ * From: http://www.java2s.com/Code/Java/Collections-Data-Structure/ConcurrentDoublyLinkedList.htm
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
+ *
+ * Modified to actually implement List<E>
+ */
+
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.lang3.NotImplementedException;
+
+/**
+ * A concurrent linked-list implementation of a {@link Deque} (double-ended
+ * queue). Concurrent insertion, removal, and access operations execute safely
+ * across multiple threads. Iterators are <i>weakly consistent</i>, returning
+ * elements reflecting the state of the deque at some point at or since the
+ * creation of the iterator. They do <em>not</em> throw
+ * {@link ConcurrentModificationException}, and may proceed concurrently with
+ * other operations.
+ *
+ * <p>
+ * This class and its iterators implement all of the <em>optional</em> methods
+ * of the {@link Collection} and {@link Iterator} interfaces. Like most other
+ * concurrent collection implementations, this class does not permit the use of
+ * <tt>null</tt> elements. because some null arguments and return values cannot
+ * be reliably distinguished from the absence of elements. Arbitrarily, the
+ * {@link Collection#remove} method is mapped to <tt>removeFirstOccurrence</tt>,
+ * and {@link Collection#add} is mapped to <tt>addLast</tt>.
+ *
+ * <p>
+ * Beware that, unlike in most collections, the <tt>size</tt> method is
+ * <em>NOT</em> a constant-time operation. Because of the asynchronous nature of
+ * these deques, determining the current number of elements requires a traversal
+ * of the elements.
+ *
+ * <p>
+ * This class is <tt>Serializable</tt>, but relies on default serialization
+ * mechanisms. Usually, it is a better idea for any serializable class using a
+ * <tt>ConcurrentLinkedDeque</tt> to instead serialize a snapshot of the
+ * elements obtained by method <tt>toArray</tt>.
+ *
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
+ */
+
+public class ConcurrentDoublyLinkedList<E> extends AbstractCollection<E> implements List<E>, java.io.Serializable {
+
+ /*
+ * This is an adaptation of an algorithm described in Paul Martin's "A Practical
+ * Lock-Free Doubly-Linked List". Sun Labs Tech report. The basic idea is to
+ * primarily rely on next-pointers to ensure consistency. Prev-pointers are in
+ * part optimistic, reconstructed using forward pointers as needed. The main
+ * forward list uses a variant of HM-list algorithm similar to the one used in
+ * ConcurrentSkipListMap class, but a little simpler. It is also basically
+ * similar to the approach in Edya Ladan-Mozes and Nir Shavit "An Optimistic
+ * Approach to Lock-Free FIFO Queues" in DISC04.
+ *
+ * Quoting a summary in Paul Martin's tech report:
+ *
+ * All cleanups work to maintain these invariants: (1) forward pointers are the
+ * ground truth. (2) forward pointers to dead nodes can be improved by swinging
+ * them further forward around the dead node. (2.1) forward pointers are still
+ * correct when pointing to dead nodes, and forward pointers from dead nodes are
+ * left as they were when the node was deleted. (2.2) multiple dead nodes may
+ * point forward to the same node. (3) backward pointers were correct when they
+ * were installed (3.1) backward pointers are correct when pointing to any node
+ * which points forward to them, but since more than one forward pointer may
+ * point to them, the live one is best. (4) backward pointers that are out of
+ * date due to deletion point to a deleted node, and need to point further back
+ * until they point to the live node that points to their source. (5) backward
+ * pointers that are out of date due to insertion point too far backwards, so
+ * shortening their scope (by searching forward) fixes them. (6) backward
+ * pointers from a dead node cannot be "improved" since there may be no live
+ * node pointing forward to their origin. (However, it does no harm to try to
+ * improve them while racing with a deletion.)
+ *
+ *
+ * Notation guide for local variables n, b, f : a node, its predecessor, and
+ * successor s : some other successor
+ */
+
+ // Minor convenience utilities
+
+ /**
+ * Returns true if given reference is non-null and isn't a header, trailer, or
+ * marker.
+ *
+ * @param n (possibly null) node
+ * @return true if n exists as a user node
+ */
+ private static boolean usable(Node<?> n) {
+ return n != null && !n.isSpecial();
+ }
+
+ /**
+ * Throws NullPointerException if argument is null
+ *
+ * @param v the element
+ */
+ private static void checkNullArg(Object v) {
+ if (v == null)
+ throw new NullPointerException();
+ }
+
+ /**
+ * Returns element unless it is null, in which case throws
+ * NoSuchElementException.
+ *
+ * @param v the element
+ * @return the element
+ */
+ private E screenNullResult(E v) {
+ if (v == null)
+ throw new NoSuchElementException();
+ return v;
+ }
+
+ /**
+ * Creates an array list and fills it with elements of this list. Used by
+ * toArray.
+ *
+ * @return the arrayList
+ */
+ private ArrayList<E> toArrayList() {
+ ArrayList<E> c = new ArrayList<E>();
+ for (Node<E> n = header.forward(); n != null; n = n.forward())
+ c.add(n.element);
+ return c;
+ }
+
+ // Fields and constructors
+
+ private static final long serialVersionUID = 876323262645176354L;
+
+ /**
+ * List header. First usable node is at header.forward().
+ */
+ private final Node<E> header;
+
+ /**
+ * List trailer. Last usable node is at trailer.back().
+ */
+ private final Node<E> trailer;
+
+ /**
+ * Constructs an empty deque.
+ */
+ public ConcurrentDoublyLinkedList() {
+ Node<E> h = new Node<E>(null, null, null);
+ Node<E> t = new Node<E>(null, null, h);
+ h.setNext(t);
+ header = h;
+ trailer = t;
+ }
+
+ /**
+ * Constructs a deque containing the elements of the specified collection, in
+ * the order they are returned by the collection's iterator.
+ *
+ * @param c the collection whose elements are to be placed into this deque.
+ * @throws NullPointerException if <tt>c</tt> or any element within it is
+ * <tt>null</tt>
+ */
+ public ConcurrentDoublyLinkedList(Collection<? extends E> c) {
+ this();
+ addAll(c);
+ }
+
+ /**
+ * Prepends the given element at the beginning of this deque.
+ *
+ * @param o the element to be inserted at the beginning of this deque.
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public void addFirst(E o) {
+ checkNullArg(o);
+ while (header.append(o) == null)
+ ;
+ }
+
+ /**
+ * Appends the given element to the end of this deque. This is identical in
+ * function to the <tt>add</tt> method.
+ *
+ * @param o the element to be inserted at the end of this deque.
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public void addLast(E o) {
+ checkNullArg(o);
+ while (trailer.prepend(o) == null)
+ ;
+ }
+
+ /**
+ * Prepends the given element at the beginning of this deque.
+ *
+ * @param o the element to be inserted at the beginning of this deque.
+ * @return <tt>true</tt> always
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public boolean offerFirst(E o) {
+ addFirst(o);
+ return true;
+ }
+
+ /**
+ * Appends the given element to the end of this deque. (Identical in function to
+ * the <tt>add</tt> method; included only for consistency.)
+ *
+ * @param o the element to be inserted at the end of this deque.
+ * @return <tt>true</tt> always
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public boolean offerLast(E o) {
+ addLast(o);
+ return true;
+ }
+
+ /**
+ * Retrieves, but does not remove, the first element of this deque, or returns
+ * null if this deque is empty.
+ *
+ * @return the first element of this queue, or <tt>null</tt> if empty.
+ */
+ public E peekFirst() {
+ Node<E> n = header.successor();
+ return (n == null) ? null : n.element;
+ }
+
+ /**
+ * Retrieves, but does not remove, the last element of this deque, or returns
+ * null if this deque is empty.
+ *
+ * @return the last element of this deque, or <tt>null</tt> if empty.
+ */
+ public E peekLast() {
+ Node<E> n = trailer.predecessor();
+ return (n == null) ? null : n.element;
+ }
+
+ /**
+ * Returns the first element in this deque.
+ *
+ * @return the first element in this deque.
+ * @throws NoSuchElementException if this deque is empty.
+ */
+ public E getFirst() {
+ return screenNullResult(peekFirst());
+ }
+
+ /**
+ * Returns the last element in this deque.
+ *
+ * @return the last element in this deque.
+ * @throws NoSuchElementException if this deque is empty.
+ */
+ public E getLast() {
+ return screenNullResult(peekLast());
+ }
+
+ /**
+ * Retrieves and removes the first element of this deque, or returns null if
+ * this deque is empty.
+ *
+ * @return the first element of this deque, or <tt>null</tt> if empty.
+ */
+ public E pollFirst() {
+ for (;;) {
+ Node<E> n = header.successor();
+ if (!usable(n))
+ return null;
+ if (n.delete())
+ return n.element;
+ }
+ }
+
+ /**
+ * Retrieves and removes the last element of this deque, or returns null if this
+ * deque is empty.
+ *
+ * @return the last element of this deque, or <tt>null</tt> if empty.
+ */
+ public E pollLast() {
+ for (;;) {
+ Node<E> n = trailer.predecessor();
+ if (!usable(n))
+ return null;
+ if (n.delete())
+ return n.element;
+ }
+ }
+
+ /**
+ * Removes and returns the first element from this deque.
+ *
+ * @return the first element from this deque.
+ * @throws NoSuchElementException if this deque is empty.
+ */
+ public E removeFirst() {
+ return screenNullResult(pollFirst());
+ }
+
+ /**
+ * Removes and returns the last element from this deque.
+ *
+ * @return the last element from this deque.
+ * @throws NoSuchElementException if this deque is empty.
+ */
+ public E removeLast() {
+ return screenNullResult(pollLast());
+ }
+
+ // *** Queue and stack methods ***
+ public boolean offer(E e) {
+ return offerLast(e);
+ }
+
+ public boolean add(E e) {
+ return offerLast(e);
+ }
+
+ public E poll() {
+ return pollFirst();
+ }
+
+ public E remove() {
+ return removeFirst();
+ }
+
+ public E peek() {
+ return peekFirst();
+ }
+
+ public E element() {
+ return getFirst();
+ }
+
+ public void push(E e) {
+ addFirst(e);
+ }
+
+ public E pop() {
+ return removeFirst();
+ }
+
+ /**
+ * Removes the first element <tt>e</tt> such that <tt>o.equals(e)</tt>, if such
+ * an element exists in this deque. If the deque does not contain the element,
+ * it is unchanged.
+ *
+ * @param o element to be removed from this deque, if present.
+ * @return <tt>true</tt> if the deque contained the specified element.
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public boolean removeFirstOccurrence(Object o) {
+ checkNullArg(o);
+ for (;;) {
+ Node<E> n = header.forward();
+ for (;;) {
+ if (n == null)
+ return false;
+ if (o.equals(n.element)) {
+ if (n.delete())
+ return true;
+ else
+ break; // restart if interference
+ }
+ n = n.forward();
+ }
+ }
+ }
+
+ /**
+ * Removes the last element <tt>e</tt> such that <tt>o.equals(e)</tt>, if such
+ * an element exists in this deque. If the deque does not contain the element,
+ * it is unchanged.
+ *
+ * @param o element to be removed from this deque, if present.
+ * @return <tt>true</tt> if the deque contained the specified element.
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public boolean removeLastOccurrence(Object o) {
+ checkNullArg(o);
+ for (;;) {
+ Node<E> s = trailer;
+ for (;;) {
+ Node<E> n = s.back();
+ if (s.isDeleted() || (n != null && n.successor() != s))
+ break; // restart if pred link is suspect.
+ if (n == null)
+ return false;
+ if (o.equals(n.element)) {
+ if (n.delete())
+ return true;
+ else
+ break; // restart if interference
+ }
+ s = n;
+ }
+ }
+ }
+
+ /**
+ * Returns <tt>true</tt> if this deque contains at least one element <tt>e</tt>
+ * such that <tt>o.equals(e)</tt>.
+ *
+ * @param o element whose presence in this deque is to be tested.
+ * @return <tt>true</tt> if this deque contains the specified element.
+ */
+ public boolean contains(Object o) {
+ if (o == null)
+ return false;
+ for (Node<E> n = header.forward(); n != null; n = n.forward())
+ if (o.equals(n.element))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this collection contains no elements.
+ * <p>
+ *
+ * @return <tt>true</tt> if this collection contains no elements.
+ */
+ public boolean isEmpty() {
+ return !usable(header.successor());
+ }
+
+ /**
+ * Returns the number of elements in this deque. If this deque contains more
+ * than <tt>Integer.MAX_VALUE</tt> elements, it returns
+ * <tt>Integer.MAX_VALUE</tt>.
+ *
+ * <p>
+ * Beware that, unlike in most collections, this method is <em>NOT</em> a
+ * constant-time operation. Because of the asynchronous nature of these deques,
+ * determining the current number of elements requires traversing them all to
+ * count them. Additionally, it is possible for the size to change during
+ * execution of this method, in which case the returned result will be
+ * inaccurate. Thus, this method is typically not very useful in concurrent
+ * applications.
+ *
+ * @return the number of elements in this deque.
+ */
+ public int size() {
+ long count = 0;
+ for (Node<E> n = header.forward(); n != null; n = n.forward())
+ ++count;
+ return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count;
+ }
+
+ /**
+ * Removes the first element <tt>e</tt> such that <tt>o.equals(e)</tt>, if such
+ * an element exists in this deque. If the deque does not contain the element,
+ * it is unchanged.
+ *
+ * @param o element to be removed from this deque, if present.
+ * @return <tt>true</tt> if the deque contained the specified element.
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public boolean remove(Object o) {
+ return removeFirstOccurrence(o);
+ }
+
+ /**
+ * Appends all of the elements in the specified collection to the end of this
+ * deque, in the order that they are returned by the specified collection's
+ * iterator. The behavior of this operation is undefined if the specified
+ * collection is modified while the operation is in progress. (This implies that
+ * the behavior of this call is undefined if the specified Collection is this
+ * deque, and this deque is nonempty.)
+ *
+ * @param c the elements to be inserted into this deque.
+ * @return <tt>true</tt> if this deque changed as a result of the call.
+ * @throws NullPointerException if <tt>c</tt> or any element within it is
+ * <tt>null</tt>
+ */
+ public boolean addAll(Collection<? extends E> c) {
+ Iterator<? extends E> it = c.iterator();
+ if (!it.hasNext())
+ return false;
+ do {
+ addLast(it.next());
+ } while (it.hasNext());
+ return true;
+ }
+
+ /**
+ * Removes all of the elements from this deque.
+ */
+ public void clear() {
+ while (pollFirst() != null)
+ ;
+ }
+
+ /**
+ * Returns an array containing all of the elements in this deque in the correct
+ * order.
+ *
+ * @return an array containing all of the elements in this deque in the correct
+ * order.
+ */
+ public Object[] toArray() {
+ return toArrayList().toArray();
+ }
+
+ /**
+ * Returns an array containing all of the elements in this deque in the correct
+ * order; the runtime type of the returned array is that of the specified array.
+ * If the deque fits in the specified array, it is returned therein. Otherwise,
+ * a new array is allocated with the runtime type of the specified array and the
+ * size of this deque.
+ * <p>
+ *
+ * If the deque fits in the specified array with room to spare (i.e., the array
+ * has more elements than the deque), the element in the array immediately
+ * following the end of the collection is set to null. This is useful in
+ * determining the length of the deque <i>only</i> if the caller knows that the
+ * deque does not contain any null elements.
+ *
+ * @param a the array into which the elements of the deque are to be stored, if
+ * it is big enough; otherwise, a new array of the same runtime type is
+ * allocated for this purpose.
+ * @return an array containing the elements of the deque.
+ * @throws ArrayStoreException if the runtime type of a is not a supertype of
+ * the runtime type of every element in this deque.
+ * @throws NullPointerException if the specified array is null.
+ */
+ public <T> T[] toArray(T[] a) {
+ return toArrayList().toArray(a);
+ }
+
+ /**
+ * Returns a weakly consistent iterator over the elements in this deque, in
+ * first-to-last order. The <tt>next</tt> method returns elements reflecting the
+ * state of the deque at some point at or since the creation of the iterator.
+ * The method does <em>not</em> throw {@link ConcurrentModificationException},
+ * and may proceed concurrently with other operations.
+ *
+ * @return an iterator over the elements in this deque
+ */
+ public Iterator<E> iterator() {
+ return new CLDIterator();
+ }
+
+ final class CLDIterator implements Iterator<E> {
+ Node<E> last;
+
+ Node<E> next = header.forward();
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public E next() {
+ Node<E> l = last = next;
+ if (l == null)
+ throw new NoSuchElementException();
+ next = next.forward();
+ return l.element;
+ }
+
+ public void remove() {
+ Node<E> l = last;
+ if (l == null)
+ throw new IllegalStateException();
+ while (!l.delete() && !l.isDeleted())
+ ;
+ }
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends E> c) {
+ throw new NotImplementedException("TODO");
+ }
+
+ @Override
+ public E get(int index) {
+ Node<E> current = header.successor();
+ if (current == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ for (; index > 0; index --) {
+ current = current.successor();
+ if (current == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+ return current.element;
+ }
+
+ @Override
+ public E set(int index, E element) {
+ throw new NotImplementedException("INVALID");
+ }
+
+ @Override
+ public void add(int index, E element) {
+ throw new NotImplementedException("INVALID");
+ }
+
+ @Override
+ public E remove(int index) {
+ throw new NotImplementedException("INVALID");
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ throw new NotImplementedException("INVALID");
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ throw new NotImplementedException("INVALID");
+ }
+
+ @Override
+ public ListIterator<E> listIterator() {
+ throw new NotImplementedException("INVALID");
+ }
+
+ @Override
+ public ListIterator<E> listIterator(int index) {
+ throw new NotImplementedException("INVALID");
+ }
+
+ @Override
+ public List<E> subList(int fromIndex, int toIndex) {
+ throw new NotImplementedException("INVALID");
+ }
+
+}
+
+/**
+ * Linked Nodes. As a minor efficiency hack, this class opportunistically
+ * inherits from AtomicReference, with the atomic ref used as the "next" link.
+ *
+ * Nodes are in doubly-linked lists. There are three kinds of special nodes,
+ * distinguished by: * The list header has a null prev link * The list trailer
+ * has a null next link * A deletion marker has a prev link pointing to itself.
+ * All three kinds of special nodes have null element fields.
+ *
+ * Regular nodes have non-null element, next, and prev fields. To avoid visible
+ * inconsistencies when deletions overlap element replacement, replacements are
+ * done by replacing the node, not just setting the element.
+ *
+ * Nodes can be traversed by read-only ConcurrentLinkedDeque class operations
+ * just by following raw next pointers, so long as they ignore any special nodes
+ * seen along the way. (This is automated in method forward.) However, traversal
+ * using prev pointers is not guaranteed to see all live nodes since a prev
+ * pointer of a deleted node can become unrecoverably stale.
+ */
+
+class Node<E> extends AtomicReference<Node<E>> {
+
+ private static final long serialVersionUID = 6640557564507962862L;
+
+ private volatile Node<E> prev;
+
+ final E element;
+
+ /** Creates a node with given contents */
+ Node(E element, Node<E> next, Node<E> prev) {
+ super(next);
+ this.prev = prev;
+ this.element = element;
+ }
+
+ /** Creates a marker node with given successor */
+ Node(Node<E> next) {
+ super(next);
+ this.prev = this;
+ this.element = null;
+ }
+
+ /**
+ * Gets next link (which is actually the value held as atomic reference).
+ */
+ private Node<E> getNext() {
+ return get();
+ }
+
+ /**
+ * Sets next link
+ *
+ * @param n the next node
+ */
+ void setNext(Node<E> n) {
+ set(n);
+ }
+
+ /**
+ * compareAndSet next link
+ */
+ private boolean casNext(Node<E> cmp, Node<E> val) {
+ return compareAndSet(cmp, val);
+ }
+
+ /**
+ * Gets prev link
+ */
+ private Node<E> getPrev() {
+ return prev;
+ }
+
+ /**
+ * Sets prev link
+ *
+ * @param b the previous node
+ */
+ void setPrev(Node<E> b) {
+ prev = b;
+ }
+
+ /**
+ * Returns true if this is a header, trailer, or marker node
+ */
+ boolean isSpecial() {
+ return element == null;
+ }
+
+ /**
+ * Returns true if this is a trailer node
+ */
+ boolean isTrailer() {
+ return getNext() == null;
+ }
+
+ /**
+ * Returns true if this is a header node
+ */
+ boolean isHeader() {
+ return getPrev() == null;
+ }
+
+ /**
+ * Returns true if this is a marker node
+ */
+ boolean isMarker() {
+ return getPrev() == this;
+ }
+
+ /**
+ * Returns true if this node is followed by a marker, meaning that it is
+ * deleted.
+ *
+ * @return true if this node is deleted
+ */
+ boolean isDeleted() {
+ Node<E> f = getNext();
+ return f != null && f.isMarker();
+ }
+
+ /**
+ * Returns next node, ignoring deletion marker
+ */
+ private Node<E> nextNonmarker() {
+ Node<E> f = getNext();
+ return (f == null || !f.isMarker()) ? f : f.getNext();
+ }
+
+ /**
+ * Returns the next non-deleted node, swinging next pointer around any
+ * encountered deleted nodes, and also patching up successor''s prev link to
+ * point back to this. Returns null if this node is trailer so has no successor.
+ *
+ * @return successor, or null if no such
+ */
+ Node<E> successor() {
+ Node<E> f = nextNonmarker();
+ for (;;) {
+ if (f == null)
+ return null;
+ if (!f.isDeleted()) {
+ if (f.getPrev() != this && !isDeleted())
+ f.setPrev(this); // relink f's prev
+ return f;
+ }
+ Node<E> s = f.nextNonmarker();
+ if (f == getNext())
+ casNext(f, s); // unlink f
+ f = s;
+ }
+ }
+
+ /**
+ * Returns the apparent predecessor of target by searching forward for it
+ * starting at this node, patching up pointers while traversing. Used by
+ * predecessor().
+ *
+ * @return target's predecessor, or null if not found
+ */
+ private Node<E> findPredecessorOf(Node<E> target) {
+ Node<E> n = this;
+ for (;;) {
+ Node<E> f = n.successor();
+ if (f == target)
+ return n;
+ if (f == null)
+ return null;
+ n = f;
+ }
+ }
+
+ /**
+ * Returns the previous non-deleted node, patching up pointers as needed.
+ * Returns null if this node is header so has no successor. May also return null
+ * if this node is deleted, so doesn't have a distinct predecessor.
+ *
+ * @return predecessor or null if not found
+ */
+ Node<E> predecessor() {
+ Node<E> n = this;
+ for (;;) {
+ Node<E> b = n.getPrev();
+ if (b == null)
+ return n.findPredecessorOf(this);
+ Node<E> s = b.getNext();
+ if (s == this)
+ return b;
+ if (s == null || !s.isMarker()) {
+ Node<E> p = b.findPredecessorOf(this);
+ if (p != null)
+ return p;
+ }
+ n = b;
+ }
+ }
+
+ /**
+ * Returns the next node containing a nondeleted user element. Use for forward
+ * list traversal.
+ *
+ * @return successor, or null if no such
+ */
+ Node<E> forward() {
+ Node<E> f = successor();
+ return (f == null || f.isSpecial()) ? null : f;
+ }
+
+ /**
+ * Returns previous node containing a nondeleted user element, if possible. Use
+ * for backward list traversal, but beware that if this method is called from a
+ * deleted node, it might not be able to determine a usable predecessor.
+ *
+ * @return predecessor, or null if no such could be found
+ */
+ Node<E> back() {
+ Node<E> f = predecessor();
+ return (f == null || f.isSpecial()) ? null : f;
+ }
+
+ /**
+ * Tries to insert a node holding element as successor, failing if this node is
+ * deleted.
+ *
+ * @param element the element
+ * @return the new node, or null on failure.
+ */
+ Node<E> append(E element) {
+ for (;;) {
+ Node<E> f = getNext();
+ if (f == null || f.isMarker())
+ return null;
+ Node<E> x = new Node<E>(element, f, this);
+ if (casNext(f, x)) {
+ f.setPrev(x); // optimistically link
+ return x;
+ }
+ }
+ }
+
+ /**
+ * Tries to insert a node holding element as predecessor, failing if no live
+ * predecessor can be found to link to.
+ *
+ * @param element the element
+ * @return the new node, or null on failure.
+ */
+ Node<E> prepend(E element) {
+ for (;;) {
+ Node<E> b = predecessor();
+ if (b == null)
+ return null;
+ Node<E> x = new Node<E>(element, this, b);
+ if (b.casNext(this, x)) {
+ setPrev(x); // optimistically link
+ return x;
+ }
+ }
+ }
+
+ /**
+ * Tries to mark this node as deleted, failing if already deleted or if this
+ * node is header or trailer
+ *
+ * @return true if successful
+ */
+ boolean delete() {
+ Node<E> b = getPrev();
+ Node<E> f = getNext();
+ if (b != null && f != null && !f.isMarker() && casNext(f, new Node<E>(f))) {
+ if (b.casNext(this, f))
+ f.setPrev(b);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Tries to insert a node holding element to replace this node. failing if
+ * already deleted.
+ *
+ * @param newElement the new element
+ * @return the new node, or null on failure.
+ */
+ Node<E> replace(E newElement) {
+ for (;;) {
+ Node<E> b = getPrev();
+ Node<E> f = getNext();
+ if (b == null || f == null || f.isMarker())
+ return null;
+ Node<E> x = new Node<E>(newElement, f, b);
+ if (casNext(f, new Node<E>(x))) {
+ b.successor(); // to relink b
+ x.successor(); // to relink f
+ return x;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
index 5ef58831a857fd8aa4ac30147762dc17d773a53e..2a8590d46bab64fe27e8dadf80f91ab0662a4352 100644
--- a/src/main/java/net/minecraft/Util.java
+++ b/src/main/java/net/minecraft/Util.java
@@ -65,6 +65,8 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.annotation.Nullable;
+
+import it.unimi.dsi.fastutil.objects.ObjectList;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.Bootstrap;
import net.minecraft.util.Mth;
@@ -793,7 +795,7 @@ public class Util {
return objectArrayList;
}
- public static <T> void shuffle(ObjectArrayList<T> list, RandomSource random) {
+ public static <T> void shuffle(ObjectList<T> list, RandomSource random) {
int i = list.size();
for(int j = i; j > 1; --j) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index e642b4a83687d03e55feb340452d608c53ae7cce..4beaa69da4001fc2723e9628d64bd3de728d7213 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -5,6 +5,7 @@ import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.shorts.ShortArraySet;
import it.unimi.dsi.fastutil.shorts.ShortSet;
import it.unimi.dsi.fastutil.shorts.ShortSets;
+import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentShortHashSet;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.Packet;
@@ -222,7 +223,7 @@ public class ChunkHolder {
if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
if (this.changedBlocksPerSection[i] == null) {
this.hasChangedSections = true; this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
- this.changedBlocksPerSection[i] = ShortSets.synchronize(new ShortArraySet());
+ this.changedBlocksPerSection[i] = new ConcurrentShortHashSet();
}
this.changedBlocksPerSection[i].add(SectionPos.sectionRelativePos(pos));
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 81697ea6d00967852556c3bb741317db030c24db..85c03dc7c1e714fab281374a177cd4c54e97d939 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -788,7 +788,7 @@ public class ServerChunkCache extends ChunkSource {
//gameprofilerfiller.popPush("broadcast"); // Purpur
//this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur
if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
- ObjectSet<ChunkHolder> copy = new ObjectArraySet<>(this.chunkMap.needsChangeBroadcasting);
+ List<ChunkHolder> copy = new ArrayList<>(this.chunkMap.needsChangeBroadcasting);
this.chunkMap.needsChangeBroadcasting.clear();
for (ChunkHolder holder : copy) {
holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index f33476a35706d7a236fe3bb178d166d568c07674..483c19ac24b074cbdb924d684c0892ddc546af3e 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -24,6 +24,8 @@ import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.annotation.Nullable;
+
+import it.unimi.dsi.fastutil.objects.Object2DoubleMaps;
import net.minecraft.BlockUtil;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
@@ -511,14 +513,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.nextStep = 1.0F;
this.random = SHARED_RANDOM; // Paper
this.remainingFireTicks = -this.getFireImmuneTicks();
- this.fluidHeight = new Object2DoubleArrayMap(2);
- this.fluidOnEyes = new HashSet();
+ this.fluidHeight = Object2DoubleMaps.synchronize(new Object2DoubleArrayMap(2));
+ this.fluidOnEyes = Sets.newConcurrentHashSet();
this.firstTick = true;
this.levelCallback = EntityInLevelCallback.NULL;
this.packetPositionCodec = new VecDeltaCodec();
this.uuid = Mth.createInsecureUUID(this.random);
this.stringUUID = this.uuid.toString();
- this.tags = Sets.newHashSet();
+ this.tags = Sets.newConcurrentHashSet();
this.pistonDeltas = new double[]{0.0D, 0.0D, 0.0D};
this.feetBlockState = null;
this.type = type;
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 93c32dd39693b37efaa05af0486e1bdd298661f3..cdb33a430d0d1671899ab8bb0911193a5688af23 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -17,6 +17,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import net.minecraft.BlockUtil;
@@ -181,7 +182,7 @@ public abstract class LivingEntity extends Entity {
public static final float EXTRA_RENDER_CULLING_SIZE_WITH_BIG_HAT = 0.5F;
private final AttributeMap attributes;
public CombatTracker combatTracker = new CombatTracker(this);
- public final Map<MobEffect, MobEffectInstance> activeEffects = Maps.newHashMap();
+ public final Map<MobEffect, MobEffectInstance> activeEffects = Maps.newConcurrentMap();
private final NonNullList<ItemStack> lastHandItemStacks;
private final NonNullList<ItemStack> lastArmorItemStacks;
public boolean swinging;
@@ -257,7 +258,7 @@ public abstract class LivingEntity extends Entity {
// CraftBukkit start
public int expToDrop;
public boolean forceDrops;
- public ArrayList<org.bukkit.inventory.ItemStack> drops = new ArrayList<org.bukkit.inventory.ItemStack>();
+ public List<org.bukkit.inventory.ItemStack> drops = new CopyOnWriteArrayList<>();
public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes;
public boolean collides = true;
public Set<UUID> collidableExemptions = new HashSet<>();
@@ -875,7 +876,7 @@ public abstract class LivingEntity extends Entity {
// CraftBukkit start
private boolean isTickingEffects = false;
- private List<ProcessableEffect> effectsToProcess = Lists.newArrayList();
+ private List<ProcessableEffect> effectsToProcess = Lists.newCopyOnWriteArrayList();
private static class ProcessableEffect {
@@ -1779,7 +1780,7 @@ public abstract class LivingEntity extends Entity {
}
}); // Paper end
this.postDeathDropItems(deathEvent); // Paper
- this.drops = new ArrayList<>();
+ this.drops = new CopyOnWriteArrayList<>();
// CraftBukkit end
// this.dropInventory();// CraftBukkit - moved up
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java
index 80215972538d5cfce5f224253ea0e34ea4fd45a4..71e44f4aa64b29610f424ea91a9b54b56a155736 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java
@@ -9,6 +9,8 @@ import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
+
+import net.himeki.mcmtfabric.parallelised.ConcurrentDoublyLinkedList;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
@@ -103,7 +105,7 @@ public class LongJumpToRandomPos<E extends Mob> extends Behavior<E> {
return !blockPos2.equals(blockPos);
}).map((blockPos2) -> {
return new LongJumpToRandomPos.PossibleJump(blockPos2.immutable(), Mth.ceil(blockPos.distSqr(blockPos2)));
- }).collect(Collectors.toCollection(Lists::newArrayList));
+ }).collect(Collectors.toCollection(Lists::newCopyOnWriteArrayList));
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
index 99142f749371828f6f55e4fbab03b22eb519ec1e..fc26edc5082f701e6450ca9abf78423840cd773c 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -111,14 +111,7 @@ public class GoalSelector {
}
}
- Iterator<Map.Entry<Goal.Flag, WrappedGoal>> iterator = this.lockedFlags.entrySet().iterator();
-
- while(iterator.hasNext()) {
- Map.Entry<Goal.Flag, WrappedGoal> entry = iterator.next();
- if (!entry.getValue().isRunning()) {
- iterator.remove();
- }
- }
+ this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning());
//profilerFiller.pop(); // Purpur
//profilerFiller.push("goalUpdate"); // Purpur
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
index 8db20db72cd51046213625fac46c35854c59ec5d..4d40526cc90c19ff5a1569c8d6d828a0d0b73ccb 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
@@ -3,6 +3,7 @@ package net.minecraft.world.entity.ai.sensing;
import com.google.common.collect.ImmutableSet;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.longs.Long2LongMap;
+import it.unimi.dsi.fastutil.longs.Long2LongMaps;
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
import java.util.Optional;
import java.util.Set;
@@ -23,7 +24,7 @@ public class NearestBedSensor extends Sensor<Mob> {
private static final int CACHE_TIMEOUT = 40;
private static final int BATCH_SIZE = 5;
private static final int RATE = 20;
- private final Long2LongMap batchCache = new Long2LongOpenHashMap();
+ private final Long2LongMap batchCache = Long2LongMaps.synchronize(new Long2LongOpenHashMap());
private int triedCount;
private long lastUpdate;
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java
index 9babe636176da3c40598eb5bdac0919a1704eaa0..58c6b1f67aedf5ab2167fd070604fc0d8f710435 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java
@@ -2,13 +2,14 @@ package net.minecraft.world.entity.ai.sensing;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
+import it.unimi.dsi.fastutil.ints.IntSets;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Mob;
public class Sensing {
private final Mob mob;
- private final IntSet seen = new IntOpenHashSet();
- private final IntSet unseen = new IntOpenHashSet();
+ private final IntSet seen = IntSets.synchronize(new IntOpenHashSet());
+ private final IntSet unseen = IntSets.synchronize(new IntOpenHashSet());
public Sensing(Mob owner) {
this.mob = owner;
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
index 9f138bc471b5c2a4fa813ff943dbe34018b8df74..5c8a90f8536c9291df5891d8c75de963b75ec4bd 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
@@ -7,6 +7,7 @@ import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
+import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import java.util.List;
import java.util.Map;
@@ -25,8 +26,9 @@ import org.slf4j.Logger;
public class PoiSection {
private static final Logger LOGGER = LogUtils.getLogger();
- private final Short2ObjectMap<PoiRecord> records = new Short2ObjectOpenHashMap<>();
- private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newHashMap(); public final Map<Holder<PoiType>, Set<PoiRecord>> getData() { return this.byType; } // Paper - public accessor
+ private final Short2ObjectMap<PoiRecord> records = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>());
+ private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newConcurrentMap();
+ public final Map<Holder<PoiType>, Set<PoiRecord>> getData() { return this.byType; } // Paper - public accessor
private final Runnable setDirty;
private boolean isValid;
public final Optional<PoiSection> noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
index 8450a22b0fc6e8dc5cad0f61ac52a82b3cd3791e..c55f98fb2a6aade8e0a6f60248f4e29f66f0c193 100644
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
@@ -248,7 +248,9 @@ public final class ItemStack {
}
private void updateEmptyCacheFlag() {
- if (this.emptyCacheFlag && this == ItemStack.EMPTY) throw new AssertionError("TRAP"); // CraftBukkit
+ if (this.emptyCacheFlag && this == ItemStack.EMPTY){
+ return;
+ }//throw new AssertionError("TRAP"); // CraftBukkit
this.emptyCacheFlag = false;
this.emptyCacheFlag = this.isEmpty();
}
diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
index c0d39afe5b80159ed9aaca4ddd4763d707882f2e..2f53f8f2695231179ff16bb014cd990e94f9ec79 100644
--- a/src/main/java/net/minecraft/world/level/Explosion.java
+++ b/src/main/java/net/minecraft/world/level/Explosion.java
@@ -4,12 +4,15 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
+
+import it.unimi.dsi.fastutil.objects.ObjectLists;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
@@ -63,7 +66,7 @@ public class Explosion {
private final float radius;
private final DamageSource damageSource;
private final ExplosionDamageCalculator damageCalculator;
- private final ObjectArrayList<BlockPos> toBlow;
+ private final ObjectList<BlockPos> toBlow;
private final Map<Player, Vec3> hitPlayers;
public boolean wasCanceled = false; // CraftBukkit - add field
@@ -81,9 +84,9 @@ public class Explosion {
}
public Explosion(Level world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, double x, double y, double z, float power, boolean createFire, Explosion.BlockInteraction destructionType) {
- this.random = RandomSource.create();
- this.toBlow = new ObjectArrayList();
- this.hitPlayers = Maps.newHashMap();
+ this.random = RandomSource.createThreadSafe();
+ this.toBlow = ObjectLists.synchronize(new ObjectArrayList<>());
+ this.hitPlayers = Maps.newConcurrentMap();
this.level = world;
this.source = entity;
this.radius = (float) Math.max(power, 0.0); // CraftBukkit - clamp bad values
@@ -396,14 +399,10 @@ public class Explosion {
}
if (this.fire) {
- ObjectListIterator objectlistiterator1 = this.toBlow.iterator();
-
- while (objectlistiterator1.hasNext()) {
- BlockPos blockposition2 = (BlockPos) objectlistiterator1.next();
-
+ for (BlockPos blockposition2 : this.toBlow) {
if (this.random.nextInt(3) == 0 && this.level.getBlockState(blockposition2).isAir() && this.level.getBlockState(blockposition2.below()).isSolidRender(this.level, blockposition2.below())) {
// CraftBukkit start - Ignition by explosion
- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), this).isCancelled()) {
+ if (!CraftEventFactory.callBlockIgniteEvent(this.level, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), this).isCancelled()) {
this.level.setBlockAndUpdate(blockposition2, BaseFireBlock.getState(this.level, blockposition2));
}
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
index 2e922bb844bc147224a60ef2aae33a0125e6ca4a..a78f04ae5eb2d0f11579cb0a40384f3f103371af 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -88,7 +88,7 @@ public class LevelChunk extends ChunkAccess {
private Supplier<ChunkHolder.FullChunkStatus> fullStatus;
@Nullable
private LevelChunk.PostLoadProcessor postLoad;
- private final Int2ObjectMap<GameEventListenerRegistry> gameEventListenerRegistrySections;
+ private final Map<Integer,GameEventListenerRegistry> gameEventListenerRegistrySections;
private final LevelChunkTicks<Block> blockTicks;
private final LevelChunkTicks<Fluid> fluidTicks;
@@ -114,10 +114,10 @@ public class LevelChunk extends ChunkAccess {
this.setBlockNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world));
this.setSkyNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world));
// Paper end - rewrite light engine
- this.tickersInLevel = Maps.newHashMap();
+ this.tickersInLevel = Maps.newConcurrentMap();
this.clientLightReady = false;
this.level = (ServerLevel) world; // CraftBukkit - type
- this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap();
+ this.gameEventListenerRegistrySections = Maps.newConcurrentMap();
Heightmap.Types[] aheightmap_type = Heightmap.Types.values();
int j = aheightmap_type.length;