mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-24 01:19:25 +00:00
1840 lines
83 KiB
Diff
1840 lines
83 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: wangxyper <wangxyper@163.com>
|
|
Date: Sun, 8 Jan 2023 21:08:14 +0800
|
|
Subject: [PATCH] Hearse: Change paper's code
|
|
|
|
Original license: MIT
|
|
Original project: https://github.com/NaturalCodeClub/HearseRewrite
|
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java b/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java
|
|
index 0133ea6feb1ab88f021f66855669f58367e7420b..a1ac254c71eb5559b88ed1a6bd5128539d3b38b5 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java
|
|
@@ -1,128 +1,50 @@
|
|
package com.destroystokyo.paper.util.maplist;
|
|
|
|
-import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectList;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectLists;
|
|
import net.minecraft.world.entity.Entity;
|
|
-import java.util.Arrays;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+
|
|
import java.util.Iterator;
|
|
-import java.util.NoSuchElementException;
|
|
|
|
-// list with O(1) remove & contains
|
|
-/**
|
|
- * @author Spottedleaf
|
|
- */
|
|
public final class EntityList implements Iterable<Entity> {
|
|
-
|
|
- protected final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
|
|
- {
|
|
- this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
|
- }
|
|
-
|
|
- protected static final Entity[] EMPTY_LIST = new Entity[0];
|
|
-
|
|
- protected Entity[] entities = EMPTY_LIST;
|
|
- protected int count;
|
|
+ private final ObjectList<Entity> entities = ObjectLists.synchronize(new ObjectArrayList<>());
|
|
|
|
public int size() {
|
|
- return this.count;
|
|
+ return this.entities.size();
|
|
}
|
|
|
|
public boolean contains(final Entity entity) {
|
|
- return this.entityToIndex.containsKey(entity.getId());
|
|
+ return this.entities.contains(entity);
|
|
}
|
|
|
|
public boolean remove(final Entity entity) {
|
|
- final int index = this.entityToIndex.remove(entity.getId());
|
|
- if (index == Integer.MIN_VALUE) {
|
|
- return false;
|
|
- }
|
|
-
|
|
- // move the entity at the end to this index
|
|
- final int endIndex = --this.count;
|
|
- final Entity end = this.entities[endIndex];
|
|
- if (index != endIndex) {
|
|
- // not empty after this call
|
|
- this.entityToIndex.put(end.getId(), index); // update index
|
|
- }
|
|
- this.entities[index] = end;
|
|
- this.entities[endIndex] = null;
|
|
-
|
|
- return true;
|
|
+ return this.entities.remove(entity);
|
|
}
|
|
|
|
public boolean add(final Entity entity) {
|
|
- final int count = this.count;
|
|
- final int currIndex = this.entityToIndex.putIfAbsent(entity.getId(), count);
|
|
-
|
|
- if (currIndex != Integer.MIN_VALUE) {
|
|
- return false; // already in this list
|
|
- }
|
|
-
|
|
- Entity[] list = this.entities;
|
|
-
|
|
- if (list.length == count) {
|
|
- // resize required
|
|
- list = this.entities = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
|
|
- }
|
|
-
|
|
- list[count] = entity;
|
|
- this.count = count + 1;
|
|
-
|
|
- return true;
|
|
+ return this.entities.add(entity);
|
|
}
|
|
|
|
public Entity getChecked(final int index) {
|
|
- if (index < 0 || index >= this.count) {
|
|
- throw new IndexOutOfBoundsException("Index: " + index + " is out of bounds, size: " + this.count);
|
|
- }
|
|
- return this.entities[index];
|
|
+ return this.entities.get(index);
|
|
}
|
|
|
|
public Entity getUnchecked(final int index) {
|
|
- return this.entities[index];
|
|
+ return this.entities.get(index);
|
|
}
|
|
|
|
public Entity[] getRawData() {
|
|
- return this.entities;
|
|
+ return this.entities.toArray(Entity[]::new);
|
|
}
|
|
|
|
public void clear() {
|
|
- this.entityToIndex.clear();
|
|
- Arrays.fill(this.entities, 0, this.count, null);
|
|
- this.count = 0;
|
|
+ this.entities.clear();
|
|
}
|
|
|
|
@Override
|
|
- public Iterator<Entity> iterator() {
|
|
- return new Iterator<Entity>() {
|
|
-
|
|
- Entity lastRet;
|
|
- int current;
|
|
-
|
|
- @Override
|
|
- public boolean hasNext() {
|
|
- return this.current < EntityList.this.count;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public Entity next() {
|
|
- if (this.current >= EntityList.this.count) {
|
|
- throw new NoSuchElementException();
|
|
- }
|
|
- return this.lastRet = EntityList.this.entities[this.current++];
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void remove() {
|
|
- final Entity lastRet = this.lastRet;
|
|
-
|
|
- if (lastRet == null) {
|
|
- throw new IllegalStateException();
|
|
- }
|
|
- this.lastRet = null;
|
|
-
|
|
- EntityList.this.remove(lastRet);
|
|
- --this.current;
|
|
- }
|
|
- };
|
|
+ public @NotNull Iterator<Entity> iterator() {
|
|
+ return this.entities.iterator();
|
|
}
|
|
}
|
|
diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
|
|
index 0b060183429f4c72ec767075538477b4302bbf0d..f72d99bea1c78c8882793fa8ee3dc3642be1b8c7 100644
|
|
--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
|
|
+++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
|
|
@@ -22,11 +22,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
|
|
import org.apache.commons.lang3.mutable.MutableObject;
|
|
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
|
import org.bukkit.entity.Player;
|
|
-import java.util.ArrayDeque;
|
|
-import java.util.ArrayList;
|
|
-import java.util.List;
|
|
-import java.util.TreeSet;
|
|
+
|
|
+import java.util.*;
|
|
+import java.util.concurrent.ConcurrentSkipListSet;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
+import java.util.concurrent.locks.StampedLock;
|
|
|
|
public final class PlayerChunkLoader {
|
|
|
|
@@ -75,6 +75,9 @@ public final class PlayerChunkLoader {
|
|
return data.getTargetSendViewDistance();
|
|
}
|
|
|
|
+ private final StampedLock sendLock = new StampedLock();
|
|
+ private final StampedLock loadLock = new StampedLock();
|
|
+
|
|
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);
|
|
@@ -109,7 +112,7 @@ public final class PlayerChunkLoader {
|
|
return Integer.compare(System.identityHashCode(p1), System.identityHashCode(p2));
|
|
});
|
|
|
|
- protected final TreeSet<PlayerLoaderData> chunkSendWaitQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
|
|
+ protected final NavigableSet<PlayerLoaderData> chunkSendWaitQueue = new ConcurrentSkipListSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
|
|
if (p1 == p2) {
|
|
return 0;
|
|
}
|
|
@@ -481,8 +484,21 @@ public final class PlayerChunkLoader {
|
|
return;
|
|
}
|
|
loaderData.remove();
|
|
- this.chunkLoadQueue.remove(loaderData);
|
|
- this.chunkSendQueue.remove(loaderData);
|
|
+
|
|
+ long id1 = this.loadLock.writeLock();
|
|
+ try {
|
|
+ this.chunkLoadQueue.remove(loaderData);
|
|
+ }finally {
|
|
+ this.loadLock.unlockWrite(id1);
|
|
+ }
|
|
+
|
|
+ final long id = this.sendLock.writeLock();
|
|
+ try {
|
|
+ this.chunkSendQueue.remove(loaderData);
|
|
+ }finally {
|
|
+ this.sendLock.unlockWrite(id);
|
|
+ }
|
|
+
|
|
this.chunkSendWaitQueue.remove(loaderData);
|
|
synchronized (this.sendingChunkCounts) {
|
|
final int count = this.sendingChunkCounts.removeInt(loaderData);
|
|
@@ -523,29 +539,46 @@ public final class PlayerChunkLoader {
|
|
if (time < nextChunkSend) {
|
|
return;
|
|
}
|
|
- // drain entries from wait queue
|
|
- while (!this.chunkSendWaitQueue.isEmpty()) {
|
|
- final PlayerLoaderData data = this.chunkSendWaitQueue.first();
|
|
+ final long id = this.sendLock.writeLock();
|
|
+ try {
|
|
+ // drain entries from wait queue
|
|
+ while (!this.chunkSendWaitQueue.isEmpty()) {
|
|
+ final PlayerLoaderData data = this.chunkSendWaitQueue.first();
|
|
|
|
- if (data.nextChunkSendTarget > time) {
|
|
- break;
|
|
- }
|
|
+ if (data.nextChunkSendTarget > time) {
|
|
+ break;
|
|
+ }
|
|
|
|
- this.chunkSendWaitQueue.pollFirst();
|
|
+ this.chunkSendWaitQueue.pollFirst();
|
|
|
|
- this.chunkSendQueue.add(data);
|
|
+ this.chunkSendQueue.add(data);
|
|
+ }
|
|
+ }finally {
|
|
+ this.sendLock.unlockWrite(id);
|
|
}
|
|
|
|
- if (this.chunkSendQueue.isEmpty()) {
|
|
- return;
|
|
+ long id2 = this.sendLock.readLock();
|
|
+ try {
|
|
+ if (this.chunkSendQueue.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+ }finally {
|
|
+ this.sendLock.unlockRead(id2);
|
|
}
|
|
|
|
final int maxSends = this.getMaxConcurrentChunkSends();
|
|
final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time;
|
|
for (;;) {
|
|
- if (this.chunkSendQueue.isEmpty()) {
|
|
- break;
|
|
+
|
|
+ long id3 = this.sendLock.readLock();
|
|
+ try {
|
|
+ if (this.chunkSendQueue.isEmpty()) {
|
|
+ break;
|
|
+ }
|
|
+ }finally {
|
|
+ this.sendLock.unlockRead(id3);
|
|
}
|
|
+
|
|
final int currSends = concurrentChunkSends.get();
|
|
if (currSends >= maxSends) {
|
|
break;
|
|
@@ -557,17 +590,29 @@ public final class PlayerChunkLoader {
|
|
|
|
// send chunk
|
|
|
|
- final PlayerLoaderData data = this.chunkSendQueue.removeFirst();
|
|
+ PlayerLoaderData data;
|
|
+
|
|
+ final long id4 = this.sendLock.writeLock();
|
|
+ try {
|
|
+ data = this.chunkSendQueue.removeFirst();
|
|
+ }finally {
|
|
+ this.sendLock.unlockWrite(id4);
|
|
+ }
|
|
|
|
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;
|
|
+ final long id5 = this.sendLock.readLock();
|
|
+ try {
|
|
+ if (this.chunkSendQueue.isEmpty()) {
|
|
+ // nothing left
|
|
+ break;
|
|
+ }
|
|
+ continue;
|
|
+ }finally {
|
|
+ this.sendLock.unlockRead(id5);
|
|
}
|
|
- continue;
|
|
}
|
|
|
|
if (!this.isChunkPlayerLoaded(queuedSend.chunkX, queuedSend.chunkZ)) {
|
|
@@ -575,7 +620,7 @@ public final class PlayerChunkLoader {
|
|
}
|
|
|
|
data.nextChunkSendTarget = nextPlayerDeadline;
|
|
- this.chunkSendWaitQueue.add(data);
|
|
+ this.sendLock.writeLock();
|
|
|
|
synchronized (this.sendingChunkCounts) {
|
|
this.sendingChunkCounts.addTo(data, 1);
|
|
@@ -611,22 +656,39 @@ public final class PlayerChunkLoader {
|
|
// 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
|
|
private void tryLoadChunks() {
|
|
- if (this.chunkLoadQueue.isEmpty()) {
|
|
- return;
|
|
+ final long id = this.loadLock.writeLock();
|
|
+ try {
|
|
+ if (this.chunkLoadQueue.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+ }finally {
|
|
+ this.loadLock.unlockWrite(id);
|
|
}
|
|
|
|
final int maxLoads = this.getMaxChunkLoads();
|
|
final long time = System.nanoTime();
|
|
boolean updatedCounters = false;
|
|
for (;;) {
|
|
- final PlayerLoaderData data = this.chunkLoadQueue.pollFirst();
|
|
+ PlayerLoaderData data;
|
|
+
|
|
+ final long id1 = this.loadLock.writeLock();
|
|
+ try {
|
|
+ data = this.chunkLoadQueue.pollFirst();
|
|
+ }finally {
|
|
+ this.loadLock.unlock(id1);
|
|
+ }
|
|
|
|
data.lastChunkLoad = time;
|
|
|
|
final ChunkPriorityHolder queuedLoad = data.loadQueue.peekFirst();
|
|
if (queuedLoad == null) {
|
|
- if (this.chunkLoadQueue.isEmpty()) {
|
|
- break;
|
|
+ long id2 = this.loadLock.writeLock();
|
|
+ try {
|
|
+ if (this.chunkLoadQueue.isEmpty()) {
|
|
+ break;
|
|
+ }
|
|
+ }finally {
|
|
+ this.loadLock.unlockWrite(id2);
|
|
}
|
|
continue;
|
|
}
|
|
@@ -642,8 +704,12 @@ public final class PlayerChunkLoader {
|
|
if (this.isChunkPlayerLoaded(queuedLoad.chunkX, queuedLoad.chunkZ)) {
|
|
// already loaded!
|
|
data.loadQueue.pollFirst(); // already loaded so we just skip
|
|
- this.chunkLoadQueue.add(data);
|
|
-
|
|
+ final long id3 = this.loadLock.writeLock();
|
|
+ try {
|
|
+ this.chunkLoadQueue.add(data);
|
|
+ }finally {
|
|
+ this.loadLock.unlockWrite(id3);
|
|
+ }
|
|
// ensure the chunk is queued to send
|
|
this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ);
|
|
continue;
|
|
@@ -675,7 +741,12 @@ public final class PlayerChunkLoader {
|
|
if (currentChunkLoads >= maxLoads || (GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate > 0 && (TICKET_ADDITION_COUNTER_SHORT.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate || TICKET_ADDITION_COUNTER_LONG.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate))
|
|
|| (GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate > 0.0 && (data.ticketAdditionCounterShort.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate || data.ticketAdditionCounterLong.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate))) {
|
|
// don't poll, we didn't load it
|
|
- this.chunkLoadQueue.add(data);
|
|
+ final long id4 = this.loadLock.writeLock();
|
|
+ try {
|
|
+ this.chunkLoadQueue.add(data);
|
|
+ }finally {
|
|
+ this.loadLock.unlockWrite(id4);
|
|
+ }
|
|
break;
|
|
}
|
|
}
|
|
@@ -684,7 +755,12 @@ public final class PlayerChunkLoader {
|
|
data.loadQueue.pollFirst();
|
|
|
|
// now that we've polled we can re-add to load queue
|
|
- this.chunkLoadQueue.add(data);
|
|
+ final long id4 = this.loadLock.writeLock();
|
|
+ try {
|
|
+ this.chunkLoadQueue.add(data);
|
|
+ }finally {
|
|
+ this.loadLock.unlockWrite(id4);
|
|
+ }
|
|
|
|
// add necessary tickets to load chunk up to send-ready
|
|
for (int dz = -1; dz <= 1; ++dz) {
|
|
@@ -1110,13 +1186,23 @@ public final class PlayerChunkLoader {
|
|
});
|
|
|
|
// we're modifying loadQueue, must remove
|
|
- this.loader.chunkLoadQueue.remove(this);
|
|
+ final long id3 = this.loader.loadLock.writeLock();
|
|
+ try {
|
|
+ this.loader.chunkLoadQueue.remove(this);
|
|
+ }finally {
|
|
+ this.loader.loadLock.unlockWrite(id3);
|
|
+ }
|
|
|
|
this.loadQueue.clear();
|
|
this.loadQueue.addAll(loadQueue);
|
|
|
|
- // must re-add
|
|
- this.loader.chunkLoadQueue.add(this);
|
|
+ final long id4 = this.loader.loadLock.writeLock();
|
|
+ try {
|
|
+ // must re-add
|
|
+ this.loader.chunkLoadQueue.add(this);
|
|
+ }finally {
|
|
+ this.loader.loadLock.unlockWrite(id4);
|
|
+ }
|
|
|
|
// update the chunk center
|
|
// this must be done last so that the client does not ignore any of our unload chunk packets
|
|
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 61c170555c8854b102c640b0b6a615f9f732edbf..36d3b0d0e1e716cb0e5a2e98c37a834776e26c20 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
|
|
@@ -31,6 +31,8 @@ import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.UUID;
|
|
+import java.util.concurrent.locks.ReadWriteLock;
|
|
+import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
import java.util.concurrent.locks.StampedLock;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Predicate;
|
|
@@ -45,14 +47,17 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
|
|
public final ServerLevel world;
|
|
|
|
+ private final StampedLock entityByLock = new StampedLock();
|
|
private final StampedLock stateLock = new StampedLock();
|
|
+
|
|
+ private final ReadWriteLock paraEntityLock = new ReentrantReadWriteLock(false); //Hearse -- Just for multithreaded entity ticking
|
|
+
|
|
protected final Long2ObjectOpenHashMap<ChunkSlicesRegion> regions = new Long2ObjectOpenHashMap<>(128, 0.5f);
|
|
|
|
private final int minSection; // inclusive
|
|
private final int maxSection; // inclusive
|
|
private final LevelCallback<Entity> worldCallback;
|
|
|
|
- private final StampedLock entityByLock = new StampedLock();
|
|
private final Int2ReferenceOpenHashMap<Entity> entityById = new Int2ReferenceOpenHashMap<>();
|
|
private final Object2ReferenceOpenHashMap<UUID, Entity> entityByUUID = new Object2ReferenceOpenHashMap<>();
|
|
private final EntityList accessibleEntities = new EntityList();
|
|
@@ -75,52 +80,61 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
@Nullable
|
|
@Override
|
|
public Entity get(final int id) {
|
|
- final long attempt = this.entityByLock.tryOptimisticRead();
|
|
- if (attempt != 0L) {
|
|
- try {
|
|
- final Entity ret = this.entityById.get(id);
|
|
+ this.paraEntityLock.readLock().lock();//Hearse
|
|
+ try {
|
|
+ final long attempt = this.entityByLock.tryOptimisticRead();
|
|
+ if (attempt != 0L) {
|
|
+ try {
|
|
+ final Entity ret = this.entityById.get(id);
|
|
|
|
- if (this.entityByLock.validate(attempt)) {
|
|
- return maskNonAccessible(ret);
|
|
+ if (this.entityByLock.validate(attempt)) {
|
|
+ return maskNonAccessible(ret);
|
|
+ }
|
|
+ } catch (final Error error) {
|
|
+ throw error;
|
|
+ } catch (final Throwable thr) {
|
|
+ // ignore
|
|
}
|
|
- } catch (final Error error) {
|
|
- throw error;
|
|
- } catch (final Throwable thr) {
|
|
- // ignore
|
|
}
|
|
- }
|
|
|
|
- this.entityByLock.readLock();
|
|
- try {
|
|
- return maskNonAccessible(this.entityById.get(id));
|
|
- } finally {
|
|
- this.entityByLock.tryUnlockRead();
|
|
+ this.entityByLock.readLock();
|
|
+ try {
|
|
+ return maskNonAccessible(this.entityById.get(id));
|
|
+ } finally {
|
|
+ this.entityByLock.tryUnlockRead();
|
|
+ }
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public Entity get(final UUID id) {
|
|
- final long attempt = this.entityByLock.tryOptimisticRead();
|
|
- if (attempt != 0L) {
|
|
- try {
|
|
- final Entity ret = this.entityByUUID.get(id);
|
|
-
|
|
- if (this.entityByLock.validate(attempt)) {
|
|
- return maskNonAccessible(ret);
|
|
+ this.paraEntityLock.readLock().lock();//Hearse
|
|
+ try {
|
|
+ final long attempt = this.entityByLock.tryOptimisticRead();
|
|
+ if (attempt != 0L) {
|
|
+ try {
|
|
+ final Entity ret = this.entityByUUID.get(id);
|
|
+ if (this.entityByLock.validate(attempt)) {
|
|
+ return maskNonAccessible(ret);
|
|
+ }
|
|
+ } catch (final Error error) {
|
|
+ throw error;
|
|
+ } catch (final Throwable thr) {
|
|
+ // ignore
|
|
}
|
|
- } catch (final Error error) {
|
|
- throw error;
|
|
- } catch (final Throwable thr) {
|
|
- // ignore
|
|
}
|
|
- }
|
|
|
|
- this.entityByLock.readLock();
|
|
- try {
|
|
- return maskNonAccessible(this.entityByUUID.get(id));
|
|
- } finally {
|
|
- this.entityByLock.tryUnlockRead();
|
|
+ this.entityByLock.readLock();
|
|
+ try {
|
|
+ return maskNonAccessible(this.entityByUUID.get(id));
|
|
+ } finally {
|
|
+ this.entityByLock.tryUnlockRead();
|
|
+ }
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
}
|
|
}
|
|
|
|
@@ -129,7 +143,12 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
}
|
|
|
|
public String getDebugInfo() {
|
|
- return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + this.regions.size();
|
|
+ this.paraEntityLock.readLock();//Hearse
|
|
+ try {
|
|
+ return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + this.regions.size();
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
+ }
|
|
}
|
|
|
|
static final class ArrayIterable<T> implements Iterable<T> {
|
|
@@ -166,12 +185,12 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
}
|
|
|
|
@Override
|
|
- public boolean hasNext() {
|
|
+ public synchronized boolean hasNext() {
|
|
return this.off < this.length;
|
|
}
|
|
|
|
@Override
|
|
- public T next() {
|
|
+ public synchronized T next() {
|
|
if (this.off >= this.length) {
|
|
throw new NoSuchElementException();
|
|
}
|
|
@@ -179,7 +198,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
}
|
|
|
|
@Override
|
|
- public void remove() {
|
|
+ public synchronized void remove() {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
}
|
|
@@ -187,20 +206,30 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
|
|
@Override
|
|
public Iterable<Entity> getAll() {
|
|
- return new ArrayIterable<>(this.accessibleEntities.getRawData(), 0, this.accessibleEntities.size());
|
|
+ this.paraEntityLock.readLock().lock();//Hearse
|
|
+ try {
|
|
+ return new ArrayIterable<>(this.accessibleEntities.getRawData(), 0, this.accessibleEntities.size());
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
+ }
|
|
}
|
|
|
|
@Override
|
|
public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AbortableIterationConsumer<U> action) {
|
|
- for (final Entity entity : this.entityById.values()) {
|
|
- final Visibility visibility = EntityLookup.getEntityStatus(entity);
|
|
- if (!visibility.isAccessible()) {
|
|
- continue;
|
|
- }
|
|
- final U casted = filter.tryCast(entity);
|
|
- if (casted != null && action.accept(casted).shouldAbort()) {
|
|
- break;
|
|
+ this.paraEntityLock.readLock().lock();//Hearse
|
|
+ try {
|
|
+ for (final Entity entity : this.entityById.values()) {
|
|
+ final Visibility visibility = EntityLookup.getEntityStatus(entity);
|
|
+ if (!visibility.isAccessible()) {
|
|
+ continue;
|
|
+ }
|
|
+ final U casted = filter.tryCast(entity);
|
|
+ if (casted != null && action.accept(casted).shouldAbort()) {
|
|
+ break;
|
|
+ }
|
|
}
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
}
|
|
}
|
|
|
|
@@ -208,8 +237,8 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
public void get(final AABB box, final Consumer<Entity> action) {
|
|
List<Entity> entities = new ArrayList<>();
|
|
this.getEntitiesWithoutDragonParts(null, box, entities, null);
|
|
- for (int i = 0, len = entities.size(); i < len; ++i) {
|
|
- action.accept(entities.get(i));
|
|
+ for (Entity entity : entities) {
|
|
+ action.accept(entity);
|
|
}
|
|
}
|
|
|
|
@@ -217,8 +246,8 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AABB box, final AbortableIterationConsumer<U> action) {
|
|
List<Entity> entities = new ArrayList<>();
|
|
this.getEntitiesWithoutDragonParts(null, box, entities, null);
|
|
- for (int i = 0, len = entities.size(); i < len; ++i) {
|
|
- final U casted = filter.tryCast(entities.get(i));
|
|
+ for (Entity entity : entities) {
|
|
+ final U casted = filter.tryCast(entity);
|
|
if (casted != null && action.accept(casted).shouldAbort()) {
|
|
break;
|
|
}
|
|
@@ -228,75 +257,55 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
public void entityStatusChange(final Entity entity, final ChunkEntitySlices slices, final Visibility oldVisibility, final Visibility newVisibility, final boolean moved,
|
|
final boolean created, final boolean destroyed) {
|
|
TickThread.ensureTickThread(entity, "Entity status change must only happen on the main thread");
|
|
-
|
|
- if (entity.updatingSectionStatus) {
|
|
- // recursive status update
|
|
- LOGGER.error("Cannot recursively update entity chunk status for entity " + entity, new Throwable());
|
|
- return;
|
|
- }
|
|
-
|
|
- final boolean entityStatusUpdateBefore = slices == null ? false : slices.startPreventingStatusUpdates();
|
|
-
|
|
- if (entityStatusUpdateBefore) {
|
|
- LOGGER.error("Cannot update chunk status for entity " + entity + " since entity chunk (" + slices.chunkX + "," + slices.chunkZ + ") is receiving update", new Throwable());
|
|
- return;
|
|
- }
|
|
-
|
|
+ this.paraEntityLock.writeLock().lock();//Hearse
|
|
try {
|
|
final Boolean ticketBlockBefore = this.world.chunkTaskScheduler.chunkHolderManager.blockTicketUpdates();
|
|
try {
|
|
- entity.updatingSectionStatus = true;
|
|
- try {
|
|
- if (created) {
|
|
- EntityLookup.this.worldCallback.onCreated(entity);
|
|
- }
|
|
+ if (created) {
|
|
+ EntityLookup.this.worldCallback.onCreated(entity);
|
|
+ }
|
|
|
|
- if (oldVisibility == newVisibility) {
|
|
- if (moved && newVisibility.isAccessible()) {
|
|
- EntityLookup.this.worldCallback.onSectionChange(entity);
|
|
- }
|
|
- return;
|
|
+ if (oldVisibility == newVisibility) {
|
|
+ if (moved && newVisibility.isAccessible()) {
|
|
+ EntityLookup.this.worldCallback.onSectionChange(entity);
|
|
}
|
|
+ return;
|
|
+ }
|
|
|
|
- if (newVisibility.ordinal() > oldVisibility.ordinal()) {
|
|
- // status upgrade
|
|
- if (!oldVisibility.isAccessible() && newVisibility.isAccessible()) {
|
|
- this.accessibleEntities.add(entity);
|
|
- EntityLookup.this.worldCallback.onTrackingStart(entity);
|
|
- }
|
|
-
|
|
- if (!oldVisibility.isTicking() && newVisibility.isTicking()) {
|
|
- EntityLookup.this.worldCallback.onTickingStart(entity);
|
|
- }
|
|
- } else {
|
|
- // status downgrade
|
|
- if (oldVisibility.isTicking() && !newVisibility.isTicking()) {
|
|
- EntityLookup.this.worldCallback.onTickingEnd(entity);
|
|
- }
|
|
-
|
|
- if (oldVisibility.isAccessible() && !newVisibility.isAccessible()) {
|
|
- this.accessibleEntities.remove(entity);
|
|
- EntityLookup.this.worldCallback.onTrackingEnd(entity);
|
|
- }
|
|
+ if (newVisibility.ordinal() > oldVisibility.ordinal()) {
|
|
+ // status upgrade
|
|
+ if (!oldVisibility.isAccessible() && newVisibility.isAccessible()) {
|
|
+ this.accessibleEntities.add(entity);
|
|
+ EntityLookup.this.worldCallback.onTrackingStart(entity);
|
|
}
|
|
|
|
- if (moved && newVisibility.isAccessible()) {
|
|
- EntityLookup.this.worldCallback.onSectionChange(entity);
|
|
+ if (!oldVisibility.isTicking() && newVisibility.isTicking()) {
|
|
+ EntityLookup.this.worldCallback.onTickingStart(entity);
|
|
+ }
|
|
+ } else {
|
|
+ // status downgrade
|
|
+ if (oldVisibility.isTicking() && !newVisibility.isTicking()) {
|
|
+ EntityLookup.this.worldCallback.onTickingEnd(entity);
|
|
}
|
|
|
|
- if (destroyed) {
|
|
- EntityLookup.this.worldCallback.onDestroyed(entity);
|
|
+ if (oldVisibility.isAccessible() && !newVisibility.isAccessible()) {
|
|
+ this.accessibleEntities.remove(entity);
|
|
+ EntityLookup.this.worldCallback.onTrackingEnd(entity);
|
|
}
|
|
- } finally {
|
|
- entity.updatingSectionStatus = false;
|
|
+ }
|
|
+
|
|
+ if (moved && newVisibility.isAccessible()) {
|
|
+ EntityLookup.this.worldCallback.onSectionChange(entity);
|
|
+ }
|
|
+
|
|
+ if (destroyed) {
|
|
+ EntityLookup.this.worldCallback.onDestroyed(entity);
|
|
}
|
|
} finally {
|
|
this.world.chunkTaskScheduler.chunkHolderManager.unblockTicketUpdates(ticketBlockBefore);
|
|
}
|
|
} finally {
|
|
- if (slices != null) {
|
|
- slices.stopPreventingStatusUpdates(false);
|
|
- }
|
|
+ this.paraEntityLock.writeLock().lock();//Hearse
|
|
}
|
|
}
|
|
|
|
@@ -341,53 +350,53 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
final int sectionZ = pos.getZ() >> 4;
|
|
TickThread.ensureTickThread(this.world, sectionX, sectionZ, "Cannot add entity off-main thread");
|
|
|
|
- if (entity.isRemoved()) {
|
|
- LOGGER.warn("Refusing to add removed entity: " + entity);
|
|
- return false;
|
|
- }
|
|
-
|
|
- if (entity.updatingSectionStatus) {
|
|
- LOGGER.warn("Entity " + entity + " is currently prevented from being added/removed to world since it is processing section status updates", new Throwable());
|
|
- return false;
|
|
- }
|
|
-
|
|
- if (fromDisk) {
|
|
- ChunkSystem.onEntityPreAdd(this.world, entity);
|
|
+ this.paraEntityLock.writeLock().lock();//Hearse
|
|
+ try {
|
|
if (entity.isRemoved()) {
|
|
- // removed from checkDupeUUID call
|
|
+ LOGGER.warn("Refusing to add removed entity: " + entity);
|
|
return false;
|
|
}
|
|
- }
|
|
|
|
- this.entityByLock.writeLock();
|
|
- try {
|
|
- if (this.entityById.containsKey(entity.getId())) {
|
|
- LOGGER.warn("Entity id already exists: " + entity.getId() + ", mapped to " + this.entityById.get(entity.getId()) + ", can't add " + entity);
|
|
- return false;
|
|
+ if (fromDisk) {
|
|
+ ChunkSystem.onEntityPreAdd(this.world, entity);
|
|
+ if (entity.isRemoved()) {
|
|
+ // removed from checkDupeUUID call
|
|
+ return false;
|
|
+ }
|
|
}
|
|
- if (this.entityByUUID.containsKey(entity.getUUID())) {
|
|
- LOGGER.warn("Entity uuid already exists: " + entity.getUUID() + ", mapped to " + this.entityByUUID.get(entity.getUUID()) + ", can't add " + entity);
|
|
- return false;
|
|
+
|
|
+ this.entityByLock.writeLock();
|
|
+ try {
|
|
+ if (this.entityById.containsKey(entity.getId())) {
|
|
+ LOGGER.warn("Entity id already exists: " + entity.getId() + ", mapped to " + this.entityById.get(entity.getId()) + ", can't add " + entity);
|
|
+ return false;
|
|
+ }
|
|
+ if (this.entityByUUID.containsKey(entity.getUUID())) {
|
|
+ LOGGER.warn("Entity uuid already exists: " + entity.getUUID() + ", mapped to " + this.entityByUUID.get(entity.getUUID()) + ", can't add " + entity);
|
|
+ return false;
|
|
+ }
|
|
+ this.entityById.put(entity.getId(), entity);
|
|
+ this.entityByUUID.put(entity.getUUID(), entity);
|
|
+ } finally {
|
|
+ this.entityByLock.tryUnlockWrite();
|
|
}
|
|
- this.entityById.put(entity.getId(), entity);
|
|
- this.entityByUUID.put(entity.getUUID(), entity);
|
|
- } finally {
|
|
- this.entityByLock.tryUnlockWrite();
|
|
- }
|
|
|
|
- entity.sectionX = sectionX;
|
|
- entity.sectionY = sectionY;
|
|
- entity.sectionZ = sectionZ;
|
|
- final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ);
|
|
- if (!slices.addEntity(entity, sectionY)) {
|
|
- LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")");
|
|
- }
|
|
+ entity.sectionX = sectionX;
|
|
+ entity.sectionY = sectionY;
|
|
+ entity.sectionZ = sectionZ;
|
|
+ final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ);
|
|
+ if (!slices.addEntity(entity, sectionY)) {
|
|
+ LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")");
|
|
+ }
|
|
|
|
- entity.setLevelCallback(new EntityCallback(entity));
|
|
+ entity.setLevelCallback(new EntityCallback(entity));
|
|
|
|
- this.entityStatusChange(entity, slices, Visibility.HIDDEN, getEntityStatus(entity), false, !fromDisk, false);
|
|
+ this.entityStatusChange(entity, slices, Visibility.HIDDEN, getEntityStatus(entity), false, !fromDisk, false);
|
|
|
|
- return true;
|
|
+ return true;
|
|
+ }finally {
|
|
+ this.paraEntityLock.writeLock().unlock();//Hearse
|
|
+ }
|
|
}
|
|
|
|
private void removeEntity(final Entity entity) {
|
|
@@ -398,27 +407,32 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
if (!entity.isRemoved()) {
|
|
throw new IllegalStateException("Only call Entity#setRemoved to remove an entity");
|
|
}
|
|
- final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ);
|
|
- // all entities should be in a chunk
|
|
- if (slices == null) {
|
|
- LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")");
|
|
- } else {
|
|
- if (!slices.removeEntity(entity, sectionY)) {
|
|
- LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")");
|
|
- }
|
|
- }
|
|
- entity.sectionX = entity.sectionY = entity.sectionZ = Integer.MIN_VALUE;
|
|
-
|
|
- this.entityByLock.writeLock();
|
|
+ this.paraEntityLock.writeLock().lock();//Hearse
|
|
try {
|
|
- if (!this.entityById.remove(entity.getId(), entity)) {
|
|
- LOGGER.warn("Failed to remove entity " + entity + " by id, current entity mapped: " + this.entityById.get(entity.getId()));
|
|
+ final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ);
|
|
+ // all entities should be in a chunk
|
|
+ if (slices == null) {
|
|
+ LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")");
|
|
+ } else {
|
|
+ if (!slices.removeEntity(entity, sectionY)) {
|
|
+ LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")");
|
|
+ }
|
|
}
|
|
- if (!this.entityByUUID.remove(entity.getUUID(), entity)) {
|
|
- LOGGER.warn("Failed to remove entity " + entity + " by uuid, current entity mapped: " + this.entityByUUID.get(entity.getUUID()));
|
|
+ entity.sectionX = entity.sectionY = entity.sectionZ = Integer.MIN_VALUE;
|
|
+
|
|
+ this.entityByLock.writeLock();
|
|
+ try {
|
|
+ if (!this.entityById.remove(entity.getId(), entity)) {
|
|
+ LOGGER.warn("Failed to remove entity " + entity + " by id, current entity mapped: " + this.entityById.get(entity.getId()));
|
|
+ }
|
|
+ if (!this.entityByUUID.remove(entity.getUUID(), entity)) {
|
|
+ LOGGER.warn("Failed to remove entity " + entity + " by uuid, current entity mapped: " + this.entityByUUID.get(entity.getUUID()));
|
|
+ }
|
|
+ } finally {
|
|
+ this.entityByLock.tryUnlockWrite();
|
|
}
|
|
- } finally {
|
|
- this.entityByLock.tryUnlockWrite();
|
|
+ }finally {
|
|
+ this.paraEntityLock.writeLock().unlock();//Hearse
|
|
}
|
|
}
|
|
|
|
@@ -441,22 +455,27 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
// ensure the old section is owned by this tick thread
|
|
TickThread.ensureTickThread(this.world, entity.sectionX, entity.sectionZ, "Cannot move entity off-main");
|
|
|
|
- final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ);
|
|
- final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
|
|
+ this.paraEntityLock.writeLock().lock();//Hearse
|
|
+ try {
|
|
+ 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.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)) {
|
|
- LOGGER.warn("Could not add entity " + entity + " to its new chunk section (" + newSectionX + "," + newSectionY + "," + newSectionZ + ") as it is already contained in the section");
|
|
- }
|
|
+ if (!slices.addEntity(entity, newSectionY)) {
|
|
+ LOGGER.warn("Could not add entity " + entity + " to its new chunk section (" + newSectionX + "," + newSectionY + "," + newSectionZ + ") as it is already contained in the section");
|
|
+ }
|
|
|
|
- entity.sectionX = newSectionX;
|
|
- entity.sectionY = newSectionY;
|
|
- entity.sectionZ = newSectionZ;
|
|
+ entity.sectionX = newSectionX;
|
|
+ entity.sectionY = newSectionY;
|
|
+ entity.sectionZ = newSectionZ;
|
|
|
|
- return slices;
|
|
+ return slices;
|
|
+ }finally {
|
|
+ this.paraEntityLock.writeLock().unlock();//Hearse
|
|
+ }
|
|
}
|
|
|
|
public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
|
|
@@ -470,31 +489,36 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
final int maxRegionX = maxChunkX >> REGION_SHIFT;
|
|
final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
|
|
|
|
- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
+ this.paraEntityLock.readLock().lock();//Hearse
|
|
+ try {
|
|
+ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
+ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
+ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
+ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
+ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
|
|
- if (region == null) {
|
|
- continue;
|
|
- }
|
|
+ if (region == null) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
+ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
+ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
- for (int currX = minX; currX <= maxX; ++currX) {
|
|
- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
- continue;
|
|
- }
|
|
+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
+ for (int currX = minX; currX <= maxX; ++currX) {
|
|
+ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
+ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- chunk.getEntitiesWithoutDragonParts(except, box, into, predicate);
|
|
+ chunk.getEntitiesWithoutDragonParts(except, box, into, predicate);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
}
|
|
}
|
|
|
|
@@ -509,31 +533,36 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
final int maxRegionX = maxChunkX >> REGION_SHIFT;
|
|
final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
|
|
|
|
- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
+ this.paraEntityLock.readLock().lock();//Hearse
|
|
+ try {
|
|
+ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
+ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
+ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
+ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
+ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
|
|
- if (region == null) {
|
|
- continue;
|
|
- }
|
|
+ if (region == null) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
+ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
+ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
- for (int currX = minX; currX <= maxX; ++currX) {
|
|
- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
- continue;
|
|
- }
|
|
+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
+ for (int currX = minX; currX <= maxX; ++currX) {
|
|
+ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
+ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- chunk.getEntities(except, box, into, predicate);
|
|
+ chunk.getEntities(except, box, into, predicate);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
}
|
|
}
|
|
|
|
@@ -548,31 +577,36 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
final int maxRegionX = maxChunkX >> REGION_SHIFT;
|
|
final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
|
|
|
|
- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
+ this.paraEntityLock.readLock().lock();//Hearse
|
|
+ try {
|
|
+ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
+ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
+ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
+ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
+ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
|
|
- if (region == null) {
|
|
- continue;
|
|
- }
|
|
+ if (region == null) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
+ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
+ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
- for (int currX = minX; currX <= maxX; ++currX) {
|
|
- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
- continue;
|
|
- }
|
|
+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
+ for (int currX = minX; currX <= maxX; ++currX) {
|
|
+ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
+ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- chunk.getHardCollidingEntities(except, box, into, predicate);
|
|
+ chunk.getHardCollidingEntities(except, box, into, predicate);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
}
|
|
}
|
|
|
|
@@ -588,31 +622,36 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
final int maxRegionX = maxChunkX >> REGION_SHIFT;
|
|
final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
|
|
|
|
- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
+ this.paraEntityLock.readLock().lock();//Hearse
|
|
+ try {
|
|
+ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
+ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
+ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
+ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
+ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
|
|
- if (region == null) {
|
|
- continue;
|
|
- }
|
|
+ if (region == null) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
+ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
+ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
- for (int currX = minX; currX <= maxX; ++currX) {
|
|
- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
- continue;
|
|
- }
|
|
+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
+ for (int currX = minX; currX <= maxX; ++currX) {
|
|
+ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
+ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- chunk.getEntities(type, box, (List)into, (Predicate)predicate);
|
|
+ chunk.getEntities(type, box, (List)into, (Predicate)predicate);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
}
|
|
}
|
|
|
|
@@ -628,31 +667,36 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
|
final int maxRegionX = maxChunkX >> REGION_SHIFT;
|
|
final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
|
|
|
|
- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
+ try {
|
|
+ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
|
|
+ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
|
|
+ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
+ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
|
|
+ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
|
|
|
|
- if (region == null) {
|
|
- continue;
|
|
- }
|
|
+ if (region == null) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
+ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
|
|
+ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
|
|
|
|
- for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
- for (int currX = minX; currX <= maxX; ++currX) {
|
|
- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
- continue;
|
|
- }
|
|
+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
+ for (int currX = minX; currX <= maxX; ++currX) {
|
|
+ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
|
|
+ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- chunk.getEntities(clazz, except, box, into, predicate);
|
|
+ chunk.getEntities(clazz, except, box, into, predicate);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
+ }finally {
|
|
+ this.paraEntityLock.readLock().unlock();//Hearse
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
|
index 830d863cd9665d58875bfa5ca2bcd22f89ab2d49..15eeea40bb7a44470f6f3f0e2473cb451812eec1 100644
|
|
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
|
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
|
@@ -13,14 +13,10 @@ import io.papermc.paper.util.CoordinateUtils;
|
|
import io.papermc.paper.util.TickThread;
|
|
import io.papermc.paper.util.misc.Delayed8WayDistancePropagator2D;
|
|
import io.papermc.paper.world.ChunkEntitySlices;
|
|
-import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap;
|
|
-import it.unimi.dsi.fastutil.longs.Long2IntMap;
|
|
-import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
|
-import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
-import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
|
-import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
-import it.unimi.dsi.fastutil.longs.LongIterator;
|
|
+import it.unimi.dsi.fastutil.longs.*;
|
|
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectSortedSet;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectSortedSets;
|
|
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import io.papermc.paper.chunk.system.ChunkSystem;
|
|
@@ -39,13 +35,8 @@ import org.bukkit.plugin.Plugin;
|
|
import org.slf4j.Logger;
|
|
import java.io.IOException;
|
|
import java.text.DecimalFormat;
|
|
-import java.util.ArrayDeque;
|
|
-import java.util.ArrayList;
|
|
-import java.util.Collection;
|
|
-import java.util.Collections;
|
|
-import java.util.Iterator;
|
|
-import java.util.List;
|
|
-import java.util.Objects;
|
|
+import java.util.*;
|
|
+import java.util.concurrent.ConcurrentLinkedDeque;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
@@ -67,16 +58,16 @@ public final class ChunkHolderManager {
|
|
final ReentrantLock ticketLock = new ReentrantLock();
|
|
|
|
private final SWMRLong2ObjectHashTable<NewChunkHolder> chunkHolders = new SWMRLong2ObjectHashTable<>(16384, 0.25f);
|
|
- private final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets = new Long2ObjectOpenHashMap<>(8192, 0.25f);
|
|
+ private final Long2ObjectMap<SortedArraySet<Ticket<?>>> tickets = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(8192, 0.25f));
|
|
// what a disaster of a name
|
|
// this is a map of removal tick to a map of chunks and the number of tickets a chunk has that are to expire that tick
|
|
- private final Long2ObjectOpenHashMap<Long2IntOpenHashMap> removeTickToChunkExpireTicketCount = new Long2ObjectOpenHashMap<>();
|
|
+ private final Long2ObjectMap<Long2IntOpenHashMap> removeTickToChunkExpireTicketCount = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
|
private final ServerLevel world;
|
|
private final ChunkTaskScheduler taskScheduler;
|
|
private long currentTick;
|
|
|
|
- private final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = new ArrayDeque<>();
|
|
- private final ObjectRBTreeSet<NewChunkHolder> autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> {
|
|
+ private final Deque<NewChunkHolder> pendingFullLoadUpdate = new ConcurrentLinkedDeque<>();
|
|
+ private final ObjectSortedSet<NewChunkHolder> autoSaveQueue = ObjectSortedSets.synchronize(new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> {
|
|
if (c1 == c2) {
|
|
return 0;
|
|
}
|
|
@@ -95,7 +86,7 @@ public final class ChunkHolderManager {
|
|
}
|
|
|
|
return Long.compare(coord1, coord2);
|
|
- });
|
|
+ }));
|
|
|
|
public ChunkHolderManager(final ServerLevel world, final ChunkTaskScheduler taskScheduler) {
|
|
this.world = world;
|
|
@@ -311,7 +302,7 @@ public final class ChunkHolderManager {
|
|
public Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> getTicketsCopy() {
|
|
this.ticketLock.lock();
|
|
try {
|
|
- return this.tickets.clone();
|
|
+ return new Long2ObjectOpenHashMap<>(this.tickets);
|
|
} finally {
|
|
this.ticketLock.unlock();
|
|
}
|
|
@@ -784,7 +775,7 @@ public final class ChunkHolderManager {
|
|
}
|
|
if (!TickThread.isTickThread()) {
|
|
this.taskScheduler.scheduleChunkTask(() -> {
|
|
- final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
|
|
+ final Deque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
|
|
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
|
|
pendingFullLoadUpdate.add(changedFullStatus.get(i));
|
|
}
|
|
@@ -792,7 +783,7 @@ public final class ChunkHolderManager {
|
|
ChunkHolderManager.this.processPendingFullUpdate();
|
|
}, PrioritisedExecutor.Priority.HIGHEST);
|
|
} else {
|
|
- final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
|
+ final Deque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
|
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
|
|
pendingFullLoadUpdate.add(changedFullStatus.get(i));
|
|
}
|
|
@@ -1039,7 +1030,7 @@ public final class ChunkHolderManager {
|
|
|
|
// only call on tick thread
|
|
protected final boolean processPendingFullUpdate() {
|
|
- final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
|
+ final Deque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
|
|
|
boolean ret = false;
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/util/CachedLists.java b/src/main/java/io/papermc/paper/util/CachedLists.java
|
|
index e08f4e39db4ee3fed62e37364d17dcc5c5683504..f543462240e744e3424d302eb62be46c70ef9377 100644
|
|
--- a/src/main/java/io/papermc/paper/util/CachedLists.java
|
|
+++ b/src/main/java/io/papermc/paper/util/CachedLists.java
|
|
@@ -1,57 +1,22 @@
|
|
package io.papermc.paper.util;
|
|
|
|
+import com.google.common.collect.Lists;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.phys.AABB;
|
|
-import org.bukkit.Bukkit;
|
|
-import org.bukkit.craftbukkit.util.UnsafeList;
|
|
import java.util.List;
|
|
|
|
public final class CachedLists {
|
|
-
|
|
- // Paper start - optimise collisions
|
|
- static final UnsafeList<AABB> TEMP_COLLISION_LIST = new UnsafeList<>(1024);
|
|
- static boolean tempCollisionListInUse;
|
|
-
|
|
- public static UnsafeList<AABB> getTempCollisionList() {
|
|
- if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) {
|
|
- return new UnsafeList<>(16);
|
|
- }
|
|
- tempCollisionListInUse = true;
|
|
- return TEMP_COLLISION_LIST;
|
|
- }
|
|
-
|
|
- public static void returnTempCollisionList(List<AABB> list) {
|
|
- if (list != TEMP_COLLISION_LIST) {
|
|
- return;
|
|
- }
|
|
- ((UnsafeList)list).setSize(0);
|
|
- tempCollisionListInUse = false;
|
|
+ public static List<AABB> getTempCollisionList() {
|
|
+ return Lists.newCopyOnWriteArrayList();
|
|
}
|
|
|
|
- static final UnsafeList<Entity> TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024);
|
|
- static boolean tempGetEntitiesListInUse;
|
|
+ public static void returnTempCollisionList(List<AABB> list) {}
|
|
|
|
- public static UnsafeList<Entity> getTempGetEntitiesList() {
|
|
- if (!Bukkit.isPrimaryThread() || tempGetEntitiesListInUse) {
|
|
- return new UnsafeList<>(16);
|
|
- }
|
|
- tempGetEntitiesListInUse = true;
|
|
- return TEMP_GET_ENTITIES_LIST;
|
|
+ public static List<Entity> getTempGetEntitiesList() {
|
|
+ return Lists.newCopyOnWriteArrayList();
|
|
}
|
|
|
|
- public static void returnTempGetEntitiesList(List<Entity> list) {
|
|
- if (list != TEMP_GET_ENTITIES_LIST) {
|
|
- return;
|
|
- }
|
|
- ((UnsafeList)list).setSize(0);
|
|
- tempGetEntitiesListInUse = false;
|
|
- }
|
|
- // Paper end - optimise collisions
|
|
+ public static void returnTempGetEntitiesList(List<Entity> list) {}
|
|
|
|
- public static void reset() {
|
|
- // Paper start - optimise collisions
|
|
- TEMP_COLLISION_LIST.completeReset();
|
|
- TEMP_GET_ENTITIES_LIST.completeReset();
|
|
- // Paper end - optimise collisions
|
|
- }
|
|
+ public static void reset() {}
|
|
}
|
|
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
|
|
index f597d65d56964297eeeed6c7e77703764178fee0..f6c8dccb7f7cd287f1ebdf46481365b952baa891 100644
|
|
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
|
|
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
|
|
@@ -22,6 +22,7 @@ import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
+import java.util.concurrent.locks.StampedLock;
|
|
import java.util.function.Predicate;
|
|
|
|
public final class ChunkEntitySlices {
|
|
@@ -168,17 +169,6 @@ public final class ChunkEntitySlices {
|
|
}
|
|
}
|
|
|
|
- private boolean preventStatusUpdates;
|
|
- public boolean startPreventingStatusUpdates() {
|
|
- final boolean ret = this.preventStatusUpdates;
|
|
- this.preventStatusUpdates = true;
|
|
- return ret;
|
|
- }
|
|
-
|
|
- public void stopPreventingStatusUpdates(final boolean prev) {
|
|
- this.preventStatusUpdates = prev;
|
|
- }
|
|
-
|
|
public void updateStatus(final ChunkHolder.FullChunkStatus status, final EntityLookup lookup) {
|
|
this.status = status;
|
|
|
|
@@ -378,6 +368,7 @@ public final class ChunkEntitySlices {
|
|
protected final long[] nonEmptyBitset;
|
|
protected final BasicEntityList<Entity>[] entitiesBySection;
|
|
protected int count;
|
|
+ private final StampedLock listLock = new StampedLock();//Hearse
|
|
|
|
public EntityCollectionBySection(final ChunkEntitySlices manager) {
|
|
this.manager = manager;
|
|
@@ -389,212 +380,242 @@ public final class ChunkEntitySlices {
|
|
}
|
|
|
|
public void addEntity(final Entity entity, final int sectionIndex) {
|
|
- BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
|
|
+ final long id = this.listLock.writeLock();
|
|
+ try {
|
|
+ BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
|
|
|
|
- if (list != null && list.has(entity)) {
|
|
- return;
|
|
- }
|
|
+ if (list != null && list.has(entity)) {
|
|
+ return;
|
|
+ }
|
|
|
|
- if (list == null) {
|
|
- this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>();
|
|
- this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1)));
|
|
- }
|
|
+ if (list == null) {
|
|
+ this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>();
|
|
+ this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1)));
|
|
+ }
|
|
|
|
- list.add(entity);
|
|
- ++this.count;
|
|
+ list.add(entity);
|
|
+ ++this.count;
|
|
+ }finally {
|
|
+ this.listLock.unlockWrite(id);
|
|
+ }
|
|
}
|
|
|
|
public void removeEntity(final Entity entity, final int sectionIndex) {
|
|
- final BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
|
|
+ final long id = this.listLock.writeLock();
|
|
+ try {
|
|
+ final BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
|
|
|
|
- if (list == null || !list.remove(entity)) {
|
|
- return;
|
|
- }
|
|
+ if (list == null || !list.remove(entity)) {
|
|
+ return;
|
|
+ }
|
|
|
|
- --this.count;
|
|
+ --this.count;
|
|
|
|
- if (list.isEmpty()) {
|
|
- this.entitiesBySection[sectionIndex] = null;
|
|
- this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1)));
|
|
+ if (list.isEmpty()) {
|
|
+ this.entitiesBySection[sectionIndex] = null;
|
|
+ this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1)));
|
|
+ }
|
|
+ }finally {
|
|
+ this.listLock.unlockWrite(id);
|
|
}
|
|
}
|
|
|
|
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
|
|
- if (this.count == 0) {
|
|
- return;
|
|
- }
|
|
+ final long id = this.listLock.readLock();
|
|
+ try {
|
|
+ if (this.count == 0) {
|
|
+ return;
|
|
+ }
|
|
|
|
- final int minSection = this.manager.minSection;
|
|
- final int maxSection = this.manager.maxSection;
|
|
+ final int minSection = this.manager.minSection;
|
|
+ final int maxSection = this.manager.maxSection;
|
|
|
|
- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
|
|
- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
|
|
+ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
|
|
+ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
|
|
|
|
- final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
|
|
+ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
|
|
|
|
- for (int section = min; section <= max; ++section) {
|
|
- final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
|
|
+ for (int section = min; section <= max; ++section) {
|
|
+ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
|
|
|
|
- if (list == null) {
|
|
- continue;
|
|
- }
|
|
+ if (list == null) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final Entity[] storage = list.storage;
|
|
+ final Entity[] storage = list.storage;
|
|
|
|
- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
|
|
- final Entity entity = storage[i];
|
|
+ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
|
|
+ final Entity entity = storage[i];
|
|
|
|
- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
|
|
- continue;
|
|
- }
|
|
+ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- if (predicate != null && !predicate.test(entity)) {
|
|
- continue;
|
|
- }
|
|
+ if (predicate != null && !predicate.test(entity)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- into.add(entity);
|
|
+ into.add(entity);
|
|
+ }
|
|
}
|
|
+ }finally {
|
|
+ this.listLock.unlockRead(id);
|
|
}
|
|
}
|
|
|
|
public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List<Entity> into,
|
|
final Predicate<? super Entity> predicate) {
|
|
- if (this.count == 0) {
|
|
- return;
|
|
- }
|
|
+ final long id = this.listLock.readLock();
|
|
+ try {
|
|
+ if (this.count == 0) {
|
|
+ return;
|
|
+ }
|
|
|
|
- final int minSection = this.manager.minSection;
|
|
- final int maxSection = this.manager.maxSection;
|
|
+ final int minSection = this.manager.minSection;
|
|
+ final int maxSection = this.manager.maxSection;
|
|
|
|
- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
|
|
- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
|
|
+ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
|
|
+ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
|
|
|
|
- final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
|
|
+ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
|
|
|
|
- for (int section = min; section <= max; ++section) {
|
|
- final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
|
|
+ for (int section = min; section <= max; ++section) {
|
|
+ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
|
|
|
|
- if (list == null) {
|
|
- continue;
|
|
- }
|
|
+ if (list == null) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final Entity[] storage = list.storage;
|
|
+ final Entity[] storage = list.storage;
|
|
|
|
- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
|
|
- final Entity entity = storage[i];
|
|
+ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
|
|
+ final Entity entity = storage[i];
|
|
|
|
- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
|
|
- continue;
|
|
- }
|
|
+ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- if (predicate == null || predicate.test(entity)) {
|
|
- into.add(entity);
|
|
- } // else: continue to test the ender dragon parts
|
|
+ if (predicate == null || predicate.test(entity)) {
|
|
+ into.add(entity);
|
|
+ } // else: continue to test the ender dragon parts
|
|
|
|
- if (entity instanceof EnderDragon) {
|
|
- for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) {
|
|
- if (part == except || !part.getBoundingBox().intersects(box)) {
|
|
- continue;
|
|
- }
|
|
+ if (entity instanceof EnderDragon) {
|
|
+ for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) {
|
|
+ if (part == except || !part.getBoundingBox().intersects(box)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- if (predicate != null && !predicate.test(part)) {
|
|
- continue;
|
|
- }
|
|
+ if (predicate != null && !predicate.test(part)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- into.add(part);
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
+ into.add(part);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }finally {
|
|
+ this.listLock.unlockRead(id);
|
|
+ }
|
|
}
|
|
|
|
public void getEntitiesWithEnderDragonParts(final Entity except, final Class<?> clazz, final AABB box, final List<Entity> into,
|
|
final Predicate<? super Entity> predicate) {
|
|
- if (this.count == 0) {
|
|
- return;
|
|
- }
|
|
+ final long id = this.listLock.readLock();
|
|
+ try {
|
|
+ if (this.count == 0) {
|
|
+ return;
|
|
+ }
|
|
|
|
- final int minSection = this.manager.minSection;
|
|
- final int maxSection = this.manager.maxSection;
|
|
+ final int minSection = this.manager.minSection;
|
|
+ final int maxSection = this.manager.maxSection;
|
|
|
|
- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
|
|
- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
|
|
+ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
|
|
+ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
|
|
|
|
- final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
|
|
+ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
|
|
|
|
- for (int section = min; section <= max; ++section) {
|
|
- final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
|
|
+ for (int section = min; section <= max; ++section) {
|
|
+ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
|
|
|
|
- if (list == null) {
|
|
- continue;
|
|
- }
|
|
+ if (list == null) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final Entity[] storage = list.storage;
|
|
+ final Entity[] storage = list.storage;
|
|
|
|
- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
|
|
- final Entity entity = storage[i];
|
|
+ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
|
|
+ final Entity entity = storage[i];
|
|
|
|
- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
|
|
- continue;
|
|
- }
|
|
+ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- if (predicate == null || predicate.test(entity)) {
|
|
- into.add(entity);
|
|
- } // else: continue to test the ender dragon parts
|
|
+ if (predicate == null || predicate.test(entity)) {
|
|
+ into.add(entity);
|
|
+ } // else: continue to test the ender dragon parts
|
|
|
|
- if (entity instanceof EnderDragon) {
|
|
- for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) {
|
|
- if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) {
|
|
- continue;
|
|
- }
|
|
+ if (entity instanceof EnderDragon) {
|
|
+ for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) {
|
|
+ if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- if (predicate != null && !predicate.test(part)) {
|
|
- continue;
|
|
- }
|
|
+ if (predicate != null && !predicate.test(part)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- into.add(part);
|
|
+ into.add(part);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
+ }finally {
|
|
+ this.listLock.unlockRead(id);
|
|
}
|
|
}
|
|
|
|
public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
|
|
final Predicate<? super T> predicate) {
|
|
- if (this.count == 0) {
|
|
- return;
|
|
- }
|
|
+ final long id = this.listLock.readLock();
|
|
+ try {
|
|
+ if (this.count == 0) {
|
|
+ return;
|
|
+ }
|
|
|
|
- final int minSection = this.manager.minSection;
|
|
- final int maxSection = this.manager.maxSection;
|
|
+ final int minSection = this.manager.minSection;
|
|
+ final int maxSection = this.manager.maxSection;
|
|
|
|
- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
|
|
- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
|
|
+ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
|
|
+ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
|
|
|
|
- final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
|
|
+ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
|
|
|
|
- for (int section = min; section <= max; ++section) {
|
|
- final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
|
|
+ for (int section = min; section <= max; ++section) {
|
|
+ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
|
|
|
|
- if (list == null) {
|
|
- continue;
|
|
- }
|
|
+ if (list == null) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final Entity[] storage = list.storage;
|
|
+ final Entity[] storage = list.storage;
|
|
|
|
- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
|
|
- final Entity entity = storage[i];
|
|
+ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
|
|
+ final Entity entity = storage[i];
|
|
|
|
- if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) {
|
|
- continue;
|
|
- }
|
|
+ if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- if (predicate != null && !predicate.test((T)entity)) {
|
|
- continue;
|
|
- }
|
|
+ if (predicate != null && !predicate.test((T)entity)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- into.add((T)entity);
|
|
+ into.add((T)entity);
|
|
+ }
|
|
}
|
|
+ }finally {
|
|
+ this.listLock.unlockRead(id);
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
index 8f3f7a4c2429dd3c9060a1d2f412e41a5448bf4c..99912ae883227e1c0a0adc1edbb4c4baee4fe2bc 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -4441,12 +4441,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
// Paper end - block invalid positions
|
|
// Paper end
|
|
- // Paper start - rewrite chunk system
|
|
- if (this.updatingSectionStatus) {
|
|
- LOGGER.error("Refusing to update position for entity " + this + " to position " + new Vec3(x, y, z) + " since it is processing a section status update", new Throwable());
|
|
- return;
|
|
- }
|
|
- // Paper end - rewrite chunk system
|
|
// Paper start - fix MC-4
|
|
if (this instanceof ItemEntity) {
|
|
if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixEntityPositionDesync) {
|
|
@@ -4562,10 +4556,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
public final void setRemoved(Entity.RemovalReason reason) {
|
|
// Paper start - rewrite chunk system
|
|
io.papermc.paper.util.TickThread.ensureTickThread(this, "Cannot remove entity off-main");
|
|
- if (this.updatingSectionStatus) {
|
|
- LOGGER.warn("Entity " + this + " is currently prevented from being added/removed to world since it is processing section status updates", new Throwable());
|
|
- return;
|
|
- }
|
|
// Paper end - rewrite chunk system
|
|
if (this.removalReason == null) {
|
|
this.removalReason = reason;
|