9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-23 17:09:29 +00:00

Updated Upstream (Hearse)

This commit is contained in:
Dreeam
2023-01-15 12:31:48 -05:00
parent d1e7115af3
commit fcf3d2f94e
41 changed files with 5994 additions and 19689 deletions

View File

@@ -64,4 +64,4 @@ Credits:
- [Petal](https://github.com/Bloom-host/Petal) - [Petal](https://github.com/Bloom-host/Petal)
- [Carpet Fixes](https://github.com/fxmorin/carpet-fixes) - [Carpet Fixes](https://github.com/fxmorin/carpet-fixes)
- [VMP](https://github.com/RelativityMC/VMP-fabric) - [VMP](https://github.com/RelativityMC/VMP-fabric)
- [Hearse](https://github.com/NaturalCodeClub/HearseRewrite) - [Hearse](https://github.com/NaturalCodeClub/Hearse)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,688 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 15 Jan 2023 09:52:27 +0800
Subject: [PATCH] Hearse: Paper chunk system changes
Original license: MIT
Original project: https://github.com/NaturalCodeClub/Hearse
diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
index 0b060183429f4c72ec767075538477b4302bbf0d..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;
@@ -22,10 +22,10 @@ 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.ConcurrentLinkedDeque;
+import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;
public final class PlayerChunkLoader {
@@ -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;
}
@@ -109,7 +109,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;
}
@@ -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);
@@ -373,7 +373,7 @@ public final class PlayerChunkLoader {
}
return !(data.hasSentChunk(chunkX - 1, chunkZ) && data.hasSentChunk(chunkX + 1, chunkZ) &&
- data.hasSentChunk(chunkX, chunkZ - 1) && data.hasSentChunk(chunkX, chunkZ + 1));
+ data.hasSentChunk(chunkX, chunkZ - 1) && data.hasSentChunk(chunkX, chunkZ + 1));
}
protected int getMaxConcurrentChunkSends() {
@@ -523,17 +523,16 @@ public final class PlayerChunkLoader {
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()) {
@@ -542,10 +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;
@@ -554,19 +552,12 @@ public final class PlayerChunkLoader {
if (!concurrentChunkSends.compareAndSet(currSends, currSends + 1)) {
continue;
}
-
// send chunk
-
- final 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;
}
@@ -581,17 +572,18 @@ public final class PlayerChunkLoader {
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);
}
}
@@ -618,16 +610,12 @@ public final class PlayerChunkLoader {
final int maxLoads = this.getMaxChunkLoads();
final long time = System.nanoTime();
boolean updatedCounters = false;
- for (;;) {
- final 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;
}
@@ -673,7 +661,7 @@ public final class PlayerChunkLoader {
final int currentChunkLoads = this.concurrentChunkLoads;
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))) {
+ || (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);
break;
@@ -786,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;
@@ -964,14 +952,14 @@ public final class PlayerChunkLoader {
&& tickViewDistance == this.lastTickDistance
&& (this.usingLookingPriority ? (
- // has our block stayed the same (this also accounts for chunk change)?
- Mth.floor(this.lastLocX) == Mth.floor(posX)
+ // has our block stayed the same (this also accounts for chunk change)?
+ Mth.floor(this.lastLocX) == Mth.floor(posX)
&& Mth.floor(this.lastLocZ) == Mth.floor(posZ)
- ) : (
- // has our chunk stayed the same
- (Mth.floor(this.lastLocX) >> 4) == (Mth.floor(posX) >> 4)
+ ) : (
+ // has our chunk stayed the same
+ (Mth.floor(this.lastLocX) >> 4) == (Mth.floor(posX) >> 4)
&& (Mth.floor(this.lastLocZ) >> 4) == (Mth.floor(posZ) >> 4)
- ))
+ ))
// has our decision about look priority changed?
&& this.usingLookingPriority == useLookPriority
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..ec8982b89cde24eefd3d392eed3e6cc926053dff 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
@@ -6,8 +6,14 @@ import io.papermc.paper.util.CoordinateUtils;
import io.papermc.paper.util.TickThread;
import io.papermc.paper.util.WorldUtil;
import io.papermc.paper.world.ChunkEntitySlices;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceMaps;
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import net.minecraft.core.BlockPos;
import io.papermc.paper.chunk.system.ChunkSystem;
@@ -45,16 +51,16 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public final ServerLevel world;
- private final StampedLock stateLock = new StampedLock();
- protected final Long2ObjectOpenHashMap<ChunkSlicesRegion> regions = new Long2ObjectOpenHashMap<>(128, 0.5f);
+ private final StampedLock entityByLock = new StampedLock();
+ private final Object chunkEntitySlicesLock = new Object();
+
+ protected final Long2ObjectMap<ChunkSlicesRegion> regions = Long2ObjectMaps.synchronize(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 Int2ReferenceMap<Entity> entityById = new Int2ReferenceOpenHashMap<>();
+ private final Object2ReferenceMap<UUID, Entity> entityByUUID = new Object2ReferenceOpenHashMap<>();
private final EntityList accessibleEntities = new EntityList();
public EntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
@@ -105,7 +111,6 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
if (attempt != 0L) {
try {
final Entity ret = this.entityByUUID.get(id);
-
if (this.entityByLock.validate(attempt)) {
return maskNonAccessible(ret);
}
@@ -166,12 +171,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 +184,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
@Override
- public void remove() {
+ public synchronized void remove() {
throw new UnsupportedOperationException();
}
}
@@ -208,8 +213,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 +222,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 +233,50 @@ 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;
- }
-
+ final Boolean ticketBlockBefore = this.world.chunkTaskScheduler.chunkHolderManager.blockTicketUpdates();
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 (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.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 (oldVisibility.isAccessible() && !newVisibility.isAccessible()) {
+ this.accessibleEntities.remove(entity);
+ EntityLookup.this.worldCallback.onTrackingEnd(entity);
+ }
+ }
- if (moved && newVisibility.isAccessible()) {
- EntityLookup.this.worldCallback.onSectionChange(entity);
- }
+ if (moved && newVisibility.isAccessible()) {
+ EntityLookup.this.worldCallback.onSectionChange(entity);
+ }
- if (destroyed) {
- EntityLookup.this.worldCallback.onDestroyed(entity);
- }
- } finally {
- entity.updatingSectionStatus = false;
- }
- } finally {
- this.world.chunkTaskScheduler.chunkHolderManager.unblockTicketUpdates(ticketBlockBefore);
+ if (destroyed) {
+ EntityLookup.this.worldCallback.onDestroyed(entity);
}
} finally {
- if (slices != null) {
- slices.stopPreventingStatusUpdates(false);
- }
+ this.world.chunkTaskScheduler.chunkHolderManager.unblockTicketUpdates(ticketBlockBefore);
}
}
@@ -346,11 +326,6 @@ public final class EntityLookup implements LevelEntityGetter<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);
if (entity.isRemoved()) {
@@ -398,7 +373,10 @@ 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);
+ ChunkEntitySlices slices;
+ synchronized (this.chunkEntitySlicesLock){
+ 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 + ")");
@@ -441,7 +419,10 @@ 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);
+ ChunkEntitySlices old;
+ synchronized (this.chunkEntitySlicesLock){
+ old = this.getChunk(entity.sectionX, entity.sectionZ);
+ }
final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
if (!old.removeEntity(entity, entity.sectionY)) {
@@ -609,7 +590,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
continue;
}
- chunk.getEntities(type, box, (List)into, (Predicate)predicate);
+ chunk.getEntities(type, box, (List) into, (Predicate) predicate);
}
}
}
@@ -660,21 +641,24 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot load in entity section off-main");
synchronized (this) {
final ChunkEntitySlices curr = this.getChunk(chunkX, chunkZ);
- if (curr != null) {
- this.removeChunk(chunkX, chunkZ);
-
- curr.mergeInto(slices);
-
- this.addChunk(chunkX, chunkZ, slices);
- } else {
- this.addChunk(chunkX, chunkZ, slices);
+ synchronized (this.chunkEntitySlicesLock){
+ if (curr != null) {
+ this.removeChunk(chunkX, chunkZ);
+ curr.mergeInto(slices);
+ this.addChunk(chunkX, chunkZ, slices);
+ } else {
+ this.addChunk(chunkX, chunkZ, slices);
+ }
}
}
}
+
public void entitySectionUnload(final int chunkX, final int chunkZ) {
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot unload entity section off-main");
- this.removeChunk(chunkX, chunkZ);
+ synchronized (this.chunkEntitySlicesLock){
+ this.removeChunk(chunkX, chunkZ);
+ }
}
public ChunkEntitySlices getChunk(final int chunkX, final int chunkZ) {
@@ -699,27 +683,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public ChunkSlicesRegion getRegion(final int regionX, final int regionZ) {
final long key = CoordinateUtils.getChunkKey(regionX, regionZ);
- final long attempt = this.stateLock.tryOptimisticRead();
- if (attempt != 0L) {
- try {
- final ChunkSlicesRegion ret = this.regions.get(key);
-
- if (this.stateLock.validate(attempt)) {
- return ret;
- }
- } catch (final Error error) {
- throw error;
- } catch (final Throwable thr) {
- // ignore
- }
- }
-
- this.stateLock.readLock();
- try {
- return this.regions.get(key);
- } finally {
- this.stateLock.tryUnlockRead();
- }
+ return this.regions.get(key);
}
private synchronized void removeChunk(final int chunkX, final int chunkZ) {
@@ -730,12 +694,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
final int remaining = region.remove(relIndex);
if (remaining == 0) {
- this.stateLock.writeLock();
- try {
- this.regions.remove(key);
- } finally {
- this.stateLock.tryUnlockWrite();
- }
+ this.regions.remove(key);
}
}
@@ -749,12 +708,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
} else {
region = new ChunkSlicesRegion();
region.add(relIndex, slices);
- this.stateLock.writeLock();
- try {
- this.regions.put(key, region);
- } finally {
- this.stateLock.tryUnlockWrite();
- }
+ this.regions.put(key, region);
}
}
@@ -831,9 +785,11 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public static final NoOpCallback INSTANCE = new NoOpCallback();
@Override
- public void onMove() {}
+ public void onMove() {
+ }
@Override
- public void onRemove(final Entity.RemovalReason reason) {}
+ public void onRemove(final Entity.RemovalReason reason) {
+ }
}
}
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;

View File

@@ -0,0 +1,763 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 15 Jan 2023 09:54:18 +0800
Subject: [PATCH] Hearse: Paper world code changes
Original license: MIT
Original project: https://github.com/NaturalCodeClub/Hearse
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..e831738a2988746fe4e065f6ded811a8bdf5dabe 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,42 @@ 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);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
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);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
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);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
this.levelRemoveWorkQueueBitset |= (1L << level);
}
@@ -163,9 +181,20 @@ public final class Delayed26WayDistancePropagator3D {
this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex];
- while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirstLong();
- byte level = queue.queuedLevels.removeFirstByte();
+ while (true) {
+
+ long coordinate;
+ byte level;
+ final long id = queue.lock.writeLock();
+ try {
+ if (queue.queuedLevels.isEmpty()){
+ break;
+ }
+ coordinate = queue.queuedCoordinates.removeFirst();
+ level = queue.queuedLevels.removeFirst();
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
final boolean neighbourCheck = level < 0;
@@ -232,9 +261,19 @@ public final class Delayed26WayDistancePropagator3D {
this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[queueIndex];
- while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirstLong();
- final byte level = queue.queuedLevels.removeFirstByte();
+ while (true) {
+ long coordinate;
+ byte level;
+ final long id = queue.lock.writeLock();
+ try {
+ if (queue.queuedLevels.isEmpty()){
+ break;
+ }
+ coordinate = queue.queuedCoordinates.removeFirst();
+ level = queue.queuedLevels.removeFirst();
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
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..0fa95d81bafc7fe5c1bede7a0608b54795a78fa0 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,14 @@
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;
+import java.util.concurrent.locks.StampedLock;
public final class Delayed8WayDistancePropagator2D {
@@ -356,24 +358,42 @@ 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);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
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);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
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);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
this.levelRemoveWorkQueueBitset |= (1L << level);
}
@@ -425,9 +445,19 @@ public final class Delayed8WayDistancePropagator2D {
this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) {
final WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex];
- while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirstLong();
- byte level = queue.queuedLevels.removeFirstByte();
+ while (true) {
+ byte level;
+ long coordinate;
+ final long id = queue.lock.writeLock();
+ try {
+ if (queue.queuedLevels.isEmpty()){
+ break;
+ }
+ coordinate = queue.queuedCoordinates.removeFirst();
+ level = queue.queuedLevels.removeFirst();
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
final boolean neighbourCheck = level < 0;
@@ -491,9 +521,20 @@ public final class Delayed8WayDistancePropagator2D {
this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) {
final WorkQueue queue = this.levelRemoveWorkQueues[queueIndex];
- while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirstLong();
- final byte level = queue.queuedLevels.removeFirstByte();
+ while (true) {
+ long coordinate;
+ byte level;
+
+ final long id = queue.lock.writeLock();
+ try {
+ if (queue.queuedLevels.isEmpty()){
+ break;
+ }
+ coordinate = queue.queuedCoordinates.removeFirst();
+ level = queue.queuedLevels.removeFirst();
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level);
if (currentLevel == 0) {
@@ -678,41 +719,9 @@ public final class Delayed8WayDistancePropagator2D {
}
protected static final class WorkQueue {
-
- public final NoResizeLongArrayFIFODeque queuedCoordinates = new NoResizeLongArrayFIFODeque();
- public final NoResizeByteArrayFIFODeque queuedLevels = new NoResizeByteArrayFIFODeque();
-
+ public final Deque<Long> queuedCoordinates = new ConcurrentLinkedDeque<>();
+ public final Deque<Byte> queuedLevels = new ConcurrentLinkedDeque<>();
+ public final StampedLock lock = new StampedLock();
}
- 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;
- }
- }
-
- 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/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index f597d65d56964297eeeed6c7e77703764178fee0..b12c02962e9dad92ae79d762887c65db10765488 100644
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
@@ -2,8 +2,8 @@ package io.papermc.paper.world;
import com.destroystokyo.paper.util.maplist.EntityList;
import io.papermc.paper.chunk.system.entity.EntityLookup;
-import io.papermc.paper.util.TickThread;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
+import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ChunkHolder;
@@ -20,8 +20,8 @@ import net.minecraft.world.phys.AABB;
import org.bukkit.craftbukkit.event.CraftEventFactory;
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 {
@@ -31,15 +31,15 @@ public final class ChunkEntitySlices {
public final int chunkX;
public final int chunkZ;
protected final ServerLevel world;
-
+ protected final StampedLock accessLock = new StampedLock(); //Hearse -- fix some entity can't be removed
protected final EntityCollectionBySection allEntities;
protected final EntityCollectionBySection hardCollidingEntities;
- protected final Reference2ObjectOpenHashMap<Class<? extends Entity>, EntityCollectionBySection> entitiesByClass;
+ protected final Reference2ObjectMap<Class<? extends Entity>, EntityCollectionBySection> entitiesByClass;
protected final EntityList entities = new EntityList();
- public ChunkHolder.FullChunkStatus status;
+ public volatile ChunkHolder.FullChunkStatus status;
- protected boolean isTransient;
+ protected volatile boolean isTransient;
public boolean isTransient() {
return this.isTransient;
@@ -61,13 +61,12 @@ public final class ChunkEntitySlices {
this.allEntities = new EntityCollectionBySection(this);
this.hardCollidingEntities = new EntityCollectionBySection(this);
- this.entitiesByClass = new Reference2ObjectOpenHashMap<>();
+ this.entitiesByClass = Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>());
this.status = status;
}
- // Paper start - optimise CraftChunk#getEntities
- public org.bukkit.entity.Entity[] getChunkEntities() {
+ private org.bukkit.entity.Entity[] getChunkEntitiesUnsafe(){
List<org.bukkit.entity.Entity> ret = new java.util.ArrayList<>();
final Entity[] entities = this.entities.getRawData();
for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
@@ -80,11 +79,25 @@ public final class ChunkEntitySlices {
ret.add(bukkit);
}
}
-
return ret.toArray(new org.bukkit.entity.Entity[0]);
}
- public CompoundTag save() {
+ // Paper start - optimise CraftChunk#getEntities
+ public org.bukkit.entity.Entity[] getChunkEntities() {
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)) {
+ return this.getChunkEntitiesUnsafe();
+ }
+
+ id = this.accessLock.readLock();
+ try {
+ return this.getChunkEntitiesUnsafe();
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+ }
+
+ private CompoundTag saveUnsafe(){
final int len = this.entities.size();
if (len == 0) {
return null;
@@ -106,11 +119,36 @@ public final class ChunkEntitySlices {
return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world);
}
+ public CompoundTag save() {
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ return this.saveUnsafe();
+ }
+ id = this.accessLock.readLock();
+ try {
+ return this.saveUnsafe();
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+ }
+
// returns true if this chunk has transient entities remaining
public boolean unload() {
- final int len = this.entities.size();
- final Entity[] collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
-
+ Entity[] collectedEntities;
+ int len;
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ len = this.entities.size();
+ collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
+ }else {
+ id = this.accessLock.readLock();
+ try {
+ len = this.entities.size();
+ collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+ }
for (int i = 0; i < len; ++i) {
final Entity entity = collectedEntities[i];
if (entity.isRemoved()) {
@@ -128,7 +166,6 @@ public final class ChunkEntitySlices {
}
}
}
-
return this.entities.size() != 0;
}
@@ -140,53 +177,98 @@ public final class ChunkEntitySlices {
final Entity[] rawData = this.entities.getRawData();
final List<Entity> collectedEntities = new ArrayList<>(len);
- for (int i = 0; i < len; ++i) {
- collectedEntities.add(rawData[i]);
- }
+ collectedEntities.addAll(Arrays.asList(rawData).subList(0, len));
return collectedEntities;
}
public void callEntitiesLoadEvent() {
- CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ return;
+ }
+ id = this.accessLock.readLock();
+ try {
+ CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
public void callEntitiesUnloadEvent() {
- CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ return;
+ }
+ id = this.accessLock.readLock();
+ try {
+ CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
// Paper end - optimise CraftChunk#getEntities
public boolean isEmpty() {
- return this.entities.size() == 0;
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ return this.entities.size() == 0;
+ }
+ id = this.accessLock.readLock();
+ try {
+ return this.entities.size() == 0;
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
public void mergeInto(final ChunkEntitySlices slices) {
- final Entity[] entities = this.entities.getRawData();
- for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
- final Entity entity = entities[i];
+ final List<Entity> cop = new ArrayList<>();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ final Entity[] entities = this.entities.getRawData();
+ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
+ final Entity entity = entities[i];
+ cop.add(entity);
+ }
+ }else {
+ id = this.accessLock.readLock();
+ try {
+ final Entity[] entities = this.entities.getRawData();
+ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
+ final Entity entity = entities[i];
+ cop.add(entity);
+ }
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+ }
+ for (Entity entity : cop){
slices.addEntity(entity, entity.sectionY);
}
}
- 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;
- final Entity[] entities = this.entities.getRawData();
+ Entity[] entities;
+
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ entities = Arrays.copyOf(this.entities.getRawData(), this.entities.getRawData().length);
+ }else {
+ id = this.accessLock.readLock();
+ try {
+ entities = Arrays.copyOf(this.entities.getRawData(), this.entities.getRawData().length);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+ }
- for (int i = 0, size = this.entities.size(); i < size; ++i) {
- final Entity entity = entities[i];
+ for (final Entity entity : entities) {
final Visibility oldVisibility = EntityLookup.getEntityStatus(entity);
entity.chunkStatus = status;
final Visibility newVisibility = EntityLookup.getEntityStatus(entity);
@@ -196,70 +278,112 @@ public final class ChunkEntitySlices {
}
public boolean addEntity(final Entity entity, final int chunkSection) {
- if (!this.entities.add(entity)) {
- return false;
- }
- entity.chunkStatus = this.status;
- final int sectionIndex = chunkSection - this.minSection;
-
- this.allEntities.addEntity(entity, sectionIndex);
+ long id = this.accessLock.writeLock();
+ try {
+ if (!this.entities.add(entity)) {
+ return false;
+ }
+ entity.chunkStatus = this.status;
+ final int sectionIndex = chunkSection - this.minSection;
- if (entity.hardCollides()) {
- this.hardCollidingEntities.addEntity(entity, sectionIndex);
- }
+ this.allEntities.addEntity(entity, sectionIndex);
- for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
- final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
+ if (entity.hardCollides()) {
+ this.hardCollidingEntities.addEntity(entity, sectionIndex);
+ }
- if (entry.getKey().isInstance(entity)) {
- entry.getValue().addEntity(entity, sectionIndex);
+ for (final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry : this.entitiesByClass.reference2ObjectEntrySet()) {
+ if (entry.getKey().isInstance(entity)) {
+ entry.getValue().addEntity(entity, sectionIndex);
+ }
}
+ } finally {
+ this.accessLock.unlockWrite(id);
}
-
return true;
}
public boolean removeEntity(final Entity entity, final int chunkSection) {
- if (!this.entities.remove(entity)) {
- return false;
- }
- entity.chunkStatus = null;
- final int sectionIndex = chunkSection - this.minSection;
-
- this.allEntities.removeEntity(entity, sectionIndex);
+ long id = this.accessLock.writeLock();
+ try {
+ if (!this.entities.remove(entity)) {
+ return false;
+ }
+ entity.chunkStatus = null;
+ final int sectionIndex = chunkSection - this.minSection;
- if (entity.hardCollides()) {
- this.hardCollidingEntities.removeEntity(entity, sectionIndex);
- }
+ this.allEntities.removeEntity(entity, sectionIndex);
- for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
- final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
+ if (entity.hardCollides()) {
+ this.hardCollidingEntities.removeEntity(entity, sectionIndex);
+ }
- if (entry.getKey().isInstance(entity)) {
- entry.getValue().removeEntity(entity, sectionIndex);
+ for (final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry : this.entitiesByClass.reference2ObjectEntrySet()) {
+ if (entry.getKey().isInstance(entity)) {
+ entry.getValue().removeEntity(entity, sectionIndex);
+ }
}
+ } finally {
+ this.accessLock.unlockWrite(id);
}
-
return true;
}
public void getHardCollidingEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- this.hardCollidingEntities.getEntities(except, box, into, predicate);
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ this.hardCollidingEntities.getEntities(except, box, into, predicate);
+ return;
+ }
+ id = this.accessLock.readLock();
+ try {
+ this.hardCollidingEntities.getEntities(except, box, into, predicate);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate);
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate);
+ return;
+ }
+ id = this.accessLock.readLock();
+ try {
+ this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- this.allEntities.getEntities(except, box, into, predicate);
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ this.allEntities.getEntities(except, box, into, predicate);
+ return;
+ }
+ id = this.accessLock.readLock();
+ try {
+ this.allEntities.getEntities(except, box, into, predicate);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate) {
- this.allEntities.getEntities(type, box, (List)into, (Predicate)predicate);
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ this.allEntities.getEntities(type, box, (List) into, (Predicate) predicate);
+ return;
+ }
+ id = this.accessLock.readLock();
+ try {
+ this.allEntities.getEntities(type, box, (List) into, (Predicate) predicate);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
protected EntityCollectionBySection initClass(final Class<? extends Entity> clazz) {
@@ -287,12 +411,28 @@ public final class ChunkEntitySlices {
public <T extends Entity> void getEntities(final Class<? extends T> clazz, final Entity except, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate) {
- EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
- if (collection != null) {
- collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate);
- } else {
- this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz));
- collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate);
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
+ if (collection != null) {
+ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate);
+ } else {
+ this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz));
+ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate);
+ }
+ return;
+ }
+ id = this.accessLock.readLock();
+ try {
+ EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
+ if (collection != null) {
+ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate);
+ } else {
+ this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz));
+ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate);
+ }
+ } finally {
+ this.accessLock.unlockRead(id);
}
}
@@ -309,7 +449,7 @@ public final class ChunkEntitySlices {
}
public BasicEntityList(final int cap) {
- this.storage = (E[])(cap <= 0 ? EMPTY : new Entity[cap]);
+ this.storage = (E[]) (cap <= 0 ? EMPTY : new Entity[cap]);
}
public boolean isEmpty() {
@@ -322,7 +462,7 @@ public final class ChunkEntitySlices {
private void resize() {
if (this.storage == EMPTY) {
- this.storage = (E[])new Entity[DEFAULT_CAPACITY];
+ this.storage = (E[]) new Entity[DEFAULT_CAPACITY];
} else {
this.storage = Arrays.copyOf(this.storage, this.storage.length * 2);
}
@@ -492,7 +632,7 @@ public final class ChunkEntitySlices {
} // else: continue to test the ender dragon parts
if (entity instanceof EnderDragon) {
- for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) {
+ for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) {
if (part == except || !part.getBoundingBox().intersects(box)) {
continue;
}
@@ -543,7 +683,7 @@ public final class ChunkEntitySlices {
} // else: continue to test the ender dragon parts
if (entity instanceof EnderDragon) {
- for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) {
+ for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) {
if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) {
continue;
}
@@ -589,11 +729,11 @@ public final class ChunkEntitySlices {
continue;
}
- if (predicate != null && !predicate.test((T)entity)) {
+ if (predicate != null && !predicate.test((T) entity)) {
continue;
}
- into.add((T)entity);
+ into.add((T) entity);
}
}
}

View File

@@ -1,203 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 8 Jan 2023 21:14:07 +0800
Subject: [PATCH] Hearse: Pathfiner changes
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java b/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java
index 27b9cefc172b391824ead382a712b8b9b1ddfe45..4b65331a9192b7ac75141183493126ee730e697e 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java
@@ -4,9 +4,9 @@ public class BinaryHeap {
private Node[] heap = new Node[128];
private int size;
- public Node insert(Node node) {
+ public synchronized Node insert(Node node) {
if (node.heapIdx >= 0) {
- throw new IllegalStateException("OW KNOWS!");
+ return node;
} else {
if (this.size == this.heap.length) {
Node[] nodes = new Node[this.size << 1];
@@ -21,15 +21,15 @@ public class BinaryHeap {
}
}
- public void clear() {
+ public synchronized void clear() {
this.size = 0;
}
- public Node peek() {
+ public synchronized Node peek() {
return this.heap[0];
}
- public Node pop() {
+ public synchronized Node pop() {
Node node = this.heap[0];
this.heap[0] = this.heap[--this.size];
this.heap[this.size] = null;
@@ -41,7 +41,7 @@ public class BinaryHeap {
return node;
}
- public void remove(Node node) {
+ public synchronized void remove(Node node) {
this.heap[node.heapIdx] = this.heap[--this.size];
this.heap[this.size] = null;
if (this.size > node.heapIdx) {
@@ -55,7 +55,7 @@ public class BinaryHeap {
node.heapIdx = -1;
}
- public void changeCost(Node node, float weight) {
+ public synchronized void changeCost(Node node, float weight) {
float f = node.f;
node.f = weight;
if (weight < f) {
@@ -66,11 +66,14 @@ public class BinaryHeap {
}
- public int size() {
+ public synchronized int size() {
return this.size;
}
private void upHeap(int index) {
+ if(index == -1){
+ return;
+ }
Node node = this.heap[index];
int i;
@@ -90,6 +93,9 @@ public class BinaryHeap {
}
private void downHeap(int index) {
+ if(index == -1){
+ return;
+ }
Node node = this.heap[index];
float f = node.f;
@@ -135,11 +141,11 @@ public class BinaryHeap {
node.heapIdx = index;
}
- public boolean isEmpty() {
+ public synchronized boolean isEmpty() {
return this.size == 0;
}
- public Node[] getHeap() {
+ public synchronized Node[] getHeap() {
Node[] nodes = new Node[this.size()];
System.arraycopy(this.heap, 0, nodes, 0, this.size());
return nodes;
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java
index b0bae04ab5a93dd4cf1eeeb02bed1e508e1f2913..d427735eff0056c171591709829d0bb76f7bb6f3 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java
@@ -1,6 +1,7 @@
package net.minecraft.world.level.pathfinder;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.EnumSet;
import java.util.List;
@@ -15,7 +16,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
public class FlyNodeEvaluator extends WalkNodeEvaluator {
- private final Long2ObjectMap<BlockPathTypes> pathTypeByPosCache = new Long2ObjectOpenHashMap<>();
+ private final Long2ObjectMap<BlockPathTypes> pathTypeByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
private static final float SMALL_MOB_INFLATED_START_NODE_BOUNDING_BOX = 1.5F;
private static final int MAX_START_NODE_CANDIDATES = 10;
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java
index a8a2594b8f5b3ebf6a1f918c7d822ad35b051b17..c614bcfc2bbbbccc7c4aac9389d4780478e739d2 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java
@@ -1,6 +1,7 @@
package net.minecraft.world.level.pathfinder;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
@@ -11,7 +12,7 @@ import net.minecraft.world.level.PathNavigationRegion;
public abstract class NodeEvaluator {
protected PathNavigationRegion level;
protected Mob mob;
- protected final Int2ObjectMap<Node> nodes = new Int2ObjectOpenHashMap<>();
+ protected final Int2ObjectMap<Node> nodes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
protected int entityWidth;
protected int entityHeight;
protected int entityDepth;
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
index a8af51a25b0f99c3a64d9150fdfcd6b818aa7581..cd2592552339a79361d2a4e7936731330e15f6fa 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
@@ -31,7 +31,7 @@ public class PathFinder {
}
@Nullable
- public Path findPath(PathNavigationRegion world, Mob mob, Set<BlockPos> positions, float followRange, int distance, float rangeMultiplier) {
+ public synchronized Path findPath(PathNavigationRegion world, Mob mob, Set<BlockPos> positions, float followRange, int distance, float rangeMultiplier) {
this.openSet.clear();
this.nodeEvaluator.prepare(world, mob);
Node node = this.nodeEvaluator.getStart();
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java
index 6084631b5b502279b84f190dc62fc76b770e368e..f526adbd31e65fc74af48f6137d293a7a7ceafbb 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java
@@ -2,6 +2,7 @@ package net.minecraft.world.level.pathfinder;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.Map;
import javax.annotation.Nullable;
@@ -17,7 +18,7 @@ import net.minecraft.world.level.material.FluidState;
public class SwimNodeEvaluator extends NodeEvaluator {
private final boolean allowBreaching;
- private final Long2ObjectMap<BlockPathTypes> pathTypesByPosCache = new Long2ObjectOpenHashMap<>();
+ private final Long2ObjectMap<BlockPathTypes> pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
public SwimNodeEvaluator(boolean canJumpOutOfWater) {
this.allowBreaching = canJumpOutOfWater;
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
index 894881018c659d874f28f5744f0b8247cfecb1c1..ae06f7ef9c4b8147508984f8b46176de46171285 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
@@ -1,8 +1,10 @@
package net.minecraft.world.level.pathfinder;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMaps;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import java.util.EnumSet;
import javax.annotation.Nullable;
@@ -33,8 +35,8 @@ public class WalkNodeEvaluator extends NodeEvaluator {
public static final double SPACE_BETWEEN_WALL_POSTS = 0.5D;
private static final double DEFAULT_MOB_JUMP_HEIGHT = 1.125D;
protected float oldWaterCost;
- private final Long2ObjectMap<BlockPathTypes> pathTypesByPosCache = new Long2ObjectOpenHashMap<>();
- private final Object2BooleanMap<AABB> collisionCache = new Object2BooleanOpenHashMap<>();
+ private final Long2ObjectMap<BlockPathTypes> pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
+ private final Object2BooleanMap<AABB> collisionCache = Object2BooleanMaps.synchronize(new Object2BooleanOpenHashMap<>());
@Override
public void prepare(PathNavigationRegion cachedWorld, Mob entity) {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,475 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 15 Jan 2023 09:55:30 +0800
Subject: [PATCH] Hearse: Paper util code changes
Original license: MIT
Original project: https://github.com/NaturalCodeClub/Hearse
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/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/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java
index b3329c6fcd6758a781a51f5ba8f5052ac1c77b49..adb02cba6cdb62752f847136000c6f7ca857bd5a 100644
--- a/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java
+++ b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java
@@ -2,9 +2,6 @@ package com.destroystokyo.paper.util.set;
import java.util.Collection;
-/**
- * @author Spottedleaf <Spottedleaf@users.noreply.github.com>
- */
public final class OptimizedSmallEnumSet<E extends Enum<E>> {
private final Class<E> enumClass;
@@ -20,7 +17,7 @@ public final class OptimizedSmallEnumSet<E extends Enum<E>> {
this.enumClass = clazz;
}
- public boolean addUnchecked(final E element) {
+ public synchronized boolean addUnchecked(final E element) {
final int ordinal = element.ordinal();
final long key = 1L << ordinal;
@@ -30,7 +27,7 @@ public final class OptimizedSmallEnumSet<E extends Enum<E>> {
return (prev & key) == 0;
}
- public boolean removeUnchecked(final E element) {
+ public synchronized boolean removeUnchecked(final E element) {
final int ordinal = element.ordinal();
final long key = 1L << ordinal;
@@ -40,15 +37,15 @@ public final class OptimizedSmallEnumSet<E extends Enum<E>> {
return (prev & key) != 0;
}
- public void clear() {
+ public synchronized void clear() {
this.backingSet = 0L;
}
- public int size() {
+ public synchronized int size() {
return Long.bitCount(this.backingSet);
}
- public void addAllUnchecked(final Collection<E> enums) {
+ public synchronized void addAllUnchecked(final Collection<E> enums) {
for (final E element : enums) {
if (element == null) {
throw new NullPointerException("Null element");
@@ -57,15 +54,15 @@ public final class OptimizedSmallEnumSet<E extends Enum<E>> {
}
}
- public long getBackingSet() {
+ public synchronized long getBackingSet() {
return this.backingSet;
}
- public boolean hasCommonElements(final OptimizedSmallEnumSet<E> other) {
+ public synchronized boolean hasCommonElements(final OptimizedSmallEnumSet<E> other) {
return (other.backingSet & this.backingSet) != 0;
}
- public boolean hasElement(final E element) {
+ public synchronized boolean hasElement(final E element) {
return (this.backingSet & (1L << element.ordinal())) != 0;
}
}
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/util/maplist/IteratorSafeOrderedReferenceSet.java b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java
index 0fd814f1d65c111266a2b20f86561839a4cef755..fe4d76875462ac9d408c972b968647af78f2ed14 100644
--- a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java
+++ b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java
@@ -94,7 +94,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
return 1.0 - ((double)this.indexMap.size() / (double)this.listSize);
}
- public int createRawIterator() {
+ public synchronized int createRawIterator() {
if (this.allowSafeIteration()) {
++this.iteratorCount;
}
@@ -105,7 +105,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
}
- public int advanceRawIterator(final int index) {
+ public synchronized int advanceRawIterator(final int index) {
final E[] elements = this.listElements;
int ret = index + 1;
for (int len = this.listSize; ret < len; ++ret) {
@@ -117,7 +117,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
return -1;
}
- public void finishRawIterator() {
+ public synchronized void finishRawIterator() {
if (this.allowSafeIteration() && --this.iteratorCount == 0) {
if (this.getFragFactor() >= this.maxFragFactor) {
this.defrag();
@@ -125,7 +125,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
}
- public boolean remove(final E element) {
+ public synchronized boolean remove(final E element) {
final int index = this.indexMap.removeInt(element);
if (index >= 0) {
if (this.firstInvalidIndex < 0 || index < this.firstInvalidIndex) {
@@ -144,11 +144,11 @@ public final class IteratorSafeOrderedReferenceSet<E> {
return false;
}
- public boolean contains(final E element) {
+ public synchronized boolean contains(final E element) {
return this.indexMap.containsKey(element);
}
- public boolean add(final E element) {
+ public synchronized boolean add(final E element) {
final int listSize = this.listSize;
final int previous = this.indexMap.putIfAbsent(element, listSize);
@@ -223,30 +223,30 @@ public final class IteratorSafeOrderedReferenceSet<E> {
//this.check();
}
- public E rawGet(final int index) {
+ public synchronized E rawGet(final int index) {
return this.listElements[index];
}
- public int size() {
+ public synchronized int size() {
// always returns the correct amount - listSize can be different
return this.indexMap.size();
}
- public IteratorSafeOrderedReferenceSet.Iterator<E> iterator() {
+ public synchronized IteratorSafeOrderedReferenceSet.Iterator<E> iterator() {
return this.iterator(0);
}
- public IteratorSafeOrderedReferenceSet.Iterator<E> iterator(final int flags) {
+ public synchronized IteratorSafeOrderedReferenceSet.Iterator<E> iterator(final int flags) {
if (this.allowSafeIteration()) {
++this.iteratorCount;
}
return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize);
}
- public java.util.Iterator<E> unsafeIterator() {
+ public synchronized java.util.Iterator<E> unsafeIterator() {
return this.unsafeIterator(0);
}
- public java.util.Iterator<E> unsafeIterator(final int flags) {
+ public synchronized java.util.Iterator<E> unsafeIterator(final int flags) {
return new BaseIterator<>(this, false, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize);
}
@@ -273,7 +273,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
@Override
- public boolean hasNext() {
+ public synchronized boolean hasNext() {
if (this.finished) {
return false;
}
@@ -297,7 +297,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
@Override
- public E next() {
+ public synchronized E next() {
if (!this.hasNext()) {
throw new NoSuchElementException();
}
@@ -310,7 +310,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
@Override
- public void remove() {
+ public synchronized void remove() {
final E lastReturned = this.lastReturned;
if (lastReturned == null) {
throw new IllegalStateException();
@@ -320,7 +320,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
@Override
- public void finishedIterating() {
+ public synchronized void finishedIterating() {
if (this.finished || !this.canFinish) {
throw new IllegalStateException();
}

View File

@@ -0,0 +1,685 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 15 Jan 2023 09:56:15 +0800
Subject: [PATCH] Hearse: Base codes
Original license: MIT
Original project: https://github.com/NaturalCodeClub/Hearse
diff --git a/src/main/java/co/earthme/hearse/Hearse.java b/src/main/java/co/earthme/hearse/Hearse.java
new file mode 100644
index 0000000000000000000000000000000000000000..692fef51b2f15dd1ddc28773a381b9da3b42725e
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/Hearse.java
@@ -0,0 +1,27 @@
+package co.earthme.hearse;
+
+import co.earthme.hearse.commands.EntityCountCommand;
+import co.earthme.hearse.commands.WorkerCommand;
+import co.earthme.hearse.server.ServerEntityTickHook;
+import co.earthme.hearse.workers.WorkerThreadPoolManager;
+import net.minecraft.server.MinecraftServer;
+
+public class Hearse {
+ private static final WorkerThreadPoolManager workerManager = new WorkerThreadPoolManager();
+
+ public static void initAll(){
+ HearseConfig.init();
+ ServerEntityTickHook.init();
+ MinecraftServer.getServer().server.getCommandMap().register("workers","hearse",new WorkerCommand());
+ MinecraftServer.getServer().server.getCommandMap().register("entitycount","hearse",new EntityCountCommand());
+ }
+
+ public static void onServerStop(){
+ HearseConfig.save();
+ workerManager.shutdownAllNow();
+ }
+
+ public static WorkerThreadPoolManager getWorkerManager() {
+ return workerManager;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/HearseConfig.java b/src/main/java/co/earthme/hearse/HearseConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..73b5e76660b5162a7a0b327ddc7dcc3295b86699
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/HearseConfig.java
@@ -0,0 +1,49 @@
+package co.earthme.hearse;
+
+import org.bukkit.configuration.InvalidConfigurationException;
+import org.bukkit.configuration.file.YamlConfiguration;
+import java.io.File;
+import java.io.IOException;
+
+public class HearseConfig {
+ private static final YamlConfiguration configEntry = new YamlConfiguration();
+ private static final File CONFIG_FILE = new File("hearse.yml");
+
+ public static void init(){
+ try {
+ configEntry.load(CONFIG_FILE);
+ }catch (IOException ignored){
+ } catch (InvalidConfigurationException e) {
+ e.printStackTrace();
+ }
+ configEntry.options().copyDefaults(true);
+ }
+
+ public static void save(){
+ try {
+ configEntry.save(CONFIG_FILE);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static int getInt(String key,int def){
+ configEntry.addDefault(key,def);
+ return configEntry.getInt(key);
+ }
+
+ public static long getLong(String key,int def){
+ configEntry.addDefault(key,def);
+ return configEntry.getLong(key);
+ }
+
+ public static String getString(String key,String def){
+ configEntry.addDefault(key,def);
+ return configEntry.getString(key);
+ }
+
+ public static boolean getBoolean(String key,boolean def){
+ configEntry.addDefault(key,def);
+ return configEntry.getBoolean(key);
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/commands/EntityCountCommand.java b/src/main/java/co/earthme/hearse/commands/EntityCountCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..de759c808040058062078130b527e78215216ebb
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/commands/EntityCountCommand.java
@@ -0,0 +1,36 @@
+package co.earthme.hearse.commands;
+
+import com.google.common.collect.Maps;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import java.util.Map;
+
+public class EntityCountCommand extends Command {
+ public EntityCountCommand() {
+ super("entitycount");
+ }
+
+ @Override
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
+ final Map<String,Integer> counts = Maps.newHashMap();
+ for (ServerLevel level : MinecraftServer.getServer().getAllLevels()){
+ for (Entity entity : level.entityTickList.entities){
+ final String name = entity.getType().getName();
+ if (!counts.containsKey(name)){
+ counts.put(name,0);
+ }
+ counts.replace(name,counts.get(name)+1);
+ }
+ }
+ sender.sendMessage("Exists entity Counts:");
+ for (Map.Entry<String,Integer> entry : counts.entrySet()){
+ sender.sendMessage(ChatColor.BLUE+String.format("%s:%s",entry.getKey(),entry.getValue()));
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/commands/WorkerCommand.java b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a4a6869a7278beadd97af006f4b5fae578b83ed
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java
@@ -0,0 +1,72 @@
+package co.earthme.hearse.commands;
+
+import co.earthme.hearse.Hearse;
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WorkerCommand extends Command {
+ public WorkerCommand() {
+ super("workers");
+ this.setPermission("hearse.commands.workers");
+ this.setDescription("You can see or edit the server workers by using this command");
+ this.setUsage("/workers <status,setThreadCount,forceStop> <workername>");
+ }
+
+ @Override
+ public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
+ final List<String> ret = new ArrayList<>();
+ if (args.length == 1){
+ ret.add("status");
+ ret.add("setThreadCount");
+ ret.add("forceStop");
+ }
+ if (args.length == 2){
+ for (Map.Entry<String, WorkerThreadPoolExecutor> entry : Hearse.getWorkerManager().getManagedWorkers().entrySet()){
+ ret.add(entry.getKey());
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
+ if (args.length >= 2){
+ final String action = args[0];
+ final String workerName = args[1];
+ final WorkerThreadPoolExecutor searchedWorker = Hearse.getWorkerManager().getTargetWorker(workerName);
+ if (searchedWorker == null){
+ sender.sendMessage(ChatColor.RED+"Target worker not found!");
+ return true;
+ }
+ switch (action){
+ case "status":
+ sender.sendMessage(ChatColor.GREEN+"Worker: "+workerName+" Status:"+ searchedWorker);
+ break;
+ case "setThreadCount":
+ if (args.length == 3){
+ try {
+ searchedWorker.setCorePoolSize(Integer.parseInt(args[2]));
+ sender.sendMessage(ChatColor.GREEN+"Finished!");
+ }catch (NumberFormatException e){
+ sender.sendMessage(ChatColor.RED+"Please supply an integer!");
+ }
+ }else{
+ sender.sendMessage(ChatColor.RED+"Please supply an integer!");
+ }
+ break;
+ case "forceStop":
+ searchedWorker.shutdownNow();
+ sender.sendMessage(ChatColor.YELLOW+"Worker "+workerName+" has been stopped!");
+ break;
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..421d4926ac674b5eb12d9613ceb6d20185ea557d
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
@@ -0,0 +1,18 @@
+package co.earthme.hearse.concurrent;
+
+import io.papermc.paper.util.TickThread;
+
+public class WorkerThread extends TickThread {
+
+ public WorkerThread(String name) {
+ super(name);
+ }
+
+ public WorkerThread(Runnable run, String name) {
+ super(run, name);
+ }
+
+ public static boolean isWorker(){
+ return Thread.currentThread() instanceof WorkerThread;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e65b1eba68003a9f7ce5080d07a521817831ff48
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java
@@ -0,0 +1,5 @@
+package co.earthme.hearse.concurrent;
+
+public interface WorkerThreadFactory {
+ WorkerThread getNewThread(Runnable task);
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e010bf23c9fc26284212a4388172f5d7d5a4b99
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
@@ -0,0 +1,76 @@
+package co.earthme.hearse.concurrent;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Queue;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.LockSupport;
+
+public class WorkerThreadPoolExecutor extends ThreadPoolExecutor {
+ private final Queue<TaskEntry> taskEntries = new ConcurrentLinkedQueue<>();
+
+ public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull WorkerThreadFactory workerThreadFactory) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, workerThreadFactory::getNewThread);
+ }
+
+ public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull WorkerThreadFactory workerThreadFactory, @NotNull RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, workerThreadFactory::getNewThread, handler);
+ }
+
+ public int getCurrentNotProcessingTasks(){
+ return this.getQueue().size();
+ }
+
+ public void clearAllTasks(){
+ this.getQueue().clear();
+ }
+
+ public void executeWithSubTask(Runnable mainTask,Runnable subTask){
+ final TaskEntry wrapped = new TaskEntry(subTask,mainTask);
+ this.taskEntries.offer(wrapped);
+ this.execute(wrapped);
+ }
+
+ public void runAllSubTasks(){
+ TaskEntry task;
+ while ((task = this.taskEntries.poll())!=null){
+ while (!task.allRunned()){
+ LockSupport.parkNanos(this,10000000);
+ }
+ }
+ }
+
+ private static class TaskEntry implements Runnable{
+ private final Runnable mainTask;
+ private final Runnable subTask;
+ private volatile boolean mainTaskFinished = false;
+
+ public TaskEntry(Runnable subTask,Runnable mainTask){
+ this.subTask = subTask;
+ this.mainTask = mainTask;
+ }
+
+ public boolean allRunned(){
+ if (!this.mainTaskFinished){
+ return false;
+ }
+ try {
+ this.subTask.run();
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ @Override
+ public void run() {
+ try {
+ this.mainTask.run();
+ }catch(Exception e){
+ e.printStackTrace();
+ }finally {
+ this.mainTaskFinished = true;
+ }
+ }
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..03a29509821a17faac2dc8ab810a2693b03bfbc6
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
@@ -0,0 +1,42 @@
+package co.earthme.hearse.concurrent.threadfactory;
+
+import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.WorkerThreadFactory;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
+import net.minecraft.server.MinecraftServer;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class DefaultWorkerFactory implements WorkerThreadFactory {
+ private static final AtomicInteger poolId = new AtomicInteger();
+ private final AtomicInteger threadId = new AtomicInteger();
+ private final String bound;
+ private final List<Thread> createdThreads = ObjectLists.synchronize(new ObjectArrayList<>());
+
+ public DefaultWorkerFactory(String bound){
+ poolId.getAndIncrement();
+ this.bound = bound;
+ }
+
+ public List<Thread> getCreatedThreads() {
+ return this.createdThreads;
+ }
+
+ @Override
+ public WorkerThread getNewThread(Runnable task) {
+ final WorkerThread workerThread = new WorkerThread(()->{
+ try {
+ task.run();
+ }finally {
+ this.createdThreads.remove(Thread.currentThread());
+ }
+ },"pool-"+poolId.get()+"-worker-"+threadId.getAndIncrement()+"-bound-"+this.bound);
+ this.createdThreads.add(workerThread);
+ workerThread.setDaemon(true);
+ workerThread.setPriority(Thread.NORM_PRIORITY - 2);
+ workerThread.setContextClassLoader(MinecraftServer.class.getClassLoader());
+ return workerThread;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0e7a9cf79ddf00827daba0aa9c7a32fa76b0c7c
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -0,0 +1,102 @@
+package co.earthme.hearse.server;
+
+import co.earthme.hearse.Hearse;
+import co.earthme.hearse.HearseConfig;
+import co.earthme.hearse.concurrent.WorkerThreadFactory;
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.player.Player;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ServerEntityTickHook {
+ private static final Logger logger = LogManager.getLogger();
+ private static volatile boolean firstTick = false;
+ private static final WorkerThreadFactory defFactory = new DefaultWorkerFactory("entity");
+ private static final AtomicInteger threadId = new AtomicInteger();
+ private static WorkerThreadPoolExecutor worker;
+ private static boolean asyncEntityEnabled;
+
+ public static void executeAsyncTask(Runnable task){
+ if (!asyncEntityEnabled){
+ throw new RejectedExecutionException();
+ }
+ worker.execute(task);
+ }
+
+ public static void init(){
+ boolean asyncEntityEnabled1 = HearseConfig.getBoolean("optimizations.enable-async-entity",true);
+ final int workerCount = HearseConfig.getInt("workers.async-entity-worker-count",Runtime.getRuntime().availableProcessors());
+ if (asyncEntityEnabled1){
+ worker = new WorkerThreadPoolExecutor(
+ workerCount,
+ workerCount,
+ 0L,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ defFactory
+ );
+ Hearse.getWorkerManager().addWorker("entity",worker);
+ }
+ asyncEntityEnabled = asyncEntityEnabled1;
+ }
+
+ public static void executeAsyncTaskWithMainThreadCallback(Runnable task,Runnable callBack){
+ if (!asyncEntityEnabled){
+ throw new IllegalStateException();
+ }
+ worker.executeWithSubTask(task,callBack);
+ }
+
+ public static void callTickStart(){
+ if (!firstTick){
+ firstTick = true;
+ return;
+ }
+ if (!asyncEntityEnabled){
+ return;
+ }
+ worker.runAllSubTasks();
+ }
+
+ public static void callAsyncEntityTick(Entity entity, ServerLevel level){
+ MinecraftServer.getServer().executeMidTickTasks();
+ Runnable task = ()->{
+ entity.activatedPriorityReset = false;
+ if (!entity.isRemoved()) {
+ entity.checkDespawn();
+ Entity entity1 = entity.getVehicle();
+ if (entity1 != null) {
+ if (!entity1.isRemoved() && entity1.hasPassenger(entity)) {
+ return;
+ }
+ entity.stopRiding();
+ }
+ try {
+ level.tickNonPassenger(entity);
+ } catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) throw throwable;
+ level.getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(throwable.getMessage(), throwable)));
+ throwable.printStackTrace();
+ }
+ }
+ };
+ if (!asyncEntityEnabled){
+ task.run();
+ return;
+ }
+ try {
+ worker.execute(task);
+ }catch (RejectedExecutionException e){
+ logger.warn("Worker rejected our task.Falling back to sync entity updating");
+ asyncEntityEnabled = false;
+ }
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
new file mode 100644
index 0000000000000000000000000000000000000000..8085eb700d8e5c20ebb5bfeceb78198c6e973019
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
@@ -0,0 +1,78 @@
+package co.earthme.hearse.server;
+
+import co.earthme.hearse.Hearse;
+import co.earthme.hearse.HearseConfig;
+import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
+import net.minecraft.CrashReport;
+import net.minecraft.ReportedException;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.LockSupport;
+import java.util.function.BooleanSupplier;
+
+public class ServerLevelTickHook {
+ private static final DefaultWorkerFactory workerFactory = new DefaultWorkerFactory("world");
+ private static WorkerThreadPoolExecutor worker;
+ private static boolean enabledParaWorld;
+ private static volatile boolean inited = false;
+ private static final AtomicInteger activeTaskCount = new AtomicInteger();
+ private static final Logger logger = LogManager.getLogger();
+
+ public static void initWorker(){
+ enabledParaWorld = HearseConfig.getBoolean("optimizations.enableparallelworldtick",true);
+ if (enabledParaWorld){
+ worker = new WorkerThreadPoolExecutor(
+ MinecraftServer.getServer().levels.size(),
+ MinecraftServer.getServer().levels.size(),
+ Long.MAX_VALUE,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ workerFactory
+ );
+ worker.allowCoreThreadTimeOut(true);
+ worker.prestartAllCoreThreads();
+ Hearse.getWorkerManager().addWorker("world",worker);
+ for (Thread worker : workerFactory.getCreatedThreads()){
+ logger.warn("World worker name:{}.This can help you to slove the lag problems when you using parallel world ticking",worker.getName());
+ }
+ }
+ inited = true;
+ }
+
+ public static boolean isInited(){
+ return inited;
+ }
+
+ public static void callWorldTick(ServerLevel worldserver, BooleanSupplier shouldKeepTicking){
+ activeTaskCount.getAndIncrement();
+ worker.execute(()->{
+ try {
+ try {
+ worldserver.tick(shouldKeepTicking);
+ for (final io.papermc.paper.chunk.SingleThreadChunkRegionManager regionManager : worldserver.getChunkSource().chunkMap.regionManagers) {
+ regionManager.recalculateRegions();
+ }
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ worldserver.explosionDensityCache.clear();
+ }finally {
+ activeTaskCount.getAndDecrement();
+ }
+ });
+ }
+
+ public static void awaitWorldTicKTasks(){
+ while (activeTaskCount.get() > 0){
+ LockSupport.parkNanos("Await world ticking",1000000);
+ }
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..527dba288e1988773fd5a89f076f92084034f421
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
@@ -0,0 +1,68 @@
+package co.earthme.hearse.workers;
+
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class WorkerThreadPoolManager {
+ private final Map<String,WorkerThreadPoolExecutor> managedWorkers = Maps.newConcurrentMap();
+
+ public void addWorker(String bound,WorkerThreadPoolExecutor worker){
+ this.managedWorkers.put(bound,worker);
+ }
+
+ public void shutdownAll() throws InterruptedException {
+ for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){
+ if (!worker.isShutdown()){
+ worker.getQueue().clear(); //Clear the tasks.We don't need wait them
+ worker.shutdown();
+ while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {}
+ }
+ }
+ }
+
+ @Deprecated
+ public Map<String, WorkerThreadPoolExecutor> getManagedWorkers() {
+ return Maps.newHashMap(this.managedWorkers);
+ }
+
+ @Deprecated
+ public WorkerThreadPoolExecutor getTargetWorker(String bound){
+ return this.managedWorkers.get(bound);
+ }
+
+ public Map<String,List<Runnable>> shutdownAllNow(){
+ final Map<String,List<Runnable>> ret = Maps.newHashMap();
+ for (Map.Entry<String,WorkerThreadPoolExecutor> entry : this.managedWorkers.entrySet()){
+ final String workerName = entry.getKey();
+ final WorkerThreadPoolExecutor worker = entry.getValue();
+ if (!worker.isShutdown()){
+ try {
+ final List<Runnable> taskNotRunned = worker.shutdownNow();
+ ret.put(workerName,taskNotRunned);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+ }
+ return ret;
+ }
+
+ public void shutdownAll(long singleWorkerAwaitTimeOutCount) throws InterruptedException {
+ long counter = singleWorkerAwaitTimeOutCount;
+ for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){
+ if (!worker.isShutdown()){
+ worker.shutdown();
+ while (worker.awaitTermination(1, TimeUnit.MILLISECONDS)) {
+ if (counter == 0){
+ break;
+ }
+ counter--;
+ }
+ counter = singleWorkerAwaitTimeOutCount;
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
index f7e8b6e1872a397c96afc938754726b0d4e493b4..2448673ee847fe3bc05f1269737aae5b43ae8291 100644
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
@@ -308,6 +308,16 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
return (EntityType) Registry.register(BuiltInRegistries.ENTITY_TYPE, id, (EntityType<T>) type.build(id)); // CraftBukkit - decompile error
}
+ // Purpur start
+ public static EntityType<?> getFromBukkitType(org.bukkit.entity.EntityType bukkitType) {
+ return getFromKey(new ResourceLocation(bukkitType.getKey().toString()));
+ }
+
+ public static EntityType<?> getFromKey(ResourceLocation location) {
+ return BuiltInRegistries.ENTITY_TYPE.get(location);
+ }
+ // Purpur end
+
public static ResourceLocation getKey(EntityType<?> type) {
return BuiltInRegistries.ENTITY_TYPE.getKey(type);
}
@@ -522,6 +532,16 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
return this.category;
}
+ // Purpur start
+ public String getName() {
+ return BuiltInRegistries.ENTITY_TYPE.getKey(this).getPath();
+ }
+
+ public String getTranslatedName() {
+ return getDescription().getString();
+ }
+ // Purpur end
+
public String getDescriptionId() {
if (this.descriptionId == null) {
this.descriptionId = Util.makeDescriptionId("entity", BuiltInRegistries.ENTITY_TYPE.getKey(this));

View File

@@ -1,386 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 8 Jan 2023 21:59:47 +0800
Subject: [PATCH] Hearse: MC code changes 2
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 03b8ef3409fd5f7a4d4b06e13cf8eb22b3bbf8a1..3d99330160a3ba0715394d0c88533bc4a79fe3a2 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -3,10 +3,12 @@ package net.minecraft.server.level;
import com.google.common.annotations.VisibleForTesting;
import co.aikar.timings.TimingHistory; // Paper
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.util.Pair;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.longs.LongSets;
@@ -21,15 +23,8 @@ import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executor;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
@@ -204,7 +199,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
final Set<Mob> navigatingMobs;
volatile boolean isUpdatingNavigations;
protected final Raids raids;
- private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents;
+ private final Deque<BlockEventData> blockEvents;
private final List<BlockEventData> blockEventsToReschedule;
private boolean handlingTick;
private final List<CustomSpawner> customSpawners;
@@ -536,10 +531,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
this.entityTickList = new EntityTickList();
this.blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
this.fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
- this.navigatingMobs = new ObjectOpenHashSet();
- this.blockEvents = new ObjectLinkedOpenHashSet();
- this.blockEventsToReschedule = new ArrayList(64);
- this.dragonParts = new Int2ObjectOpenHashMap();
+ this.navigatingMobs = Sets.newConcurrentHashSet();
+ this.blockEvents = new ConcurrentLinkedDeque<>();
+ this.blockEventsToReschedule = Collections.synchronizedList(new ArrayList(64));
+ this.dragonParts = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap());
this.tickTime = flag1;
this.server = minecraftserver;
this.customSpawners = list;
@@ -1777,10 +1772,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
private void runBlockEvents() {
this.blockEventsToReschedule.clear();
-
- while (!this.blockEvents.isEmpty()) {
- BlockEventData blockactiondata = (BlockEventData) this.blockEvents.removeFirst();
-
+ BlockEventData blockactiondata;
+ while ((blockactiondata = (BlockEventData) this.blockEvents.pollFirst())!=null) {
if (this.shouldTickBlocksAt(blockactiondata.pos())) {
if (this.doBlockEvent(blockactiondata)) {
this.server.getPlayerList().broadcast((Player) null, (double) blockactiondata.pos().getX(), (double) blockactiondata.pos().getY(), (double) blockactiondata.pos().getZ(), 64.0D, this.dimension(), new ClientboundBlockEventPacket(blockactiondata.pos(), blockactiondata.block(), blockactiondata.paramA(), blockactiondata.paramB()));
diff --git a/src/main/java/net/minecraft/util/ThreadingDetector.java b/src/main/java/net/minecraft/util/ThreadingDetector.java
index b6e98aaebe57453b8eceaa633a989aa24409830f..60162cccf765800c6172d1544f2cd9bcf30cbd97 100644
--- a/src/main/java/net/minecraft/util/ThreadingDetector.java
+++ b/src/main/java/net/minecraft/util/ThreadingDetector.java
@@ -17,7 +17,7 @@ import org.slf4j.Logger;
public class ThreadingDetector {
private static final Logger LOGGER = LogUtils.getLogger();
private final String name;
- private final Semaphore lock = new Semaphore(1);
+ private final Semaphore lock = new Semaphore(255);
private final Lock stackTraceLock = new ReentrantLock();
@Nullable
private volatile Thread threadThatFailedToAcquire;
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 d3827215ef19f6e1e63f846d91ed00525a318c7a..80215972538d5cfce5f224253ea0e34ea4fd45a4 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
@@ -40,7 +40,7 @@ public class LongJumpToRandomPos<E extends Mob> extends Behavior<E> {
protected final int maxLongJumpHeight;
protected final int maxLongJumpWidth;
protected final float maxJumpVelocity;
- protected List<LongJumpToRandomPos.PossibleJump> jumpCandidates = Lists.newArrayList();
+ protected List<LongJumpToRandomPos.PossibleJump> jumpCandidates = Lists.newCopyOnWriteArrayList();
protected Optional<Vec3> initialPosition = Optional.empty();
@Nullable
protected Vec3 chosenJump;
diff --git a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
index 878a42695ecedf0c3f2e6310e3ce44c6b6c36858..0c308e12f9b590fa169babac487c8adc7e3f823c 100644
--- a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
+++ b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
@@ -2,18 +2,17 @@ package net.minecraft.world.level.gameevent;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
+
+import java.util.*;
+
import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.phys.Vec3;
public class EuclideanGameEventListenerRegistry implements GameEventListenerRegistry {
- private final List<GameEventListener> listeners = Lists.newArrayList();
- private final Set<GameEventListener> listenersToRemove = Sets.newHashSet();
- private final List<GameEventListener> listenersToAdd = Lists.newArrayList();
+ private final List<GameEventListener> listeners = Collections.synchronizedList(Lists.newArrayList());
+ private final Set<GameEventListener> listenersToRemove = Sets.newConcurrentHashSet();
+ private final List<GameEventListener> listenersToAdd = Lists.newCopyOnWriteArrayList();
private boolean processing;
private final ServerLevel level;
diff --git a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java
index ac807277a6b26d140ea9873d17c7aa4fb5fe37b2..4c75f50ab0184637b72e08936ff8808ad6c6fb5f 100644
--- a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java
+++ b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java
@@ -13,6 +13,8 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.annotation.Nullable;
+
+import it.unimi.dsi.fastutil.objects.ObjectSets;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.ListTag;
import net.minecraft.world.level.ChunkPos;
@@ -21,7 +23,7 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
private final Queue<ScheduledTick<T>> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER);
@Nullable
private List<SavedTick<T>> pendingTicks;
- private final Set<ScheduledTick<?>> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH);
+ private final Set<ScheduledTick<?>> ticksPerPosition = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH));
@Nullable
private BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> onTickAdded;
@@ -29,11 +31,11 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
private boolean dirty;
private long lastSaved = Long.MIN_VALUE;
- public boolean isDirty(final long tick) {
+ public synchronized boolean isDirty(final long tick) {
return this.dirty || (!this.tickQueue.isEmpty() && tick != this.lastSaved);
}
- public void clearDirty() {
+ public synchronized void clearDirty() {
this.dirty = false;
}
// Paper end - add dirty flag
@@ -50,17 +52,17 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
}
- public void setOnTickAdded(@Nullable BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> tickConsumer) {
+ public synchronized void setOnTickAdded(@Nullable BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> tickConsumer) {
this.onTickAdded = tickConsumer;
}
@Nullable
- public ScheduledTick<T> peek() {
+ public synchronized ScheduledTick<T> peek() {
return this.tickQueue.peek();
}
@Nullable
- public ScheduledTick<T> poll() {
+ public synchronized ScheduledTick<T> poll() {
ScheduledTick<T> scheduledTick = this.tickQueue.poll();
if (scheduledTick != null) {
this.dirty = true; // Paper - add dirty flag
@@ -71,7 +73,7 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
}
@Override
- public void schedule(ScheduledTick<T> orderedTick) {
+ public synchronized void schedule(ScheduledTick<T> orderedTick) {
if (this.ticksPerPosition.add(orderedTick)) {
this.dirty = true; // Paper - add dirty flag
this.scheduleUnchecked(orderedTick);
@@ -88,11 +90,11 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
}
@Override
- public boolean hasScheduledTick(BlockPos pos, T type) {
+ public synchronized boolean hasScheduledTick(BlockPos pos, T type) {
return this.ticksPerPosition.contains(ScheduledTick.probe(type, pos));
}
- public void removeIf(Predicate<ScheduledTick<T>> predicate) {
+ public synchronized void removeIf(Predicate<ScheduledTick<T>> predicate) {
Iterator<ScheduledTick<T>> iterator = this.tickQueue.iterator();
while(iterator.hasNext()) {
@@ -105,17 +107,17 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
}
- public Stream<ScheduledTick<T>> getAll() {
+ public synchronized Stream<ScheduledTick<T>> getAll() {
return this.tickQueue.stream();
}
@Override
- public int count() {
+ public synchronized int count() {
return this.tickQueue.size() + (this.pendingTicks != null ? this.pendingTicks.size() : 0);
}
@Override
- public ListTag save(long l, Function<T, String> function) {
+ public synchronized ListTag save(long l, Function<T, String> function) {
this.lastSaved = l; // Paper - add dirty system to level ticks
ListTag listTag = new ListTag();
if (this.pendingTicks != null) {
@@ -131,7 +133,7 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
return listTag;
}
- public void unpack(long time) {
+ public synchronized void unpack(long time) {
if (this.pendingTicks != null) {
// Paper start - add dirty system to level chunk ticks
if (this.tickQueue.isEmpty()) {
diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java
index 5dea8414964e0d2d1fb15a6baa27227e9722bfc7..2203adc2a68e7fb253e353098fd6ddad521e3a32 100644
--- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java
+++ b/src/main/java/net/minecraft/world/ticks/LevelTicks.java
@@ -1,24 +1,9 @@
package net.minecraft.world.ticks;
-import it.unimi.dsi.fastutil.longs.Long2LongMap;
-import it.unimi.dsi.fastutil.longs.Long2LongMaps;
-import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
-import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
-import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.LongSummaryStatistics;
-import java.util.PriorityQueue;
-import java.util.Queue;
-import java.util.Set;
-import java.util.function.BiConsumer;
-import java.util.function.LongPredicate;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
+import it.unimi.dsi.fastutil.objects.ObjectSets;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
@@ -26,31 +11,36 @@ import net.minecraft.core.Vec3i;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.BiConsumer;
+import java.util.function.LongPredicate;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
public class LevelTicks<T> implements LevelTickAccess<T> {
private static final Comparator<LevelChunkTicks<?>> CONTAINER_DRAIN_ORDER = (a, b) -> {
return ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek());
};
private final LongPredicate tickCheck;
- private final Supplier<ProfilerFiller> profiler;
- private final Long2ObjectMap<LevelChunkTicks<T>> allContainers = new Long2ObjectOpenHashMap<>();
+ private final Long2ObjectMap<LevelChunkTicks<T>> allContainers = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), (map) -> {
map.defaultReturnValue(Long.MAX_VALUE);
});
private final Queue<LevelChunkTicks<T>> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER);
- private final Queue<ScheduledTick<T>> toRunThisTick = new ArrayDeque<>();
- private final List<ScheduledTick<T>> alreadyRunThisTick = new ArrayList<>();
- private final Set<ScheduledTick<?>> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH);
+ private final Queue<ScheduledTick<T>> toRunThisTick = new ConcurrentLinkedDeque<>();
+ private final List<ScheduledTick<T>> alreadyRunThisTick = new CopyOnWriteArrayList<>();
+ private final Set<ScheduledTick<?>> toRunThisTickSet = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH));
+
private final BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> chunkScheduleUpdater = (chunkTickScheduler, tick) -> {
if (tick.equals(chunkTickScheduler.peek())) {
this.updateContainerScheduling(tick);
}
-
};
public LevelTicks(LongPredicate tickingFutureReadyPredicate, Supplier<ProfilerFiller> profilerGetter) {
this.tickCheck = tickingFutureReadyPredicate;
- this.profiler = profilerGetter;
}
public void addContainer(ChunkPos pos, LevelChunkTicks<T> scheduler) {
@@ -123,7 +113,9 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
entry.setValue(scheduledTick.triggerTick());
} else if (this.tickCheck.test(l)) {
objectIterator.remove();
- this.containersToTick.add(levelChunkTicks);
+ synchronized (this.containersToTick){
+ this.containersToTick.add(levelChunkTicks);
+ }
}
}
}
@@ -133,27 +125,29 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
private void drainContainers(long time, int maxTicks) {
LevelChunkTicks<T> levelChunkTicks;
- while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) {
- ScheduledTick<T> scheduledTick = levelChunkTicks.poll();
- this.scheduleForThisTick(scheduledTick);
- this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks);
- ScheduledTick<T> scheduledTick2 = levelChunkTicks.peek();
- if (scheduledTick2 != null) {
- if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) {
- this.containersToTick.add(levelChunkTicks);
- } else {
- this.updateContainerScheduling(scheduledTick2);
+ synchronized (this.containersToTick){
+ while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) {
+ ScheduledTick<T> scheduledTick = levelChunkTicks.poll();
+ this.scheduleForThisTick(scheduledTick);
+ this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks);
+ ScheduledTick<T> scheduledTick2 = levelChunkTicks.peek();
+ if (scheduledTick2 != null) {
+ if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) {
+ this.containersToTick.add(levelChunkTicks);
+ } else {
+ this.updateContainerScheduling(scheduledTick2);
+ }
}
}
}
-
}
private void rescheduleLeftoverContainers() {
- for(LevelChunkTicks<T> levelChunkTicks : this.containersToTick) {
- this.updateContainerScheduling(levelChunkTicks.peek());
+ synchronized (this.containersToTick){
+ for(LevelChunkTicks<T> levelChunkTicks : this.containersToTick) {
+ this.updateContainerScheduling(levelChunkTicks.peek());
+ }
}
-
}
private void updateContainerScheduling(ScheduledTick<T> tick) {
@@ -201,7 +195,9 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
private void cleanupAfterTick() {
this.toRunThisTick.clear();
- this.containersToTick.clear();
+ synchronized (this.containersToTick){
+ this.containersToTick.clear();
+ }
this.alreadyRunThisTick.clear();
this.toRunThisTickSet.clear();
}

View File

@@ -1,394 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 8 Jan 2023 22:12:06 +0800
Subject: [PATCH] Hearse: Add base codes
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/HearseConfig.java b/src/main/java/co/earthme/hearse/HearseConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..912da4787f83f656da67e9533b60183c17e6c345
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/HearseConfig.java
@@ -0,0 +1,4 @@
+package co.earthme.hearse;
+
+public class HearseConfig {
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java b/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java
new file mode 100644
index 0000000000000000000000000000000000000000..3fbc81cb880cf6d38bb4c940b4cc1fa828c2ef17
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java
@@ -0,0 +1,76 @@
+package co.earthme.hearse.concurrent;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Queue;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.LockSupport;
+
+public class ThreadPool extends ThreadPoolExecutor {
+ public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
+ }
+
+ public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull ThreadFactory threadFactory) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
+ }
+
+ public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
+ }
+
+ public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull ThreadFactory threadFactory, @NotNull RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
+ }
+
+ private final Queue<TaskEntry> taskEntries = new ConcurrentLinkedQueue<>();
+
+ public void executeWithSubTask(Runnable mainTask,Runnable subTask){
+ final TaskEntry wrapped = new TaskEntry(subTask,mainTask);
+ this.taskEntries.offer(wrapped);
+ this.execute(wrapped);
+ }
+
+ public void runAllSubTasks(){
+ TaskEntry task;
+ while ((task = this.taskEntries.poll())!=null){
+ while (!task.allRunned()){
+ LockSupport.parkNanos(this,10000000);
+ }
+ }
+ }
+
+ private static class TaskEntry implements Runnable{
+ private final Runnable mainTask;
+ private final Runnable subTask;
+ private volatile boolean mainTaskFinished = false;
+
+ public TaskEntry(Runnable subTask,Runnable mainTask){
+ this.subTask = subTask;
+ this.mainTask = mainTask;
+ }
+
+ public boolean allRunned(){
+ if (!this.mainTaskFinished){
+ return false;
+ }
+ try {
+ this.subTask.run();
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ @Override
+ public void run() {
+ try {
+ this.mainTask.run();
+ }catch(Exception e){
+ e.printStackTrace();
+ }finally {
+ this.mainTaskFinished = true;
+ }
+ }
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..06f55f26eb63e356b3558622bf68711f18cda1c6
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
@@ -0,0 +1,13 @@
+package co.earthme.hearse.concurrent;
+
+import io.papermc.paper.util.TickThread;
+
+public class WorkerThread extends TickThread {
+ public WorkerThread(String name) {
+ super(name);
+ }
+
+ public static boolean isWorker(){
+ return Thread.currentThread() instanceof WorkerThread;
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 3d99330160a3ba0715394d0c88533bc4a79fe3a2..30cde5b9cd8d179f4b332563f2999f675851fbd1 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1,64 +1,33 @@
package net.minecraft.server.level;
+import co.aikar.timings.TimingHistory;
import com.google.common.annotations.VisibleForTesting;
-import co.aikar.timings.TimingHistory; // Paper
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.util.Pair;
import com.mojang.logging.LogUtils;
+import io.papermc.paper.util.MCUtil;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.longs.LongSets;
import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
-import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
-import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.*;
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.Executor;
-import java.util.function.BooleanSupplier;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import net.minecraft.CrashReport;
import net.minecraft.Util;
-import net.minecraft.core.BlockPos;
-import net.minecraft.core.Direction;
-import net.minecraft.core.Holder;
-import net.minecraft.core.HolderSet;
-import net.minecraft.core.RegistryAccess;
-import net.minecraft.core.SectionPos;
+import net.minecraft.core.*;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
-import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.protocol.Packet;
-import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
-import net.minecraft.network.protocol.game.ClientboundBlockEventPacket;
-import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
-import net.minecraft.network.protocol.game.ClientboundExplodePacket;
-import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
-import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
-import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket;
-import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket;
-import net.minecraft.network.protocol.game.ClientboundSoundPacket;
-import net.minecraft.network.protocol.game.DebugPackets;
+import net.minecraft.network.protocol.game.*;
import net.minecraft.resources.ResourceKey;
-import io.papermc.paper.util.MCUtil;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerScoreboard;
import net.minecraft.server.level.progress.ChunkProgressListener;
@@ -66,21 +35,10 @@ import net.minecraft.server.players.SleepStatus;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.TagKey;
-import net.minecraft.util.AbortableIterationConsumer;
-import net.minecraft.util.CsvOutput;
-import net.minecraft.util.Mth;
-import net.minecraft.util.ProgressListener;
-import net.minecraft.util.Unit;
-import net.minecraft.util.profiling.ProfilerFiller;
+import net.minecraft.util.*;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.damagesource.DamageSource;
-import net.minecraft.world.entity.Entity;
-import net.minecraft.world.entity.EntityType;
-import net.minecraft.world.entity.LightningBolt;
-import net.minecraft.world.entity.LivingEntity;
-import net.minecraft.world.entity.Mob;
-import net.minecraft.world.entity.MobCategory;
-import net.minecraft.world.entity.ReputationEventHandler;
+import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.ai.village.ReputationEventType;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
@@ -97,17 +55,7 @@ import net.minecraft.world.entity.raid.Raid;
import net.minecraft.world.entity.raid.Raids;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.item.crafting.RecipeManager;
-import net.minecraft.world.level.BlockEventData;
-import net.minecraft.world.level.ChunkPos;
-import net.minecraft.world.level.CustomSpawner;
-import net.minecraft.world.level.Explosion;
-import net.minecraft.world.level.ExplosionDamageCalculator;
-import net.minecraft.world.level.ForcedChunksSavedData;
-import net.minecraft.world.level.GameRules;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.level.NaturalSpawner;
-import net.minecraft.world.level.StructureManager;
-import net.minecraft.world.level.WorldGenLevel;
+import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.Block;
@@ -123,12 +71,10 @@ import net.minecraft.world.level.chunk.storage.EntityStorage;
import net.minecraft.world.level.dimension.BuiltinDimensionTypes;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.dimension.end.EndDragonFight;
-import net.minecraft.world.level.entity.EntityPersistentStorage;
import net.minecraft.world.level.entity.EntityTickList;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.entity.LevelCallback;
import net.minecraft.world.level.entity.LevelEntityGetter;
-import net.minecraft.world.level.entity.PersistentEntitySectionManager;
import net.minecraft.world.level.gameevent.DynamicGameEventListener;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.gameevent.GameEventDispatcher;
@@ -153,21 +99,32 @@ import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.ticks.LevelTicks;
-import org.slf4j.Logger;
import org.bukkit.Bukkit;
-import org.bukkit.Location;
import org.bukkit.WeatherType;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.craftbukkit.generator.CustomWorldChunkManager;
-import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.util.WorldUUID;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.server.MapInitializeEvent;
import org.bukkit.event.weather.LightningStrikeEvent;
-import org.bukkit.event.world.GenericGameEvent;
import org.bukkit.event.world.TimeSkipEvent;
-// CraftBukkit end
-import it.unimi.dsi.fastutil.ints.IntArrayList; // Paper
+import org.slf4j.Logger;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.Executor;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class ServerLevel extends Level implements WorldGenLevel {
@@ -988,7 +945,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
if (this.canSleepThroughNights()) {
if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) {
int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
- MutableComponent ichatmutablecomponent;
+ Component ichatmutablecomponent;
if (this.sleepStatus.areEnoughSleeping(i)) {
ichatmutablecomponent = Component.translatable("sleep.skipping_night");
@@ -1656,56 +1613,56 @@ public class ServerLevel extends Level implements WorldGenLevel {
@Override
public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) {
- if (this.isUpdatingNavigations) {
- String s = "recursive call to sendBlockUpdated";
+ synchronized (this.navigatingMobs){
+ if (this.isUpdatingNavigations) {
+ return;
+ }
- Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated"));
- }
+ this.getChunkSource().blockChanged(pos);
+ if(this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates
+ VoxelShape voxelshape = oldState.getCollisionShape(this, pos);
+ VoxelShape voxelshape1 = newState.getCollisionShape(this, pos);
- this.getChunkSource().blockChanged(pos);
- if(this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates
- VoxelShape voxelshape = oldState.getCollisionShape(this, pos);
- VoxelShape voxelshape1 = newState.getCollisionShape(this, pos);
+ if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) {
+ List<PathNavigation> list = new ObjectArrayList();
+ Iterator iterator = this.navigatingMobs.iterator();
- if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) {
- List<PathNavigation> list = new ObjectArrayList();
- Iterator iterator = this.navigatingMobs.iterator();
+ while (iterator.hasNext()) {
+ // CraftBukkit start - fix SPIGOT-6362
+ Mob entityinsentient;
+ try {
+ entityinsentient = (Mob) iterator.next();
+ } catch (java.util.ConcurrentModificationException ex) {
+ // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register
+ // In this case we just run the update again across all the iterators as the chunk will then be loaded
+ // As this is a relative edge case it is much faster than copying navigators (on either read or write)
+ this.sendBlockUpdated(pos, oldState, newState, flags);
+ return;
+ }
+ // CraftBukkit end
+ PathNavigation navigationabstract = entityinsentient.getNavigation();
- while (iterator.hasNext()) {
- // CraftBukkit start - fix SPIGOT-6362
- Mob entityinsentient;
- try {
- entityinsentient = (Mob) iterator.next();
- } catch (java.util.ConcurrentModificationException ex) {
- // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register
- // In this case we just run the update again across all the iterators as the chunk will then be loaded
- // As this is a relative edge case it is much faster than copying navigators (on either read or write)
- this.sendBlockUpdated(pos, oldState, newState, flags);
- return;
- }
- // CraftBukkit end
- PathNavigation navigationabstract = entityinsentient.getNavigation();
+ if (navigationabstract.shouldRecomputePath(pos)) {
+ list.add(navigationabstract);
+ }
+ }
- if (navigationabstract.shouldRecomputePath(pos)) {
- list.add(navigationabstract);
- }
- }
+ try {
+ this.isUpdatingNavigations = true;
+ iterator = list.iterator();
- try {
- this.isUpdatingNavigations = true;
- iterator = list.iterator();
+ while (iterator.hasNext()) {
+ PathNavigation navigationabstract1 = (PathNavigation) iterator.next();
- while (iterator.hasNext()) {
- PathNavigation navigationabstract1 = (PathNavigation) iterator.next();
+ navigationabstract1.recomputePath();
+ }
+ } finally {
+ this.isUpdatingNavigations = false;
+ }
- navigationabstract1.recomputePath();
}
- } finally {
- this.isUpdatingNavigations = false;
- }
-
+ } // Paper
}
- } // Paper
}
@Override

View File

@@ -1,11 +1,10 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com> From: wangxyper <wangxyper@163.com>
Date: Mon, 9 Jan 2023 21:08:52 +0800 Date: Sun, 15 Jan 2023 09:56:52 +0800
Subject: [PATCH] Hearse: Add mcmt's collections and fix some concurrent Subject: [PATCH] Hearse: Add mcmt collections
problem
Original license: MIT Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite Original project: https://github.com/NaturalCodeClub/Hearse
diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java
new file mode 100644 new file mode 100644
@@ -66,10 +65,10 @@ index 0000000000000000000000000000000000000000..67dd5fe624fe4428d8907000cb23a334
+} +}
diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java 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 new file mode 100644
index 0000000000000000000000000000000000000000..22b9d217dc06caaf8fbec21f0e31aa1cd13144ee index 0000000000000000000000000000000000000000..fec1f280c72c5b519173017877812ec3f7149ec5
--- /dev/null --- /dev/null
+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java +++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java
@@ -0,0 +1,945 @@ @@ -0,0 +1,937 @@
+package net.himeki.mcmtfabric.parallelised; +package net.himeki.mcmtfabric.parallelised;
+ +
+/* +/*
@@ -82,19 +81,11 @@ index 0000000000000000000000000000000000000000..22b9d217dc06caaf8fbec21f0e31aa1c
+ * Modified to actually implement List<E> + * 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; +import org.apache.commons.lang3.NotImplementedException;
+ +
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+/** +/**
+ * A concurrent linked-list implementation of a {@link Deque} (double-ended + * A concurrent linked-list implementation of a {@link Deque} (double-ended
+ * queue). Concurrent insertion, removal, and access operations execute safely + * queue). Concurrent insertion, removal, and access operations execute safely
@@ -1018,25 +1009,18 @@ index 0000000000000000000000000000000000000000..22b9d217dc06caaf8fbec21f0e31aa1c
\ No newline at end of file \ No newline at end of file
diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..2bf97bd3e77fe4fec785b850524a870300ecd82c index 0000000000000000000000000000000000000000..586b3c87dcf010e2401933c5ec2338f59b9edad3
--- /dev/null --- /dev/null
+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java +++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java
@@ -0,0 +1,237 @@ @@ -0,0 +1,230 @@
+package net.himeki.mcmtfabric.parallelised.fastutil; +package net.himeki.mcmtfabric.parallelised.fastutil;
+ +
+import it.unimi.dsi.fastutil.longs.*;
+
+import java.util.Collection; +import java.util.Collection;
+import java.util.Iterator; +import java.util.Iterator;
+import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.ConcurrentSkipListSet;
+ +
+import it.unimi.dsi.fastutil.longs.LongArrays;
+import it.unimi.dsi.fastutil.longs.LongCollection;
+import it.unimi.dsi.fastutil.longs.LongComparator;
+import it.unimi.dsi.fastutil.longs.LongIterator;
+import it.unimi.dsi.fastutil.longs.LongIterators;
+import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongListIterator;
+import it.unimi.dsi.fastutil.longs.LongSortedSet;
+
+public class ConcurrentLongLinkedOpenHashSet extends LongLinkedOpenHashSet { +public class ConcurrentLongLinkedOpenHashSet extends LongLinkedOpenHashSet {
+ +
+ private static final long serialVersionUID = -5532128240738069111L; + private static final long serialVersionUID = -5532128240738069111L;
@@ -1529,35 +1513,28 @@ index 0000000000000000000000000000000000000000..ff1a4f87356459d3bc990a77c3081932
+} +}
diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..a14ecb2ca64316fb85e6ecb65df50d98d337aff9 index 0000000000000000000000000000000000000000..ca1fd9ca6f5c5d5d4a0f374440ccaf80347fb781
--- /dev/null --- /dev/null
+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java +++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java
@@ -0,0 +1,1685 @@ @@ -0,0 +1,1678 @@
+package net.himeki.mcmtfabric.parallelised.fastutil; +package net.himeki.mcmtfabric.parallelised.fastutil;
+ +
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import it.unimi.dsi.fastutil.longs.*;
+import it.unimi.dsi.fastutil.shorts.ShortIterator;
+import org.apache.commons.lang3.ArrayUtils;
+
+import it.unimi.dsi.fastutil.bytes.ByteCollection; +import it.unimi.dsi.fastutil.bytes.ByteCollection;
+import it.unimi.dsi.fastutil.bytes.ByteIterator; +import it.unimi.dsi.fastutil.bytes.ByteIterator;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry;
+import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.ints.IntCollection;
+import it.unimi.dsi.fastutil.ints.IntIterator; +import it.unimi.dsi.fastutil.ints.IntIterator;
+import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntSet;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; +import it.unimi.dsi.fastutil.longs.*;
+import it.unimi.dsi.fastutil.objects.ObjectCollection; +import it.unimi.dsi.fastutil.objects.ObjectCollection;
+import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.objects.ObjectIterator;
+import it.unimi.dsi.fastutil.objects.ObjectSet; +import it.unimi.dsi.fastutil.objects.ObjectSet;
+import it.unimi.dsi.fastutil.shorts.ShortIterator;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+ +
+public class FastUtilHackUtil { +public class FastUtilHackUtil {
+ +
@@ -4329,24 +4306,17 @@ index 0000000000000000000000000000000000000000..a7d6be048ab3b8bd38231fce16eca0ac
\ No newline at end of file \ No newline at end of file
diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..565dc74cb65ef0ce9df36a72d9cd5fa440a535e6 index 0000000000000000000000000000000000000000..1d65e9575389a170e8b8465b1221a6e319749eb1
--- /dev/null --- /dev/null
+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java +++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java
@@ -0,0 +1,197 @@ @@ -0,0 +1,190 @@
+package net.himeki.mcmtfabric.parallelised.fastutil.sync; +package net.himeki.mcmtfabric.parallelised.fastutil.sync;
+ +
+import it.unimi.dsi.fastutil.longs.*;
+
+import java.util.Collection; +import java.util.Collection;
+import java.util.Iterator; +import java.util.Iterator;
+ +
+import it.unimi.dsi.fastutil.longs.LongArrays;
+import it.unimi.dsi.fastutil.longs.LongCollection;
+import it.unimi.dsi.fastutil.longs.LongComparator;
+import it.unimi.dsi.fastutil.longs.LongIterator;
+import it.unimi.dsi.fastutil.longs.LongIterators;
+import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongListIterator;
+import it.unimi.dsi.fastutil.longs.LongSortedSet;
+
+public class SyncLongLinkedOpenHashSet extends LongLinkedOpenHashSet { +public class SyncLongLinkedOpenHashSet extends LongLinkedOpenHashSet {
+ +
+ private static final long serialVersionUID = -5532128240738069111L; + private static final long serialVersionUID = -5532128240738069111L;

File diff suppressed because it is too large Load Diff

View File

@@ -1,315 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 9 Jan 2023 09:26:38 +0800
Subject: [PATCH] Hearse: Update codes
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
index 06f55f26eb63e356b3558622bf68711f18cda1c6..2a43625d13d7aa253c15aba8092ac9361785a5f0 100644
--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
@@ -3,8 +3,11 @@ package co.earthme.hearse.concurrent;
import io.papermc.paper.util.TickThread;
public class WorkerThread extends TickThread {
+
public WorkerThread(String name) {
super(name);
+ this.setDaemon(true);
+ this.setPriority(Thread.NORM_PRIORITY - 2);
}
public static boolean isWorker(){
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e65b1eba68003a9f7ce5080d07a521817831ff48
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java
@@ -0,0 +1,5 @@
+package co.earthme.hearse.concurrent;
+
+public interface WorkerThreadFactory {
+ WorkerThread getNewThread(Runnable task);
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
similarity index 62%
rename from src/main/java/co/earthme/hearse/concurrent/ThreadPool.java
rename to src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
index 3fbc81cb880cf6d38bb4c940b4cc1fa828c2ef17..f7ca6d650d9089b65137d61acca64c89e5b4db22 100644
--- a/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
@@ -6,25 +6,17 @@ import java.util.Queue;
import java.util.concurrent.*;
import java.util.concurrent.locks.LockSupport;
-public class ThreadPool extends ThreadPoolExecutor {
- public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
- }
-
- public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull ThreadFactory threadFactory) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
- }
+public class WorkerThreadPoolExecutor extends ThreadPoolExecutor {
+ private final Queue<TaskEntry> taskEntries = new ConcurrentLinkedQueue<>();
- public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull RejectedExecutionHandler handler) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
+ public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull WorkerThreadFactory workerThreadFactory) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,r->workerThreadFactory.getNewThread(r));
}
- public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull ThreadFactory threadFactory, @NotNull RejectedExecutionHandler handler) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
+ public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull WorkerThreadFactory workerThreadFactory, @NotNull RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,r->workerThreadFactory.getNewThread(r), handler);
}
- private final Queue<TaskEntry> taskEntries = new ConcurrentLinkedQueue<>();
-
public void executeWithSubTask(Runnable mainTask,Runnable subTask){
final TaskEntry wrapped = new TaskEntry(subTask,mainTask);
this.taskEntries.offer(wrapped);
diff --git a/src/main/java/co/earthme/hearse/server/ServerHook.java b/src/main/java/co/earthme/hearse/server/ServerHook.java
new file mode 100644
index 0000000000000000000000000000000000000000..22260735664d986fed6bf82e4016b647417e1932
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/server/ServerHook.java
@@ -0,0 +1,66 @@
+package co.earthme.hearse.server;
+
+import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ServerHook {
+ private static volatile boolean firstTick = false;
+ private static final AtomicInteger threadId = new AtomicInteger();
+ private static final WorkerThreadPoolExecutor worker = new WorkerThreadPoolExecutor(
+ Runtime.getRuntime().availableProcessors(),
+ Runtime.getRuntime().availableProcessors(),
+ 100,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ task -> {
+ WorkerThread workerThread = new WorkerThread("Hearse-Worker-Thread # "+threadId.getAndIncrement());
+ return workerThread;
+ }
+ );
+
+ public static void executeAsyncTask(Runnable task){
+ worker.execute(task);
+ }
+
+ public static void executeAsyncTaskWithMainThreadCallback(Runnable task,Runnable callBack){
+ worker.executeWithSubTask(task,callBack);
+ }
+
+ public static void callPostTick(){
+ if (!firstTick){
+ firstTick = true;
+ return;
+ }
+ worker.runAllSubTasks();
+ }
+
+ public static void callAsyncEntityTick(Entity entity, ServerLevel level){
+ MinecraftServer.getServer().executeMidTickTasks();
+ worker.execute(()->{
+ entity.activatedPriorityReset = false;
+ if (!entity.isRemoved()) {
+ entity.checkDespawn();
+ Entity entity1 = entity.getVehicle();
+ if (entity1 != null) {
+ if (!entity1.isRemoved() && entity1.hasPassenger(entity)) {
+ return;
+ }
+ entity.stopRiding();
+ }
+ try {
+ level.tickNonPassenger(entity);
+ } catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) throw throwable;
+ level.getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(throwable.getMessage(), throwable)));
+ throwable.printStackTrace();
+ }
+ }
+ });
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 3b7e4b724e86518ea57f5ed5ef0b8b3741d10f6f..e0e169a4403926ff6be004de1bf5ec2079acb440 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1,5 +1,6 @@
package net.minecraft.server;
+import co.earthme.hearse.server.ServerHook;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import co.aikar.timings.Timings;
@@ -1407,6 +1408,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
++this.tickCount;
this.tickChildren(shouldKeepTicking);
+ ServerHook.callPostTick();
if (i - this.lastServerStatus >= 5000000000L) {
this.lastServerStatus = i;
this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount()));
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index a2935994edc279d880ff26dd5cc4e33f1105acc8..81697ea6d00967852556c3bb741317db030c24db 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -833,12 +833,10 @@ public class ServerChunkCache extends ChunkSource {
if (chunkMap.playerMobDistanceMap != null && _pufferfish_spawnCountsReady.getAndSet(false)) {
net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> {
int mapped = distanceManager.getNaturalSpawnChunkCount();
- io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Entity> objectiterator =
- level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS);
+ Iterator<Entity> objectiterator = level.entityTickList.entities.iterator();
gg.pufferfish.pufferfish.util.IterableWrapper<Entity> wrappedIterator =
new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator);
lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true);
- objectiterator.finishedIterating();
_pufferfish_spawnCountsReady.set(true);
});
}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 6074956d677542a46066dc45ceda9d73a2e09d27..125e5fb54acb68d58ea9fb973894b02facb04edc 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1,6 +1,8 @@
package net.minecraft.server.level;
import co.aikar.timings.TimingHistory;
+import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.server.ServerHook;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -221,7 +223,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
public final void loadChunksForMoveAsync(AABB axisalignedbb, ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority priority,
java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) {
- if (Thread.currentThread() != this.thread) {
+ if (Thread.currentThread() != this.thread && !WorkerThread.isWorker()) {
this.getChunkSource().mainThreadProcessor.execute(() -> {
this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad);
});
@@ -643,71 +645,15 @@ public class ServerLevel extends Level implements WorldGenLevel {
//timings.doSounds.stopTiming(); // Spigot // Purpur
this.handlingTick = false;
//gameprofilerfiller.pop(); // Purpur
- boolean flag = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players
-
- if (flag) {
- this.resetEmptyTime();
- }
-
- if (flag || this.emptyTime++ < 300) {
- //gameprofilerfiller.push("entities"); // Purpur
- //timings.tickEntities.startTiming(); // Spigot // Purpur
- if (this.dragonFight != null) {
- //gameprofilerfiller.push("dragonFight"); // Purpur
- this.dragonFight.tick();
- //gameprofilerfiller.pop(); // Purpur
- }
-
- org.spigotmc.ActivationRange.activateEntities(this); // Spigot
- //timings.entityTick.startTiming(); // Spigot // Purpur
- this.entityTickList.forEach((entity) -> {
- entity.activatedPriorityReset = false; // Pufferfish - DAB
- if (!entity.isRemoved()) {
- if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed
- entity.discard();
- } else {
- //gameprofilerfiller.push("checkDespawn"); // Purpur
- entity.checkDespawn();
- //gameprofilerfiller.pop(); // Purpur
- if (true || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { // Paper - now always true if in the ticking list
- Entity entity1 = entity.getVehicle();
-
- if (entity1 != null) {
- if (!entity1.isRemoved() && entity1.hasPassenger(entity)) {
- return;
- }
-
- entity.stopRiding();
- }
-
- //gameprofilerfiller.push("tick"); // Purpur
- // Pufferfish start - copied from this.guardEntityTick
- try {
- this.tickNonPassenger(entity); // Pufferfish - changed
- MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick
- } catch (Throwable throwable) {
- if (throwable instanceof ThreadDeath) throw throwable; // Paper
- // Paper start - Prevent tile entity and entity crashes
- final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ());
- MinecraftServer.LOGGER.error(msg, throwable);
- getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable)));
- entity.discard();
- // Paper end
- }
- // Pufferfish end
- //gameprofilerfiller.pop(); // Purpur
- }
- }
- }
- });
- //timings.entityTick.stopTiming(); // Spigot // Purpur
- //timings.tickEntities.stopTiming(); // Spigot // Purpur
- //gameprofilerfiller.pop(); // Purpur
- this.tickBlockEntities();
+ this.resetEmptyTime();
+ if (this.dragonFight != null) {
+ this.dragonFight.tick();
}
-
- //gameprofilerfiller.push("entityManagement"); // Purpur
- //this.entityManager.tick(); // Paper - rewrite chunk system
+ org.spigotmc.ActivationRange.activateEntities(this); // Spigot
+ this.entityTickList.forEach((entity) -> {
+ ServerHook.callAsyncEntityTick(entity,this);
+ });
+ this.tickBlockEntities();
}
@Override
@@ -746,7 +692,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
- private boolean shouldDiscardEntity(Entity entity) {
+ public boolean shouldDiscardEntity(Entity entity) {
return !this.server.isSpawningAnimals() && (entity instanceof Animal || entity instanceof WaterAnimal) ? true : !this.server.areNpcsEnabled() && entity instanceof Npc;
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 958e0ee29915bddde2cb8ebfd578448b83e2b149..de78c5bdde53b4adfed9fda4d473560849bdb5aa 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -2,6 +2,7 @@ package net.minecraft.world.level;
import co.aikar.timings.Timing;
import co.aikar.timings.Timings;
+import co.earthme.hearse.concurrent.WorkerThread;
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
import com.destroystokyo.paper.exception.ServerInternalException;
import com.google.common.base.MoreObjects;
@@ -1116,7 +1117,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
}
// Paper end
// CraftBukkit end
- return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system
+ return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() && !WorkerThread.isWorker() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system
}
public void setBlockEntity(BlockEntity blockEntity) {

View File

@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 15 Jan 2023 09:58:00 +0800
Subject: [PATCH] Hearse: Misc changes
Original license: MIT
Original project: https://github.com/NaturalCodeClub/Hearse
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 6a52ae70b5f7fd9953b6b2605cae722f606e7fec..af5956bd57141cae08fe839bb2176988a48cd9b8 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -57,6 +57,7 @@ import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
+import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Location; // Paper
import org.bukkit.Material;
@@ -229,7 +230,7 @@ public class CraftEventFactory {
public static final DamageSource MELTING = CraftDamageSource.copyOf(DamageSource.ON_FIRE);
public static final DamageSource POISON = CraftDamageSource.copyOf(DamageSource.MAGIC);
public static org.bukkit.block.Block blockDamage; // For use in EntityDamageByBlockEvent
- public static Entity entityDamage; // For use in EntityDamageByEntityEvent
+ public static volatile Entity entityDamage; // For use in EntityDamageByEntityEvent
// helper methods
private static boolean canBuild(ServerLevel world, Player player, int x, int z) {
@@ -1078,7 +1079,8 @@ public class CraftEventFactory {
} else if (source == DamageSource.MAGIC) {
cause = DamageCause.MAGIC;
} else {
- throw new IllegalStateException(String.format("Unhandled damage of %s by %s from %s", entity, damager.getHandle(), source.msgId));
+ LogManager.getLogger().error(String.format("Unhandled damage of %s by %s from %s", entity, damager.getHandle(), source.msgId));
+ cause = DamageCause.CUSTOM;
}
EntityDamageEvent event = new EntityDamageByEntityEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions, source.isCritical()); // Paper - add critical damage API
event.setCancelled(cancelled);

View File

@@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 15 Jan 2023 10:51:59 +0800
Subject: [PATCH] Hearse: Fix a CME in AttributeMap
Original license: MIT
Original project: https://github.com/NaturalCodeClub/Hearse
diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index e283eb57c25f7de222f9d09dca851169f5f6e488..860a43c1f426b876eab5c908280cf379e331baf2 100644
--- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -20,8 +20,8 @@ import org.slf4j.Logger;
public class AttributeMap {
private static final Logger LOGGER = LogUtils.getLogger();
- private final Map<Attribute, AttributeInstance> attributes = Maps.newHashMap();
- private final Set<AttributeInstance> dirtyAttributes = Sets.newHashSet();
+ private final Map<Attribute, AttributeInstance> attributes = Maps.newConcurrentMap();
+ private final Set<AttributeInstance> dirtyAttributes = Sets.newConcurrentHashSet();
private final AttributeSupplier supplier;
private final java.util.function.Function<Attribute, AttributeInstance> createInstance; // Pufferfish

View File

@@ -1,96 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 9 Jan 2023 12:40:56 +0800
Subject: [PATCH] Hearse: Fix some problems in ItemEntity
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/HearseConfig.java b/src/main/java/co/earthme/hearse/HearseConfig.java
index 912da4787f83f656da67e9533b60183c17e6c345..0a1de52bcdf675b9bfcbf14d39959818a7a0cbbb 100644
--- a/src/main/java/co/earthme/hearse/HearseConfig.java
+++ b/src/main/java/co/earthme/hearse/HearseConfig.java
@@ -1,4 +1,5 @@
package co.earthme.hearse;
public class HearseConfig {
+
}
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index c58496c84b2b3f86890050813041fa49711f3a01..3e8b68b4390a6086b1b1983fe6f8b545c2847ef8 100644
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
@@ -4,6 +4,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
@@ -239,21 +241,24 @@ public class ItemEntity extends Entity {
this.setDeltaMovement(vec3d.x * 0.949999988079071D, vec3d.y + (double) (vec3d.y < 0.05999999865889549D ? 5.0E-4F : 0.0F), vec3d.z * 0.949999988079071D);
}
+ private final Lock mergeLock = new ReentrantLock();
+
private void mergeWithNeighbours() {
- if (this.isMergable()) {
- // Spigot start
- double radius = level.spigotConfig.itemMerge;
- List<ItemEntity> list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> {
- // Spigot end
- return entityitem != this && entityitem.isMergable();
- });
- Iterator iterator = list.iterator();
-
- while (iterator.hasNext()) {
- ItemEntity entityitem = (ItemEntity) iterator.next();
-
- if (entityitem.isMergable()) {
- // Paper Start - Fix items merging through walls
+ if (!this.mergeLock.tryLock()){
+ return;
+ }
+ try {
+ if (this.isMergable()) {
+ // Spigot start
+ double radius = level.spigotConfig.itemMerge;
+ List<ItemEntity> list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> {
+ // Spigot end
+ return entityitem != this && entityitem.isMergable();
+ });
+
+ for (ItemEntity entityitem : list) {
+ if (entityitem.isMergable()) {
+ // Paper Start - Fix items merging through walls
if (this.level.paperConfig().fixes.fixItemsMergingThroughWalls) {
// Pufferfish start - skip the allocations
/*
@@ -263,17 +268,19 @@ public class ItemEntity extends Entity {
if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue;
*/
if (level.rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) ==
- net.minecraft.world.phys.HitResult.Type.BLOCK) continue;
+ net.minecraft.world.phys.HitResult.Type.BLOCK) continue;
// Pufferfish end
}
- // Paper End
- this.tryToMerge(entityitem);
- if (this.isRemoved()) {
- break;
+ // Paper End
+ this.tryToMerge(entityitem);
+ if (this.isRemoved()) {
+ break;
+ }
}
}
}
-
+ }finally {
+ this.mergeLock.unlock();
}
}

View File

@@ -1,185 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 9 Jan 2023 13:15:09 +0800
Subject: [PATCH] Hearse: Fix some problems in GoalSelector and ShufflingList
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java
index b3329c6fcd6758a781a51f5ba8f5052ac1c77b49..adb02cba6cdb62752f847136000c6f7ca857bd5a 100644
--- a/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java
+++ b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java
@@ -2,9 +2,6 @@ package com.destroystokyo.paper.util.set;
import java.util.Collection;
-/**
- * @author Spottedleaf <Spottedleaf@users.noreply.github.com>
- */
public final class OptimizedSmallEnumSet<E extends Enum<E>> {
private final Class<E> enumClass;
@@ -20,7 +17,7 @@ public final class OptimizedSmallEnumSet<E extends Enum<E>> {
this.enumClass = clazz;
}
- public boolean addUnchecked(final E element) {
+ public synchronized boolean addUnchecked(final E element) {
final int ordinal = element.ordinal();
final long key = 1L << ordinal;
@@ -30,7 +27,7 @@ public final class OptimizedSmallEnumSet<E extends Enum<E>> {
return (prev & key) == 0;
}
- public boolean removeUnchecked(final E element) {
+ public synchronized boolean removeUnchecked(final E element) {
final int ordinal = element.ordinal();
final long key = 1L << ordinal;
@@ -40,15 +37,15 @@ public final class OptimizedSmallEnumSet<E extends Enum<E>> {
return (prev & key) != 0;
}
- public void clear() {
+ public synchronized void clear() {
this.backingSet = 0L;
}
- public int size() {
+ public synchronized int size() {
return Long.bitCount(this.backingSet);
}
- public void addAllUnchecked(final Collection<E> enums) {
+ public synchronized void addAllUnchecked(final Collection<E> enums) {
for (final E element : enums) {
if (element == null) {
throw new NullPointerException("Null element");
@@ -57,15 +54,15 @@ public final class OptimizedSmallEnumSet<E extends Enum<E>> {
}
}
- public long getBackingSet() {
+ public synchronized long getBackingSet() {
return this.backingSet;
}
- public boolean hasCommonElements(final OptimizedSmallEnumSet<E> other) {
+ public synchronized boolean hasCommonElements(final OptimizedSmallEnumSet<E> other) {
return (other.backingSet & this.backingSet) != 0;
}
- public boolean hasElement(final E element) {
+ public synchronized boolean hasElement(final E element) {
return (this.backingSet & (1L << element.ordinal())) != 0;
}
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
index fe3ab3d388f0481fb0db06b7f730f868dbf8e8a5..ac006bacbe8715e5c272c69afd1edab45a6511e8 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
@@ -25,7 +25,7 @@ public class ShufflingList<U> implements Iterable<U> {
public ShufflingList(boolean isUnsafe) {
this.isUnsafe = isUnsafe;
// Paper end
- this.entries = Lists.newArrayList();
+ this.entries = Lists.newCopyOnWriteArrayList();
}
private ShufflingList(List<ShufflingList.WeightedEntry<U>> list) {
@@ -35,7 +35,7 @@ public class ShufflingList<U> implements Iterable<U> {
private ShufflingList(List<ShufflingList.WeightedEntry<U>> list, boolean isUnsafe) {
this.isUnsafe = isUnsafe;
// Paper end
- this.entries = Lists.newArrayList(list);
+ this.entries = Lists.newCopyOnWriteArrayList(list);
}
public static <U> Codec<ShufflingList<U>> codec(Codec<U> codec) {
@@ -44,12 +44,12 @@ public class ShufflingList<U> implements Iterable<U> {
});
}
- public ShufflingList<U> add(U data, int weight) {
+ public synchronized ShufflingList<U> add(U data, int weight) {
this.entries.add(new ShufflingList.WeightedEntry<>(data, weight));
return this;
}
- public ShufflingList<U> shuffle() {
+ public synchronized ShufflingList<U> shuffle() {
// Paper start - make concurrent safe, work off a clone of the list
List<ShufflingList.WeightedEntry<U>> list = this.isUnsafe ? Lists.newArrayList(this.entries) : this.entries;
list.forEach(entry -> entry.setRandom(this.random.nextFloat()));
@@ -58,17 +58,17 @@ public class ShufflingList<U> implements Iterable<U> {
// Paper end
}
- public Stream<U> stream() {
+ public synchronized Stream<U> stream() {
return this.entries.stream().map(ShufflingList.WeightedEntry::getData);
}
@Override
- public Iterator<U> iterator() {
+ public synchronized Iterator<U> iterator() {
return Iterators.transform(this.entries.iterator(), ShufflingList.WeightedEntry::getData);
}
@Override
- public String toString() {
+ public synchronized String toString() {
return "ShufflingList[" + this.entries + "]";
}
@@ -90,16 +90,16 @@ public class ShufflingList<U> implements Iterable<U> {
this.randWeight = -Math.pow((double)random, (double)(1.0F / (float)this.weight));
}
- public T getData() {
+ public synchronized T getData() {
return this.data;
}
- public int getWeight() {
+ public synchronized int getWeight() {
return this.weight;
}
@Override
- public String toString() {
+ public synchronized String toString() {
return this.weight + ":" + this.data;
}
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 02978315bc2b828cc603ce7478408f3f82c249c2..96d37e0845df9b22cf60f9835787789d0d0e4a79 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
@@ -3,11 +3,8 @@ package net.minecraft.world.entity.ai.goal;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import com.mojang.logging.LogUtils;
-import java.util.EnumMap;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
+
+import java.util.*;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -27,8 +24,8 @@ public class GoalSelector {
return false;
}
};
- private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
- private final Set<WrappedGoal> availableGoals = Sets.newLinkedHashSet();
+ private final Map<Goal.Flag, WrappedGoal> lockedFlags = Collections.synchronizedMap(new EnumMap<>(Goal.Flag.class));
+ private final Set<WrappedGoal> availableGoals = Sets.newCopyOnWriteArraySet();
private final Supplier<ProfilerFiller> profiler;
private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector

View File

@@ -0,0 +1,451 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 15 Jan 2023 14:42:55 +0800
Subject: [PATCH] Hearse: Remove MCMT's fix in scheduled ticks and add locks
into them
Original license: MIT
Original project: https://github.com/NaturalCodeClub/Hearse
diff --git a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java
index 4c75f50ab0184637b72e08936ff8808ad6c6fb5f..367ce55fb9b31f718357a8da522a639848e9dc6a 100644
--- a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java
+++ b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java
@@ -23,7 +23,7 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
private final Queue<ScheduledTick<T>> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER);
@Nullable
private List<SavedTick<T>> pendingTicks;
- private final Set<ScheduledTick<?>> ticksPerPosition = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH));
+ private final Set<ScheduledTick<?>> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH);
@Nullable
private BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> onTickAdded;
@@ -40,8 +40,7 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
}
// Paper end - add dirty flag
- public LevelChunkTicks() {
- }
+ public LevelChunkTicks() {}
public LevelChunkTicks(List<SavedTick<T>> ticks) {
this.pendingTicks = ticks;
@@ -49,7 +48,6 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
for(SavedTick<T> savedTick : ticks) {
this.ticksPerPosition.add(ScheduledTick.probe(savedTick.type(), savedTick.pos()));
}
-
}
public synchronized void setOnTickAdded(@Nullable BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> tickConsumer) {
diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java
index 2203adc2a68e7fb253e353098fd6ddad521e3a32..da9fdcce1562ebf5334a7441414a1fecc5d2a791 100644
--- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java
+++ b/src/main/java/net/minecraft/world/ticks/LevelTicks.java
@@ -14,24 +14,24 @@ import net.minecraft.world.level.levelgen.structure.BoundingBox;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.locks.StampedLock;
import java.util.function.BiConsumer;
import java.util.function.LongPredicate;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class LevelTicks<T> implements LevelTickAccess<T> {
- private static final Comparator<LevelChunkTicks<?>> CONTAINER_DRAIN_ORDER = (a, b) -> {
- return ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek());
- };
+ private static final Comparator<LevelChunkTicks<?>> CONTAINER_DRAIN_ORDER = (a, b) -> ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek());
private final LongPredicate tickCheck;
- private final Long2ObjectMap<LevelChunkTicks<T>> allContainers = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
+ private final Long2ObjectMap<LevelChunkTicks<T>> allContainers = new Long2ObjectOpenHashMap<>();
private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), (map) -> {
map.defaultReturnValue(Long.MAX_VALUE);
});
private final Queue<LevelChunkTicks<T>> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER);
- private final Queue<ScheduledTick<T>> toRunThisTick = new ConcurrentLinkedDeque<>();
- private final List<ScheduledTick<T>> alreadyRunThisTick = new CopyOnWriteArrayList<>();
- private final Set<ScheduledTick<?>> toRunThisTickSet = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH));
+ private final Queue<ScheduledTick<T>> toRunThisTick = new ArrayDeque<>();
+ private final List<ScheduledTick<T>> alreadyRunThisTick = new ArrayList<>();
+ private final Set<ScheduledTick<?>> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH);
+ private final StampedLock ticksLock = new StampedLock(); //Hearse
private final BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> chunkScheduleUpdater = (chunkTickScheduler, tick) -> {
if (tick.equals(chunkTickScheduler.peek())) {
@@ -44,34 +44,47 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
}
public void addContainer(ChunkPos pos, LevelChunkTicks<T> scheduler) {
- long l = pos.toLong();
- this.allContainers.put(l, scheduler);
- ScheduledTick<T> scheduledTick = scheduler.peek();
- if (scheduledTick != null) {
- this.nextTickForContainer.put(l, scheduledTick.triggerTick());
+ final long stamp = this.ticksLock.writeLock();
+ try {
+ long l = pos.toLong();
+ this.allContainers.put(l, scheduler);
+ ScheduledTick<T> scheduledTick = scheduler.peek();
+ if (scheduledTick != null) {
+ this.nextTickForContainer.put(l, scheduledTick.triggerTick());
+ }
+ scheduler.setOnTickAdded(this.chunkScheduleUpdater);
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
}
-
- scheduler.setOnTickAdded(this.chunkScheduleUpdater);
}
public void removeContainer(ChunkPos pos) {
- long l = pos.toLong();
- LevelChunkTicks<T> levelChunkTicks = this.allContainers.remove(l);
- this.nextTickForContainer.remove(l);
- if (levelChunkTicks != null) {
- levelChunkTicks.setOnTickAdded((BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>>)null);
+ final long stamp = this.ticksLock.writeLock();
+ try {
+ long l = pos.toLong();
+ LevelChunkTicks<T> levelChunkTicks = this.allContainers.remove(l);
+ this.nextTickForContainer.remove(l);
+ if (levelChunkTicks != null) {
+ levelChunkTicks.setOnTickAdded((BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>>)null);
+ }
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
}
-
}
@Override
public void schedule(ScheduledTick<T> orderedTick) {
- long l = ChunkPos.asLong(orderedTick.pos());
- LevelChunkTicks<T> levelChunkTicks = this.allContainers.get(l);
- if (levelChunkTicks == null) {
- Util.pauseInIde(new IllegalStateException("Trying to schedule tick in not loaded position " + orderedTick.pos()));
- } else {
- levelChunkTicks.schedule(orderedTick);
+ final long stamp = this.ticksLock.readLock();
+ try{
+ long l = ChunkPos.asLong(orderedTick.pos());
+ LevelChunkTicks<T> levelChunkTicks = this.allContainers.get(l);
+ if (levelChunkTicks == null) {
+ Util.pauseInIde(new IllegalStateException("Trying to schedule tick in not loaded position " + orderedTick.pos()));
+ } else {
+ levelChunkTicks.schedule(orderedTick);
+ }
+ }finally {
+ this.ticksLock.unlockRead(stamp);
}
}
@@ -95,38 +108,40 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
}
private void sortContainersToTick(long time) {
- ObjectIterator<Long2LongMap.Entry> objectIterator = Long2LongMaps.fastIterator(this.nextTickForContainer);
-
- while(objectIterator.hasNext()) {
- Long2LongMap.Entry entry = objectIterator.next();
- long l = entry.getLongKey();
- long m = entry.getLongValue();
- if (m <= time) {
- LevelChunkTicks<T> levelChunkTicks = this.allContainers.get(l);
- if (levelChunkTicks == null) {
- objectIterator.remove();
- } else {
- ScheduledTick<T> scheduledTick = levelChunkTicks.peek();
- if (scheduledTick == null) {
- objectIterator.remove();
- } else if (scheduledTick.triggerTick() > time) {
- entry.setValue(scheduledTick.triggerTick());
- } else if (this.tickCheck.test(l)) {
+ final long stamp = this.ticksLock.writeLock();
+ try {
+ ObjectIterator<Long2LongMap.Entry> objectIterator = Long2LongMaps.fastIterator(this.nextTickForContainer);
+ while(objectIterator.hasNext()) {
+ Long2LongMap.Entry entry = objectIterator.next();
+ long l = entry.getLongKey();
+ long m = entry.getLongValue();
+ if (m <= time) {
+ LevelChunkTicks<T> levelChunkTicks = this.allContainers.get(l);
+ if (levelChunkTicks == null) {
objectIterator.remove();
- synchronized (this.containersToTick){
+ } else {
+ ScheduledTick<T> scheduledTick = levelChunkTicks.peek();
+ if (scheduledTick == null) {
+ objectIterator.remove();
+ } else if (scheduledTick.triggerTick() > time) {
+ entry.setValue(scheduledTick.triggerTick());
+ } else if (this.tickCheck.test(l)) {
+ objectIterator.remove();
this.containersToTick.add(levelChunkTicks);
}
}
}
}
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
}
-
}
private void drainContainers(long time, int maxTicks) {
- LevelChunkTicks<T> levelChunkTicks;
- synchronized (this.containersToTick){
- while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) {
+ final long stamp = this.ticksLock.writeLock();
+ try {
+ LevelChunkTicks<T> levelChunkTicks;
+ while (this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) {
ScheduledTick<T> scheduledTick = levelChunkTicks.poll();
this.scheduleForThisTick(scheduledTick);
this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks);
@@ -139,14 +154,29 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
}
}
}
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
}
}
private void rescheduleLeftoverContainers() {
- synchronized (this.containersToTick){
- for(LevelChunkTicks<T> levelChunkTicks : this.containersToTick) {
+ final List<LevelChunkTicks<T>> cop = new ArrayList<>();
+ long stamp = this.ticksLock.readLock();
+ try {
+ for (LevelChunkTicks<T> levelChunkTicks : this.containersToTick) {
+ cop.add(levelChunkTicks);
+ }
+ }finally {
+ this.ticksLock.unlockRead(stamp);
+ }
+
+ stamp = this.ticksLock.writeLock();
+ try {
+ for (LevelChunkTicks<T> levelChunkTicks : cop){
this.updateContainerScheduling(levelChunkTicks.peek());
}
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
}
}
@@ -181,44 +211,81 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
}
private void runCollectedTicks(BiConsumer<BlockPos, T> ticker) {
- while(!this.toRunThisTick.isEmpty()) {
- ScheduledTick<T> scheduledTick = this.toRunThisTick.poll();
- if (!this.toRunThisTickSet.isEmpty()) {
- this.toRunThisTickSet.remove(scheduledTick);
+ ScheduledTick[] cop;
+
+ long stamp = this.ticksLock.writeLock();
+ try {
+ cop = new ScheduledTick[this.toRunThisTick.size()];
+ int counter = 0;
+ while(!this.toRunThisTick.isEmpty()) {
+ ScheduledTick<T> scheduledTick = this.toRunThisTick.poll();
+ if (!this.toRunThisTickSet.isEmpty()) {
+ this.toRunThisTickSet.remove(scheduledTick);
+ }
+ cop[counter] = scheduledTick;
+ counter++;
}
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
+ }
- this.alreadyRunThisTick.add(scheduledTick);
+ for (ScheduledTick<T> scheduledTick : cop){
ticker.accept(scheduledTick.pos(), scheduledTick.type());
}
+ stamp = this.ticksLock.writeLock();
+ try {
+ for (ScheduledTick<T> scheduledTick : cop){
+ this.alreadyRunThisTick.add(scheduledTick);
+ }
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
+ }
}
private void cleanupAfterTick() {
- this.toRunThisTick.clear();
- synchronized (this.containersToTick){
+ final long stamp = this.ticksLock.writeLock();
+ try {
+ this.toRunThisTick.clear();
this.containersToTick.clear();
+ this.alreadyRunThisTick.clear();
+ this.toRunThisTickSet.clear();
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
}
- this.alreadyRunThisTick.clear();
- this.toRunThisTickSet.clear();
}
@Override
public boolean hasScheduledTick(BlockPos pos, T type) {
- LevelChunkTicks<T> levelChunkTicks = this.allContainers.get(ChunkPos.asLong(pos));
- return levelChunkTicks != null && levelChunkTicks.hasScheduledTick(pos, type);
+ final long stamp = this.ticksLock.readLock();
+ try {
+ LevelChunkTicks<T> levelChunkTicks = this.allContainers.get(ChunkPos.asLong(pos));
+ return levelChunkTicks != null && levelChunkTicks.hasScheduledTick(pos, type);
+ }finally {
+ this.ticksLock.unlockRead(stamp);
+ }
}
@Override
public boolean willTickThisTick(BlockPos pos, T type) {
this.calculateTickSetIfNeeded();
- return this.toRunThisTickSet.contains(ScheduledTick.probe(type, pos));
+ final long stamp = this.ticksLock.readLock();
+ try {
+ return this.toRunThisTickSet.contains(ScheduledTick.probe(type, pos));
+ }finally {
+ this.ticksLock.unlockRead(stamp);
+ }
}
private void calculateTickSetIfNeeded() {
- if (this.toRunThisTickSet.isEmpty() && !this.toRunThisTick.isEmpty()) {
- this.toRunThisTickSet.addAll(this.toRunThisTick);
+ final long stamp = this.ticksLock.writeLock();
+ try{
+ if (this.toRunThisTickSet.isEmpty() && !this.toRunThisTick.isEmpty()) {
+ this.toRunThisTickSet.addAll(this.toRunThisTick);
+ }
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
}
-
}
private void forContainersInArea(BoundingBox box, LevelTicks.PosAndContainerConsumer<T> visitor) {
@@ -230,7 +297,20 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
for(int m = i; m <= k; ++m) {
for(int n = j; n <= l; ++n) {
long o = ChunkPos.asLong(m, n);
- LevelChunkTicks<T> levelChunkTicks = this.allContainers.get(o);
+ LevelChunkTicks<T> levelChunkTicks;
+
+ long stamp = this.ticksLock.tryOptimisticRead();
+ if (this.ticksLock.validate(stamp)){
+ levelChunkTicks = this.allContainers.get(o);
+ }else{
+ stamp = this.ticksLock.readLock();
+ try {
+ levelChunkTicks = this.allContainers.get(o);
+ }finally {
+ this.ticksLock.unlockRead(stamp);
+ }
+ }
+
if (levelChunkTicks != null) {
visitor.accept(o, levelChunkTicks);
}
@@ -240,24 +320,32 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
}
public void clearArea(BoundingBox box) {
- Predicate<ScheduledTick<T>> predicate = (tick) -> {
- return box.isInside(tick.pos());
- };
+ Predicate<ScheduledTick<T>> predicate = (tick) -> box.isInside(tick.pos());
this.forContainersInArea(box, (chunkPos, chunkTickScheduler) -> {
- ScheduledTick<T> scheduledTick = chunkTickScheduler.peek();
- chunkTickScheduler.removeIf(predicate);
- ScheduledTick<T> scheduledTick2 = chunkTickScheduler.peek();
- if (scheduledTick2 != scheduledTick) {
- if (scheduledTick2 != null) {
- this.updateContainerScheduling(scheduledTick2);
- } else {
- this.nextTickForContainer.remove(chunkPos);
+ final long stamp = this.ticksLock.writeLock();
+ try {
+ ScheduledTick<T> scheduledTick = chunkTickScheduler.peek();
+ chunkTickScheduler.removeIf(predicate);
+ ScheduledTick<T> scheduledTick2 = chunkTickScheduler.peek();
+ if (scheduledTick2 != scheduledTick) {
+ if (scheduledTick2 != null) {
+ this.updateContainerScheduling(scheduledTick2);
+ } else {
+ this.nextTickForContainer.remove(chunkPos);
+ }
}
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
}
-
});
- this.alreadyRunThisTick.removeIf(predicate);
- this.toRunThisTick.removeIf(predicate);
+
+ final long stamp = this.ticksLock.writeLock();
+ try {
+ this.alreadyRunThisTick.removeIf(predicate);
+ this.toRunThisTick.removeIf(predicate);
+ }finally {
+ this.ticksLock.unlockWrite(stamp);
+ }
}
public void copyArea(BoundingBox box, Vec3i offset) {
@@ -265,22 +353,34 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
Predicate<ScheduledTick<T>> predicate = (tick) -> {
return box.isInside(tick.pos());
};
- this.alreadyRunThisTick.stream().filter(predicate).forEach(list::add);
- this.toRunThisTick.stream().filter(predicate).forEach(list::add);
- this.forContainersInArea(box, (chunkPos, chunkTickScheduler) -> {
- chunkTickScheduler.getAll().filter(predicate).forEach(list::add);
- });
- LongSummaryStatistics longSummaryStatistics = list.stream().mapToLong(ScheduledTick::subTickOrder).summaryStatistics();
- long l = longSummaryStatistics.getMin();
- long m = longSummaryStatistics.getMax();
- list.forEach((tick) -> {
- this.schedule(new ScheduledTick<>(tick.type(), tick.pos().offset(offset), tick.triggerTick(), tick.priority(), tick.subTickOrder() - l + m + 1L));
- });
+
+ long l;
+ long m;
+
+ final long stamp = this.ticksLock.readLock();
+ try {
+ this.alreadyRunThisTick.stream().filter(predicate).forEach(list::add);
+ this.toRunThisTick.stream().filter(predicate).forEach(list::add);
+ this.forContainersInArea(box, (chunkPos, chunkTickScheduler) -> {
+ chunkTickScheduler.getAll().filter(predicate).forEach(list::add);
+ });
+ LongSummaryStatistics longSummaryStatistics = list.stream().mapToLong(ScheduledTick::subTickOrder).summaryStatistics();
+ l = longSummaryStatistics.getMin();
+ m = longSummaryStatistics.getMax();
+ }finally {
+ this.ticksLock.unlockRead(stamp);
+ }
+ list.forEach((tick) -> this.schedule(new ScheduledTick<>(tick.type(), tick.pos().offset(offset), tick.triggerTick(), tick.priority(), tick.subTickOrder() - l + m + 1L)));
}
@Override
public int count() {
- return this.allContainers.values().stream().mapToInt(TickAccess::count).sum();
+ final long stamp = this.ticksLock.readLock();
+ try {
+ return this.allContainers.values().stream().mapToInt(TickAccess::count).sum();
+ }finally {
+ this.ticksLock.unlockRead(stamp);
+ }
}
@FunctionalInterface

View File

@@ -1,35 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 9 Jan 2023 13:30:48 +0800
Subject: [PATCH] Hearse: Fix an UOE in GoalSelector
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index a23379120ddc3653b58bdb08f9c837c60b6b83ca..dfb747eba6bf7088af0ff400da169de00a076365 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1136,8 +1136,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
// Paper start - ignore and warn about illegal addEntity calls instead of crashing server
if (!entity.valid || entity.level != this.level || this.entityMap.containsKey(entity.getId())) {
- LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName()
- + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable());
+ LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName() + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""));
return;
}
if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Delay adding to tracker until after list packets
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 96d37e0845df9b22cf60f9835787789d0d0e4a79..99142f749371828f6f55e4fbab03b22eb519ec1e 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
@@ -25,7 +25,7 @@ public class GoalSelector {
}
};
private final Map<Goal.Flag, WrappedGoal> lockedFlags = Collections.synchronizedMap(new EnumMap<>(Goal.Flag.class));
- private final Set<WrappedGoal> availableGoals = Sets.newCopyOnWriteArraySet();
+ private final Set<WrappedGoal> availableGoals = Sets.newConcurrentHashSet();
private final Supplier<ProfilerFiller> profiler;
private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector

View File

@@ -1,269 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 9 Jan 2023 13:55:50 +0800
Subject: [PATCH] Hearse: Add config system and rename a class
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/HearseConfig.java b/src/main/java/co/earthme/hearse/HearseConfig.java
index 0a1de52bcdf675b9bfcbf14d39959818a7a0cbbb..73b5e76660b5162a7a0b327ddc7dcc3295b86699 100644
--- a/src/main/java/co/earthme/hearse/HearseConfig.java
+++ b/src/main/java/co/earthme/hearse/HearseConfig.java
@@ -1,5 +1,49 @@
package co.earthme.hearse;
+import org.bukkit.configuration.InvalidConfigurationException;
+import org.bukkit.configuration.file.YamlConfiguration;
+import java.io.File;
+import java.io.IOException;
+
public class HearseConfig {
+ private static final YamlConfiguration configEntry = new YamlConfiguration();
+ private static final File CONFIG_FILE = new File("hearse.yml");
+
+ public static void init(){
+ try {
+ configEntry.load(CONFIG_FILE);
+ }catch (IOException ignored){
+ } catch (InvalidConfigurationException e) {
+ e.printStackTrace();
+ }
+ configEntry.options().copyDefaults(true);
+ }
+
+ public static void save(){
+ try {
+ configEntry.save(CONFIG_FILE);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static int getInt(String key,int def){
+ configEntry.addDefault(key,def);
+ return configEntry.getInt(key);
+ }
+
+ public static long getLong(String key,int def){
+ configEntry.addDefault(key,def);
+ return configEntry.getLong(key);
+ }
+
+ public static String getString(String key,String def){
+ configEntry.addDefault(key,def);
+ return configEntry.getString(key);
+ }
+ public static boolean getBoolean(String key,boolean def){
+ configEntry.addDefault(key,def);
+ return configEntry.getBoolean(key);
+ }
}
diff --git a/src/main/java/co/earthme/hearse/server/ServerHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
similarity index 60%
rename from src/main/java/co/earthme/hearse/server/ServerHook.java
rename to src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
index 524a55c3298a079e416c742641af55725a602a2b..8da657836933ae6080e6594ff57dff84155e1820 100644
--- a/src/main/java/co/earthme/hearse/server/ServerHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -1,36 +1,51 @@
package co.earthme.hearse.server;
+import co.earthme.hearse.HearseConfig;
import co.earthme.hearse.concurrent.WorkerThread;
import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
-import org.apache.logging.log4j.LogManager;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-public class ServerHook {
+public class ServerEntityTickHook {
private static volatile boolean firstTick = false;
private static final AtomicInteger threadId = new AtomicInteger();
- private static final WorkerThreadPoolExecutor worker = new WorkerThreadPoolExecutor(
- Runtime.getRuntime().availableProcessors(),
- Runtime.getRuntime().availableProcessors(),
- 100,
- TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<>(),
- task -> {
- WorkerThread workerThread = new WorkerThread(task,"Hearse-Worker-Thread # "+threadId.getAndIncrement());
- return workerThread;
- }
- );
+ private static WorkerThreadPoolExecutor worker;
+ private static boolean asyncEntityEnabled;
public static void executeAsyncTask(Runnable task){
+ if (!asyncEntityEnabled){
+ throw new IllegalStateException();
+ }
worker.execute(task);
}
+ public static void init(){
+ final boolean asyncEntityEnabled1 = HearseConfig.getBoolean("enable-async-entity",true);
+ final int workerCount = HearseConfig.getInt("async-entity-worker-count",Runtime.getRuntime().availableProcessors());
+ if (asyncEntityEnabled1){
+ worker = new WorkerThreadPoolExecutor(
+ workerCount,
+ workerCount,
+ 100,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ task -> {
+ return new WorkerThread(task,"Hearse-Worker-Thread # "+threadId.getAndIncrement());
+ }
+ );
+ }
+ asyncEntityEnabled = asyncEntityEnabled1;
+ }
+
public static void executeAsyncTaskWithMainThreadCallback(Runnable task,Runnable callBack){
+ if (!asyncEntityEnabled){
+ throw new IllegalStateException();
+ }
worker.executeWithSubTask(task,callBack);
}
@@ -39,12 +54,15 @@ public class ServerHook {
firstTick = true;
return;
}
+ if (!asyncEntityEnabled){
+ return;
+ }
worker.runAllSubTasks();
}
public static void callAsyncEntityTick(Entity entity, ServerLevel level){
MinecraftServer.getServer().executeMidTickTasks();
- worker.execute(()->{
+ Runnable task = ()->{
entity.activatedPriorityReset = false;
if (!entity.isRemoved()) {
entity.checkDespawn();
@@ -63,6 +81,11 @@ public class ServerHook {
throwable.printStackTrace();
}
}
- });
+ };
+ if (!asyncEntityEnabled){
+ task.run();
+ return;
+ }
+ worker.execute(task);
}
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index e0e169a4403926ff6be004de1bf5ec2079acb440..4a9fc14ba51f8177242c0573d37fd1b4742aa0ae 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1,11 +1,9 @@
package net.minecraft.server;
-import co.earthme.hearse.server.ServerHook;
+import co.earthme.hearse.HearseConfig;
+import co.earthme.hearse.server.ServerEntityTickHook;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
-import co.aikar.timings.Timings;
-import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
-import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -86,7 +84,6 @@ import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
-import net.minecraft.server.level.TicketType;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
import net.minecraft.server.network.ServerConnectionListener;
@@ -110,17 +107,14 @@ import net.minecraft.util.NativeModuleLister;
import net.minecraft.util.ProgressListener;
import net.minecraft.util.RandomSource;
import net.minecraft.util.SignatureValidator;
-import net.minecraft.util.Unit;
import net.minecraft.util.datafix.DataFixers;
import net.minecraft.util.profiling.EmptyProfileResults;
import net.minecraft.util.profiling.ProfileResults;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.util.profiling.ResultField;
-import net.minecraft.util.profiling.SingleTickProfiler;
import net.minecraft.util.profiling.jfr.JvmProfiler;
import net.minecraft.util.profiling.jfr.callback.ProfiledDuration;
import net.minecraft.util.profiling.metrics.profiling.ActiveMetricsRecorder;
-import net.minecraft.util.profiling.metrics.profiling.InactiveMetricsRecorder;
import net.minecraft.util.profiling.metrics.profiling.MetricsRecorder;
import net.minecraft.util.profiling.metrics.profiling.ServerMetricsSamplersProvider;
import net.minecraft.util.profiling.metrics.storage.MetricsPersister;
@@ -184,12 +178,6 @@ import net.minecraft.world.level.levelgen.PatrolSpawner;
import net.minecraft.world.level.levelgen.PhantomSpawner;
import net.minecraft.world.level.levelgen.WorldDimensions;
import net.minecraft.world.level.levelgen.presets.WorldPresets;
-import org.bukkit.Bukkit;
-import org.bukkit.craftbukkit.CraftServer;
-import org.bukkit.craftbukkit.Main;
-import org.bukkit.craftbukkit.util.CraftChatMessage;
-import org.bukkit.craftbukkit.util.LazyPlayerSet;
-import org.bukkit.event.player.AsyncPlayerChatPreviewEvent;
import org.bukkit.event.server.ServerLoadEvent;
// CraftBukkit end
@@ -411,6 +399,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Paper end
Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
this.paperConfigurations = services.paperConfigurations(); // Paper
+ HearseConfig.init();
+ ServerEntityTickHook.init();
}
// CraftBukkit end
@@ -923,6 +913,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
if (!hasLoggedStop && isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper
// Paper start - kill main thread, and kill it hard
+ HearseConfig.save(); //Hearse
shutdownThread = Thread.currentThread();
org.spigotmc.WatchdogThread.doStop(); // Paper
if (!isSameThread()) {
@@ -1408,7 +1399,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
++this.tickCount;
this.tickChildren(shouldKeepTicking);
- ServerHook.callPostTick();
+ ServerEntityTickHook.callPostTick();
if (i - this.lastServerStatus >= 5000000000L) {
this.lastServerStatus = i;
this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount()));
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index bc3cc08cd6effb9328ec74e206fe24bafc4e3d16..68523cb53573baa8ca98177f40acac3745cd625a 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -2,7 +2,7 @@ package net.minecraft.server.level;
import co.aikar.timings.TimingHistory;
import co.earthme.hearse.concurrent.WorkerThread;
-import co.earthme.hearse.server.ServerHook;
+import co.earthme.hearse.server.ServerEntityTickHook;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -651,7 +651,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
org.spigotmc.ActivationRange.activateEntities(this); // Spigot
this.entityTickList.forEach((entity) -> {
- ServerHook.callAsyncEntityTick(entity,this);
+ ServerEntityTickHook.callAsyncEntityTick(entity,this);
});
this.tickBlockEntities();
}

View File

@@ -1,50 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 9 Jan 2023 14:55:15 +0800
Subject: [PATCH] Hearse: Run tasks when tick start
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
index 52f0c9dddf29a28cc360fbacb923445e5c3f82a6..783c8ea55095dbabe594a9fe3dc604515bd0c2f1 100644
--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
@@ -4,7 +4,6 @@ import io.papermc.paper.util.TickThread;
public class WorkerThread extends TickThread {
-
public WorkerThread(String name) {
super(name);
this.setDaemon(true);
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
index 8da657836933ae6080e6594ff57dff84155e1820..2c2f752e1ae846c2b24c2d46a13473836c5feac6 100644
--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -49,7 +49,7 @@ public class ServerEntityTickHook {
worker.executeWithSubTask(task,callBack);
}
- public static void callPostTick(){
+ public static void callTickStart(){
if (!firstTick){
firstTick = true;
return;
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 4a9fc14ba51f8177242c0573d37fd1b4742aa0ae..3a7435aabbd759222fbac41ab4ca304bd112a0ac 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1396,10 +1396,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
//isOversleep = false;MinecraftTimings.serverOversleep.stopTiming(); // Purpur
// Paper end
new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper
-
+ ServerEntityTickHook.callTickStart();
++this.tickCount;
this.tickChildren(shouldKeepTicking);
- ServerEntityTickHook.callPostTick();
+
if (i - this.lastServerStatus >= 5000000000L) {
this.lastServerStatus = i;
this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount()));

View File

@@ -1,71 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 9 Jan 2023 14:57:23 +0800
Subject: [PATCH] Hearse: Adjust some locks
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
index 799cd9d04d156ed87e9b1dfde75ae15280c9eb0d..a1ff442357dfea868c319fd3c10ae28e6fb81956 100644
--- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
+++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
@@ -3,7 +3,9 @@ package net.minecraft.world.level.redstone;
import com.mojang.logging.LogUtils;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.List;
+import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.StampedLock;
import javax.annotation.Nullable;
@@ -18,7 +20,7 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
private static final Logger LOGGER = LogUtils.getLogger();
private final Level level;
private final int maxChainedNeighborUpdates;
- private final ArrayDeque<CollectingNeighborUpdater.NeighborUpdates> stack = new ArrayDeque<>();
+ private final Deque<NeighborUpdates> stack = new ConcurrentLinkedDeque<>();
private final List<CollectingNeighborUpdater.NeighborUpdates> addedThisLayer = new CopyOnWriteArrayList<>();
private int count = 0;
@@ -28,26 +30,26 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
}
@Override
- public void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) {
+ public synchronized void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) {
this.addAndRun(pos, new CollectingNeighborUpdater.ShapeUpdate(direction, neighborState, pos.immutable(), neighborPos.immutable(), flags));
}
@Override
- public void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) {
+ public synchronized void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) {
this.addAndRun(pos, new CollectingNeighborUpdater.SimpleNeighborUpdate(pos, sourceBlock, sourcePos.immutable()));
}
@Override
- public void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
+ public synchronized void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
this.addAndRun(pos, new CollectingNeighborUpdater.FullNeighborUpdate(state, pos.immutable(), sourceBlock, sourcePos.immutable(), notify));
}
@Override
- public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) {
+ public synchronized void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) {
this.addAndRun(pos, new CollectingNeighborUpdater.MultiNeighborUpdate(pos.immutable(), sourceBlock, except));
}
- private synchronized void addAndRun(BlockPos pos, CollectingNeighborUpdater.NeighborUpdates entry) {
+ private void addAndRun(BlockPos pos, CollectingNeighborUpdater.NeighborUpdates entry) {
boolean bl = this.count > 0;
boolean bl2 = this.maxChainedNeighborUpdates >= 0 && this.count >= this.maxChainedNeighborUpdates;
++this.count;
@@ -64,7 +66,6 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
if (!bl) {
this.runUpdates();
}
-
}
private void runUpdates() {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,187 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Tue, 10 Jan 2023 16:42:02 +0800
Subject: [PATCH] Hearse: Fix some problems and update workerpool
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
index 783c8ea55095dbabe594a9fe3dc604515bd0c2f1..421d4926ac674b5eb12d9613ceb6d20185ea557d 100644
--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
@@ -6,14 +6,10 @@ public class WorkerThread extends TickThread {
public WorkerThread(String name) {
super(name);
- this.setDaemon(true);
- this.setPriority(Thread.NORM_PRIORITY - 2);
}
public WorkerThread(Runnable run, String name) {
super(run, name);
- this.setDaemon(true);
- this.setPriority(Thread.NORM_PRIORITY - 2);
}
public static boolean isWorker(){
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
index 8899c02a2242b51097a03c7e3ca03b8768c60117..7e010bf23c9fc26284212a4388172f5d7d5a4b99 100644
--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
@@ -17,6 +17,14 @@ public class WorkerThreadPoolExecutor extends ThreadPoolExecutor {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, workerThreadFactory::getNewThread, handler);
}
+ public int getCurrentNotProcessingTasks(){
+ return this.getQueue().size();
+ }
+
+ public void clearAllTasks(){
+ this.getQueue().clear();
+ }
+
public void executeWithSubTask(Runnable mainTask,Runnable subTask){
final TaskEntry wrapped = new TaskEntry(subTask,mainTask);
this.taskEntries.offer(wrapped);
diff --git a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e3ae10fcc54b80ff4ec433f136d15d3b9fa4fe4
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
@@ -0,0 +1,22 @@
+package co.earthme.hearse.concurrent.threadfactory;
+
+import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.WorkerThreadFactory;
+import net.minecraft.server.MinecraftServer;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class DefaultWorkerFactory implements WorkerThreadFactory {
+ private static final AtomicInteger poolId = new AtomicInteger();
+ private final AtomicInteger threadId = new AtomicInteger();
+
+ @Override
+ public WorkerThread getNewThread(Runnable task) {
+ final WorkerThread workerThread = new WorkerThread(task,"pool-"+poolId.getAndIncrement()+"-worker-"+threadId.getAndIncrement());
+ if (workerThread.isDaemon()){
+ workerThread.setDaemon(false);
+ }
+ workerThread.setPriority(Thread.NORM_PRIORITY - 2);
+ workerThread.setContextClassLoader(MinecraftServer.class.getClassLoader());
+ return workerThread;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
index 2c2f752e1ae846c2b24c2d46a13473836c5feac6..8605d867bea87095dcf43e1c1ebedbb9180c4480 100644
--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -2,7 +2,9 @@ package co.earthme.hearse.server;
import co.earthme.hearse.HearseConfig;
import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.WorkerThreadFactory;
import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
@@ -13,6 +15,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class ServerEntityTickHook {
private static volatile boolean firstTick = false;
+ private static final WorkerThreadFactory defFactory = new DefaultWorkerFactory();
private static final AtomicInteger threadId = new AtomicInteger();
private static WorkerThreadPoolExecutor worker;
private static boolean asyncEntityEnabled;
@@ -24,19 +27,25 @@ public class ServerEntityTickHook {
worker.execute(task);
}
+ public static void onServerStop() throws InterruptedException {
+ if (!asyncEntityEnabled){
+ return;
+ }
+ worker.shutdown();
+ while (!worker.awaitTermination(100,TimeUnit.MILLISECONDS));
+ }
+
public static void init(){
- final boolean asyncEntityEnabled1 = HearseConfig.getBoolean("enable-async-entity",true);
+ boolean asyncEntityEnabled1 = HearseConfig.getBoolean("enable-async-entity",true);
final int workerCount = HearseConfig.getInt("async-entity-worker-count",Runtime.getRuntime().availableProcessors());
if (asyncEntityEnabled1){
worker = new WorkerThreadPoolExecutor(
workerCount,
workerCount,
- 100,
+ 0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
- task -> {
- return new WorkerThread(task,"Hearse-Worker-Thread # "+threadId.getAndIncrement());
- }
+ defFactory
);
}
asyncEntityEnabled = asyncEntityEnabled1;
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
index 2a8590d46bab64fe27e8dadf80f91ab0662a4352..5ef58831a857fd8aa4ac30147762dc17d773a53e 100644
--- a/src/main/java/net/minecraft/Util.java
+++ b/src/main/java/net/minecraft/Util.java
@@ -65,8 +65,6 @@ 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;
@@ -795,7 +793,7 @@ public class Util {
return objectArrayList;
}
- public static <T> void shuffle(ObjectList<T> list, RandomSource random) {
+ public static <T> void shuffle(ObjectArrayList<T> list, RandomSource random) {
int i = list.size();
for(int j = i; j > 1; --j) {
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 3a7435aabbd759222fbac41ab4ca304bd112a0ac..9c4811a9936f819e201b93e13519abf10d02ac8e 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -914,6 +914,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (!hasLoggedStop && isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper
// Paper start - kill main thread, and kill it hard
HearseConfig.save(); //Hearse
+ try {
+ ServerEntityTickHook.onServerStop(); //Hearse
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
shutdownThread = Thread.currentThread();
org.spigotmc.WatchdogThread.doStop(); // Paper
if (!isSameThread()) {
diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
index 2f53f8f2695231179ff16bb014cd990e94f9ec79..a4e031682329e127a61a9fa03a7383cd3e58d503 100644
--- a/src/main/java/net/minecraft/world/level/Explosion.java
+++ b/src/main/java/net/minecraft/world/level/Explosion.java
@@ -66,7 +66,7 @@ public class Explosion {
private final float radius;
private final DamageSource damageSource;
private final ExplosionDamageCalculator damageCalculator;
- private final ObjectList<BlockPos> toBlow;
+ private final ObjectArrayList<BlockPos> toBlow;
private final Map<Player, Vec3> hitPlayers;
public boolean wasCanceled = false; // CraftBukkit - add field
@@ -85,7 +85,7 @@ 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.createThreadSafe();
- this.toBlow = ObjectLists.synchronize(new ObjectArrayList<>());
+ this.toBlow = new ObjectArrayList<>();
this.hitPlayers = Maps.newConcurrentMap();
this.level = world;
this.source = entity;

View File

@@ -1,113 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Tue, 10 Jan 2023 18:52:00 +0800
Subject: [PATCH] Hearse: Change some config key name
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
index 8605d867bea87095dcf43e1c1ebedbb9180c4480..cf7ee6fda90fa0f6827dc2d1c584151e3b99fb38 100644
--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -36,8 +36,8 @@ public class ServerEntityTickHook {
}
public static void init(){
- boolean asyncEntityEnabled1 = HearseConfig.getBoolean("enable-async-entity",true);
- final int workerCount = HearseConfig.getInt("async-entity-worker-count",Runtime.getRuntime().availableProcessors());
+ boolean asyncEntityEnabled1 = HearseConfig.getBoolean("optimizations.enable-async-entity",true);
+ final int workerCount = HearseConfig.getInt("workers.async-entity-worker-count",Runtime.getRuntime().availableProcessors());
if (asyncEntityEnabled1){
worker = new WorkerThreadPoolExecutor(
workerCount,
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 68523cb53573baa8ca98177f40acac3745cd625a..ac92d1b36590bcc491d56a1eb442477c8f6e2d11 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1126,13 +1126,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
} else { entity.inactiveTick(); } // Paper - EAR 2
//this.getProfiler().pop(); // Purpur
//} finally { timer.stopTiming(); } // Paper - timings // Purpur
- Iterator iterator = entity.getPassengers().iterator();
- while (iterator.hasNext()) {
- Entity entity1 = (Entity) iterator.next();
-
- this.tickPassenger(entity, entity1);
- }
+ for (Entity entity1 : entity.getPassengers()) {
+ this.tickPassenger(entity, entity1);
+ }
// } finally { timer.stopTiming(); } // Paper - timings - move up
// Paper start - log detailed entity tick information
} finally {
@@ -1171,11 +1168,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
// Paper end - EAR 2
//gameprofilerfiller.pop(); // Purpur
- Iterator iterator = passenger.getPassengers().iterator();
-
- while (iterator.hasNext()) {
- Entity entity2 = (Entity) iterator.next();
+ for (Entity entity2 : passenger.getPassengers()) {
this.tickPassenger(passenger, entity2);
}
diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
index dca7c7f83043452b5fef3c1d24a99f08dfaf242a..2a14b665437336aa32ca14fb2137d5bb400e2e42 100644
--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
@@ -1,28 +1,25 @@
package net.minecraft.world.level.entity;
-import java.util.*;
-import java.util.function.Consumer;
-
import com.google.common.collect.Lists;
-import it.unimi.dsi.fastutil.objects.ObjectArraySet;
-import it.unimi.dsi.fastutil.objects.ObjectSets;
import net.minecraft.world.entity.Entity;
+import java.util.List;
+import java.util.function.Consumer;
public class EntityTickList {
public final List<Entity> entities = Lists.newCopyOnWriteArrayList();
public void add(Entity entity) {
io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper
- this.entities.add(entity); // Paper - replace with better logic, do not delay removals/additions
+ this.entities.add(entity);
}
public void remove(Entity entity) {
io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist removal"); // Paper
- this.entities.remove(entity); // Paper - replace with better logic, do not delay removals/additions
+ this.entities.remove(entity);
}
public boolean contains(Entity entity) {
- return this.entities.contains(entity); // Paper - replace with better logic, do not delay removals/additions
+ return this.entities.contains(entity);
}
public void forEach(Consumer<Entity> action) {
@@ -30,6 +27,5 @@ public class EntityTickList {
for (Entity entity : this.entities) {
action.accept(entity);
}
- // Paper end - replace with better logic, do not delay removals/additions
}
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 96cde1f86ca073e7e9e5799bcb12a10adf9230b2..c0ce5a75923e9482d3bd76fbcf730e6c6cd1bbda 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1,5 +1,6 @@
package org.bukkit.craftbukkit;
+import co.earthme.hearse.HearseConfig;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;

View File

@@ -1,302 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 11 Jan 2023 10:10:12 +0800
Subject: [PATCH] Hearse: Add worker command
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/Hearse.java b/src/main/java/co/earthme/hearse/Hearse.java
new file mode 100644
index 0000000000000000000000000000000000000000..79116449c221e0748e938f40366af03f93a4ab9f
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/Hearse.java
@@ -0,0 +1,25 @@
+package co.earthme.hearse;
+
+import co.earthme.hearse.commands.WorkerCommand;
+import co.earthme.hearse.server.ServerEntityTickHook;
+import co.earthme.hearse.workers.WorkerThreadPoolManager;
+import net.minecraft.server.MinecraftServer;
+
+public class Hearse {
+ private static final WorkerThreadPoolManager workerManager = new WorkerThreadPoolManager();
+
+ public static void initAll(){
+ HearseConfig.init();
+ ServerEntityTickHook.init();
+ MinecraftServer.getServer().server.getCommandMap().register("workers","hearse",new WorkerCommand());
+ }
+
+ public static void onServerStop(){
+ HearseConfig.save();
+ workerManager.shutdownAllNow();
+ }
+
+ public static WorkerThreadPoolManager getWorkerManager() {
+ return workerManager;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/commands/WorkerCommand.java b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..9bf8e0bdfed9a30a302c6369a727e8bb394b4670
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java
@@ -0,0 +1,72 @@
+package co.earthme.hearse.commands;
+
+import co.earthme.hearse.Hearse;
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WorkerCommand extends Command {
+ public WorkerCommand() {
+ super("workers");
+ this.setPermission("hearse.commands.workers");
+ this.setDescription("You can see or edit the server workers by using this command");
+ this.setUsage("/workers <status,setThreadCount,forceStop> <workername>");
+ }
+
+ @Override
+ public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
+ final List<String> ret = new ArrayList<>();
+ if (args.length == 1){
+ ret.add("status");
+ ret.add("setThreadCount");
+ ret.add("forceStop");
+ }
+ if (args.length == 2){
+ for (Map.Entry<String, WorkerThreadPoolExecutor> entry : Hearse.getWorkerManager().getManagedWorkers().entrySet()){
+ ret.add(entry.getKey());
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
+ if (args.length >= 2){
+ final String action = args[0];
+ final String workerName = args[1];
+ final WorkerThreadPoolExecutor searchedWorker = Hearse.getWorkerManager().getTargetWorker(workerName);
+ if (searchedWorker == null){
+ sender.sendMessage(ChatColor.RED+"Target worker not found!");
+ return true;
+ }
+ switch (action){
+ case "status":
+ sender.sendMessage(ChatColor.GREEN+"Worker: "+workerName+" Status:"+ searchedWorker);
+ break;
+ case "setThreadCount":
+ if (args.length == 3){
+ try {
+ searchedWorker.setCorePoolSize(Integer.parseInt(args[2]));
+ sender.sendMessage(ChatColor.GREEN+"Finished!");
+ }catch (NumberFormatException e){
+ sender.sendMessage(ChatColor.RED+"Please supply a integer!");
+ }
+ }else{
+ sender.sendMessage(ChatColor.RED+"Please supply a integer!");
+ }
+ break;
+ case "forceStop":
+ searchedWorker.shutdownNow();
+ sender.sendMessage(ChatColor.YELLOW+"Worker "+workerName+" has been stopped!");
+ break;
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
index cf7ee6fda90fa0f6827dc2d1c584151e3b99fb38..18c1f6ee4d4fc422fb2aa41483ce145d34fa39b1 100644
--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -1,14 +1,13 @@
package co.earthme.hearse.server;
+import co.earthme.hearse.Hearse;
import co.earthme.hearse.HearseConfig;
-import co.earthme.hearse.concurrent.WorkerThread;
import co.earthme.hearse.concurrent.WorkerThreadFactory;
import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
-
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -27,14 +26,6 @@ public class ServerEntityTickHook {
worker.execute(task);
}
- public static void onServerStop() throws InterruptedException {
- if (!asyncEntityEnabled){
- return;
- }
- worker.shutdown();
- while (!worker.awaitTermination(100,TimeUnit.MILLISECONDS));
- }
-
public static void init(){
boolean asyncEntityEnabled1 = HearseConfig.getBoolean("optimizations.enable-async-entity",true);
final int workerCount = HearseConfig.getInt("workers.async-entity-worker-count",Runtime.getRuntime().availableProcessors());
@@ -47,6 +38,7 @@ public class ServerEntityTickHook {
new LinkedBlockingQueue<>(),
defFactory
);
+ Hearse.getWorkerManager().addWorker("entity",worker);
}
asyncEntityEnabled = asyncEntityEnabled1;
}
diff --git a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..90dd97491c0313bee031b81aa43fe6df3dda5b4f
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
@@ -0,0 +1,84 @@
+package co.earthme.hearse.workers;
+
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class WorkerThreadPoolManager {
+ private final Map<String,WorkerThreadPoolExecutor> managedWorkers = Maps.newConcurrentMap();
+
+ public void addWorker(String bound,WorkerThreadPoolExecutor worker){
+ this.managedWorkers.put(bound,worker);
+ }
+
+ public void shutdownAll() throws InterruptedException {
+ for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){
+ worker.shutdown();
+ while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {}
+ }
+ }
+
+ public Map<String, WorkerThreadPoolExecutor> getManagedWorkers() {
+ return Maps.newHashMap(this.managedWorkers);
+ }
+
+ public WorkerThreadPoolExecutor getTargetWorker(String bound){
+ return this.managedWorkers.get(bound);
+ }
+
+ public Map<String,List<Runnable>> shutdownAllNow(){
+ final Map<String,List<Runnable>> ret = Maps.newHashMap();
+ for (Map.Entry<String,WorkerThreadPoolExecutor> entry : this.managedWorkers.entrySet()){
+ final String workerName = entry.getKey();
+ final WorkerThreadPoolExecutor worker = entry.getValue();
+ try {
+ final List<Runnable> taskNotRunned = worker.shutdownNow();
+ while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){
+
+ }
+ ret.put(workerName,taskNotRunned);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+ return ret;
+ }
+
+ public Map<String,List<Runnable>> shutdownAllNow(long timeOutCount){
+ final Map<String,List<Runnable>> ret = Maps.newHashMap();
+ for (Map.Entry<String,WorkerThreadPoolExecutor> entry : this.managedWorkers.entrySet()){
+ final String workerName = entry.getKey();
+ final WorkerThreadPoolExecutor worker = entry.getValue();
+ try {
+ long timeCounter = timeOutCount;
+ final List<Runnable> taskNotRunned = worker.shutdownNow();
+ while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){
+ if (timeCounter == 0){
+ break;
+ }
+ timeCounter--;
+ }
+ ret.put(workerName,taskNotRunned);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+ return ret;
+ }
+
+ public void shutdownAll(long singleWorkerAwaitTimeOutCount) throws InterruptedException {
+ long counter = singleWorkerAwaitTimeOutCount;
+ for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){
+ worker.shutdown();
+ while (worker.awaitTermination(1, TimeUnit.MILLISECONDS)) {
+ if (counter == 0){
+ break;
+ }
+ counter--;
+ }
+ counter = singleWorkerAwaitTimeOutCount;
+ }
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 9c4811a9936f819e201b93e13519abf10d02ac8e..fe47aff8654283aa6a17a36226aa9fc7f18f9886 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1,5 +1,6 @@
package net.minecraft.server;
+import co.earthme.hearse.Hearse;
import co.earthme.hearse.HearseConfig;
import co.earthme.hearse.server.ServerEntityTickHook;
import com.google.common.base.Splitter;
@@ -399,8 +400,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Paper end
Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
this.paperConfigurations = services.paperConfigurations(); // Paper
- HearseConfig.init();
- ServerEntityTickHook.init();
}
// CraftBukkit end
@@ -914,11 +913,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (!hasLoggedStop && isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper
// Paper start - kill main thread, and kill it hard
HearseConfig.save(); //Hearse
- try {
- ServerEntityTickHook.onServerStop(); //Hearse
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ Hearse.onServerStop();//Hearse
shutdownThread = Thread.currentThread();
org.spigotmc.WatchdogThread.doStop(); // Paper
if (!isSameThread()) {
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 1cc5cf3dcf33e89092b2aa689170c775007e4e4a..bfdde51d849b6e94af57bd23c0248ce34f020659 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -1,5 +1,6 @@
package net.minecraft.server.dedicated;
+import co.earthme.hearse.Hearse;
import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.DataFixer;
@@ -225,6 +226,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
gg.pufferfish.pufferfish.PufferfishConfig.load(); // Pufferfish
gg.pufferfish.pufferfish.PufferfishCommand.init(); // Pufferfish
org.dreeam.leaf.LeafConfig.load(); // Leaf
+ Hearse.initAll(); //Hearse
this.setPvpAllowed(dedicatedserverproperties.pvp);
this.setFlightAllowed(dedicatedserverproperties.allowFlight);

View File

@@ -1,187 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 11 Jan 2023 10:43:46 +0800
Subject: [PATCH] Hearse: Fix some hearse bugs
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/Hearse.java b/src/main/java/co/earthme/hearse/Hearse.java
index 79116449c221e0748e938f40366af03f93a4ab9f..f8fcb07bc54f4b150dacba325d0a47f0dc7687bc 100644
--- a/src/main/java/co/earthme/hearse/Hearse.java
+++ b/src/main/java/co/earthme/hearse/Hearse.java
@@ -16,7 +16,11 @@ public class Hearse {
public static void onServerStop(){
HearseConfig.save();
- workerManager.shutdownAllNow();
+ try {
+ workerManager.shutdownAll();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
}
public static WorkerThreadPoolManager getWorkerManager() {
diff --git a/src/main/java/co/earthme/hearse/commands/WorkerCommand.java b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java
index 9bf8e0bdfed9a30a302c6369a727e8bb394b4670..1a4a6869a7278beadd97af006f4b5fae578b83ed 100644
--- a/src/main/java/co/earthme/hearse/commands/WorkerCommand.java
+++ b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java
@@ -54,10 +54,10 @@ public class WorkerCommand extends Command {
searchedWorker.setCorePoolSize(Integer.parseInt(args[2]));
sender.sendMessage(ChatColor.GREEN+"Finished!");
}catch (NumberFormatException e){
- sender.sendMessage(ChatColor.RED+"Please supply a integer!");
+ sender.sendMessage(ChatColor.RED+"Please supply an integer!");
}
}else{
- sender.sendMessage(ChatColor.RED+"Please supply a integer!");
+ sender.sendMessage(ChatColor.RED+"Please supply an integer!");
}
break;
case "forceStop":
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
index 18c1f6ee4d4fc422fb2aa41483ce145d34fa39b1..fd7912df03ae39347206fe8db2efa7a8a0e516c8 100644
--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -8,11 +8,15 @@ import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ServerEntityTickHook {
+ private static final Logger logger = LogManager.getLogger();
private static volatile boolean firstTick = false;
private static final WorkerThreadFactory defFactory = new DefaultWorkerFactory();
private static final AtomicInteger threadId = new AtomicInteger();
@@ -21,7 +25,7 @@ public class ServerEntityTickHook {
public static void executeAsyncTask(Runnable task){
if (!asyncEntityEnabled){
- throw new IllegalStateException();
+ throw new RejectedExecutionException();
}
worker.execute(task);
}
@@ -87,6 +91,11 @@ public class ServerEntityTickHook {
task.run();
return;
}
- worker.execute(task);
+ try {
+ worker.execute(task);
+ }catch (RejectedExecutionException e){
+ logger.warn("Worker rejected our task.Falling back to sync entity updating");
+ asyncEntityEnabled = false;
+ }
}
}
diff --git a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
index 90dd97491c0313bee031b81aa43fe6df3dda5b4f..b1eb341728f41c5a62e35944c4b0222758ae8eef 100644
--- a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
+++ b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
@@ -15,15 +15,19 @@ public class WorkerThreadPoolManager {
public void shutdownAll() throws InterruptedException {
for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){
- worker.shutdown();
- while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {}
+ if (!worker.isShutdown()){
+ worker.shutdown();
+ while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {}
+ }
}
}
+ @Deprecated
public Map<String, WorkerThreadPoolExecutor> getManagedWorkers() {
return Maps.newHashMap(this.managedWorkers);
}
+ @Deprecated
public WorkerThreadPoolExecutor getTargetWorker(String bound){
return this.managedWorkers.get(bound);
}
@@ -33,14 +37,16 @@ public class WorkerThreadPoolManager {
for (Map.Entry<String,WorkerThreadPoolExecutor> entry : this.managedWorkers.entrySet()){
final String workerName = entry.getKey();
final WorkerThreadPoolExecutor worker = entry.getValue();
- try {
- final List<Runnable> taskNotRunned = worker.shutdownNow();
- while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){
+ if (!worker.isShutdown()){
+ try {
+ final List<Runnable> taskNotRunned = worker.shutdownNow();
+ while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){
+ }
+ ret.put(workerName,taskNotRunned);
+ }catch (Exception e){
+ e.printStackTrace();
}
- ret.put(workerName,taskNotRunned);
- }catch (Exception e){
- e.printStackTrace();
}
}
return ret;
@@ -51,18 +57,20 @@ public class WorkerThreadPoolManager {
for (Map.Entry<String,WorkerThreadPoolExecutor> entry : this.managedWorkers.entrySet()){
final String workerName = entry.getKey();
final WorkerThreadPoolExecutor worker = entry.getValue();
- try {
- long timeCounter = timeOutCount;
- final List<Runnable> taskNotRunned = worker.shutdownNow();
- while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){
- if (timeCounter == 0){
- break;
+ if (!worker.isShutdown()){
+ try {
+ long timeCounter = timeOutCount;
+ final List<Runnable> taskNotRunned = worker.shutdownNow();
+ while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){
+ if (timeCounter == 0){
+ break;
+ }
+ timeCounter--;
}
- timeCounter--;
+ ret.put(workerName,taskNotRunned);
+ }catch (Exception e){
+ e.printStackTrace();
}
- ret.put(workerName,taskNotRunned);
- }catch (Exception e){
- e.printStackTrace();
}
}
return ret;
@@ -71,14 +79,16 @@ public class WorkerThreadPoolManager {
public void shutdownAll(long singleWorkerAwaitTimeOutCount) throws InterruptedException {
long counter = singleWorkerAwaitTimeOutCount;
for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){
- worker.shutdown();
- while (worker.awaitTermination(1, TimeUnit.MILLISECONDS)) {
- if (counter == 0){
- break;
+ if (!worker.isShutdown()){
+ worker.shutdown();
+ while (worker.awaitTermination(1, TimeUnit.MILLISECONDS)) {
+ if (counter == 0){
+ break;
+ }
+ counter--;
}
- counter--;
+ counter = singleWorkerAwaitTimeOutCount;
}
- counter = singleWorkerAwaitTimeOutCount;
}
}
}

View File

@@ -1,126 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 11 Jan 2023 21:55:57 +0800
Subject: [PATCH] Hearse: Don't wait async tasks when stopping and change
something in CraftEventFactory
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/Hearse.java b/src/main/java/co/earthme/hearse/Hearse.java
index f8fcb07bc54f4b150dacba325d0a47f0dc7687bc..79116449c221e0748e938f40366af03f93a4ab9f 100644
--- a/src/main/java/co/earthme/hearse/Hearse.java
+++ b/src/main/java/co/earthme/hearse/Hearse.java
@@ -16,11 +16,7 @@ public class Hearse {
public static void onServerStop(){
HearseConfig.save();
- try {
- workerManager.shutdownAll();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ workerManager.shutdownAllNow();
}
public static WorkerThreadPoolManager getWorkerManager() {
diff --git a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
index 3e3ae10fcc54b80ff4ec433f136d15d3b9fa4fe4..443ac5267245c20830692b37802afd6ebdf8813b 100644
--- a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
+++ b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
@@ -9,12 +9,17 @@ public class DefaultWorkerFactory implements WorkerThreadFactory {
private static final AtomicInteger poolId = new AtomicInteger();
private final AtomicInteger threadId = new AtomicInteger();
+ public DefaultWorkerFactory(){
+ poolId.getAndIncrement();
+ }
+
@Override
public WorkerThread getNewThread(Runnable task) {
- final WorkerThread workerThread = new WorkerThread(task,"pool-"+poolId.getAndIncrement()+"-worker-"+threadId.getAndIncrement());
+ final WorkerThread workerThread = new WorkerThread(task,"pool-"+poolId.get()+"-worker-"+threadId.getAndIncrement());
if (workerThread.isDaemon()){
workerThread.setDaemon(false);
}
+ workerThread.setDaemon(true);
workerThread.setPriority(Thread.NORM_PRIORITY - 2);
workerThread.setContextClassLoader(MinecraftServer.class.getClassLoader());
return workerThread;
diff --git a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
index b1eb341728f41c5a62e35944c4b0222758ae8eef..527dba288e1988773fd5a89f076f92084034f421 100644
--- a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
+++ b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
@@ -16,6 +16,7 @@ public class WorkerThreadPoolManager {
public void shutdownAll() throws InterruptedException {
for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){
if (!worker.isShutdown()){
+ worker.getQueue().clear(); //Clear the tasks.We don't need wait them
worker.shutdown();
while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {}
}
@@ -40,33 +41,6 @@ public class WorkerThreadPoolManager {
if (!worker.isShutdown()){
try {
final List<Runnable> taskNotRunned = worker.shutdownNow();
- while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){
-
- }
- ret.put(workerName,taskNotRunned);
- }catch (Exception e){
- e.printStackTrace();
- }
- }
- }
- return ret;
- }
-
- public Map<String,List<Runnable>> shutdownAllNow(long timeOutCount){
- final Map<String,List<Runnable>> ret = Maps.newHashMap();
- for (Map.Entry<String,WorkerThreadPoolExecutor> entry : this.managedWorkers.entrySet()){
- final String workerName = entry.getKey();
- final WorkerThreadPoolExecutor worker = entry.getValue();
- if (!worker.isShutdown()){
- try {
- long timeCounter = timeOutCount;
- final List<Runnable> taskNotRunned = worker.shutdownNow();
- while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){
- if (timeCounter == 0){
- break;
- }
- timeCounter--;
- }
ret.put(workerName,taskNotRunned);
}catch (Exception e){
e.printStackTrace();
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 6a52ae70b5f7fd9953b6b2605cae722f606e7fec..af5956bd57141cae08fe839bb2176988a48cd9b8 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -57,6 +57,7 @@ import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
+import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Location; // Paper
import org.bukkit.Material;
@@ -229,7 +230,7 @@ public class CraftEventFactory {
public static final DamageSource MELTING = CraftDamageSource.copyOf(DamageSource.ON_FIRE);
public static final DamageSource POISON = CraftDamageSource.copyOf(DamageSource.MAGIC);
public static org.bukkit.block.Block blockDamage; // For use in EntityDamageByBlockEvent
- public static Entity entityDamage; // For use in EntityDamageByEntityEvent
+ public static volatile Entity entityDamage; // For use in EntityDamageByEntityEvent
// helper methods
private static boolean canBuild(ServerLevel world, Player player, int x, int z) {
@@ -1078,7 +1079,8 @@ public class CraftEventFactory {
} else if (source == DamageSource.MAGIC) {
cause = DamageCause.MAGIC;
} else {
- throw new IllegalStateException(String.format("Unhandled damage of %s by %s from %s", entity, damager.getHandle(), source.msgId));
+ LogManager.getLogger().error(String.format("Unhandled damage of %s by %s from %s", entity, damager.getHandle(), source.msgId));
+ cause = DamageCause.CUSTOM;
}
EntityDamageEvent event = new EntityDamageByEntityEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions, source.isCritical()); // Paper - add critical damage API
event.setCancelled(cancelled);

View File

@@ -1,45 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Thu, 12 Jan 2023 16:08:30 +0800
Subject: [PATCH] Hearse: Fix some NPE errors
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index cdb33a430d0d1671899ab8bb0911193a5688af23..c72920a17178059a29d21e96aeef398f6e0bbbdc 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -1964,6 +1964,10 @@ public abstract class LivingEntity extends Entity {
BlockPos blockposition = this.blockPosition();
BlockState iblockdata = this.getFeetBlockState();
+ if (iblockdata == null){
+ return false;
+ }
+
if (iblockdata.is(BlockTags.CLIMBABLE)) {
this.lastClimbablePos = Optional.of(blockposition);
return true;
diff --git a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
index 0c308e12f9b590fa169babac487c8adc7e3f823c..b61ed4d03848f86ca5e93b0374bbf4ca05369ad2 100644
--- a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
+++ b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
@@ -5,12 +5,16 @@ import com.google.common.collect.Sets;
import java.util.*;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectList;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
+import net.himeki.mcmtfabric.parallelised.ConcurrentDoublyLinkedList;
import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.phys.Vec3;
public class EuclideanGameEventListenerRegistry implements GameEventListenerRegistry {
- private final List<GameEventListener> listeners = Collections.synchronizedList(Lists.newArrayList());
+ private final List<GameEventListener> listeners = new ConcurrentDoublyLinkedList<>();
private final Set<GameEventListener> listenersToRemove = Sets.newConcurrentHashSet();
private final List<GameEventListener> listenersToAdd = Lists.newCopyOnWriteArrayList();
private boolean processing;

View File

@@ -1,564 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Thu, 12 Jan 2023 19:43:48 +0800
Subject: [PATCH] Hearse: Move player ticking to main thread and add lock in
ChunkEntitySlices
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index ae22ca9ea5fd3d78d8c5bf9f1ab96f1129fddc11..1012b8d1d192a946b0982c88c12a0fc0e6051972 100644
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
@@ -33,7 +33,7 @@ public final class ChunkEntitySlices {
public final int chunkX;
public final int chunkZ;
protected final ServerLevel world;
-
+ protected final StampedLock accessLock = new StampedLock(); //Hearse -- fix some entity can't be removed
protected final EntityCollectionBySection allEntities;
protected final EntityCollectionBySection hardCollidingEntities;
protected final Reference2ObjectMap<Class<? extends Entity>, EntityCollectionBySection> entitiesByClass;
@@ -70,68 +70,82 @@ public final class ChunkEntitySlices {
// Paper start - optimise CraftChunk#getEntities
public org.bukkit.entity.Entity[] getChunkEntities() {
- List<org.bukkit.entity.Entity> ret = new java.util.ArrayList<>();
- final Entity[] entities = this.entities.getRawData();
- for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
- final Entity entity = entities[i];
- if (entity == null) {
- continue;
- }
- final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity();
- if (bukkit != null && bukkit.isValid()) {
- ret.add(bukkit);
+ final long id = this.accessLock.readLock();
+ try {
+ List<org.bukkit.entity.Entity> ret = new java.util.ArrayList<>();
+ final Entity[] entities = this.entities.getRawData();
+ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
+ final Entity entity = entities[i];
+ if (entity == null) {
+ continue;
+ }
+ final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity();
+ if (bukkit != null && bukkit.isValid()) {
+ ret.add(bukkit);
+ }
}
+ return ret.toArray(new org.bukkit.entity.Entity[0]);
+ } finally {
+ this.accessLock.unlockRead(id);
}
-
- return ret.toArray(new org.bukkit.entity.Entity[0]);
}
public CompoundTag save() {
- final int len = this.entities.size();
- if (len == 0) {
- return null;
- }
+ final long id = this.accessLock.readLock();
+ try {
+ final int len = this.entities.size();
+ if (len == 0) {
+ return null;
+ }
- final Entity[] rawData = this.entities.getRawData();
- final List<Entity> collectedEntities = new ArrayList<>(len);
- for (int i = 0; i < len; ++i) {
- final Entity entity = rawData[i];
- if (entity.shouldBeSaved()) {
- collectedEntities.add(entity);
+ final Entity[] rawData = this.entities.getRawData();
+ final List<Entity> collectedEntities = new ArrayList<>(len);
+ for (int i = 0; i < len; ++i) {
+ final Entity entity = rawData[i];
+ if (entity.shouldBeSaved()) {
+ collectedEntities.add(entity);
+ }
}
- }
- if (collectedEntities.isEmpty()) {
- return null;
- }
+ if (collectedEntities.isEmpty()) {
+ return null;
+ }
- return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world);
+ return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
// returns true if this chunk has transient entities remaining
public boolean unload() {
- final int len = this.entities.size();
- final Entity[] collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
-
- for (int i = 0; i < len; ++i) {
- final Entity entity = collectedEntities[i];
- if (entity.isRemoved()) {
- // removed by us below
- continue;
- }
- if (entity.shouldBeSaved()) {
- entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
- if (entity.isVehicle()) {
- // we cannot assume that these entities are contained within this chunk, because entities can
- // desync - so we need to remove them all
- for (final Entity passenger : entity.getIndirectPassengers()) {
- passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
+ long id = this.accessLock.readLock();
+ try {
+ final int len = this.entities.size();
+ final Entity[] collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
+
+ for (int i = 0; i < len; ++i) {
+ final Entity entity = collectedEntities[i];
+ if (entity.isRemoved()) {
+ // removed by us below
+ continue;
+ }
+ if (entity.shouldBeSaved()) {
+ entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
+ if (entity.isVehicle()) {
+ // we cannot assume that these entities are contained within this chunk, because entities can
+ // desync - so we need to remove them all
+ for (final Entity passenger : entity.getIndirectPassengers()) {
+ passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
+ }
}
}
}
- }
- return this.entities.size() != 0;
+ return this.entities.size() != 0;
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
private List<Entity> getAllEntities() {
@@ -142,42 +156,65 @@ public final class ChunkEntitySlices {
final Entity[] rawData = this.entities.getRawData();
final List<Entity> collectedEntities = new ArrayList<>(len);
- for (int i = 0; i < len; ++i) {
- collectedEntities.add(rawData[i]);
- }
+ collectedEntities.addAll(Arrays.asList(rawData).subList(0, len));
return collectedEntities;
}
public void callEntitiesLoadEvent() {
- CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ final long id = this.accessLock.readLock();
+ try {
+ CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
public void callEntitiesUnloadEvent() {
- CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ final long id = this.accessLock.readLock();
+ try {
+ CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
// Paper end - optimise CraftChunk#getEntities
public boolean isEmpty() {
- return this.entities.size() == 0;
+ final long id = this.accessLock.readLock();
+ try {
+ return this.entities.size() == 0;
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
public void mergeInto(final ChunkEntitySlices slices) {
- final Entity[] entities = this.entities.getRawData();
- for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
- final Entity entity = entities[i];
- slices.addEntity(entity, entity.sectionY);
+ final long id = this.accessLock.readLock();
+ try {
+ final Entity[] entities = this.entities.getRawData();
+ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
+ final Entity entity = entities[i];
+ slices.addEntity(entity, entity.sectionY);
+ }
+ } finally {
+ this.accessLock.unlockRead(id);
}
}
public void updateStatus(final ChunkHolder.FullChunkStatus status, final EntityLookup lookup) {
this.status = status;
- final Entity[] entities = this.entities.getRawData();
+ Entity[] entities;
- for (int i = 0, size = this.entities.size(); i < size; ++i) {
- final Entity entity = entities[i];
+ final long id = this.accessLock.readLock();
+ try {
+ entities = Arrays.copyOf(this.entities.getRawData(), this.entities.getRawData().length);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+ for (final Entity entity : entities) {
final Visibility oldVisibility = EntityLookup.getEntityStatus(entity);
entity.chunkStatus = status;
final Visibility newVisibility = EntityLookup.getEntityStatus(entity);
@@ -187,70 +224,95 @@ public final class ChunkEntitySlices {
}
public boolean addEntity(final Entity entity, final int chunkSection) {
- if (!this.entities.add(entity)) {
- return false;
- }
- entity.chunkStatus = this.status;
- final int sectionIndex = chunkSection - this.minSection;
-
- this.allEntities.addEntity(entity, sectionIndex);
+ long id = this.accessLock.writeLock();
+ try {
+ if (!this.entities.add(entity)) {
+ return false;
+ }
+ entity.chunkStatus = this.status;
+ final int sectionIndex = chunkSection - this.minSection;
- if (entity.hardCollides()) {
- this.hardCollidingEntities.addEntity(entity, sectionIndex);
- }
+ this.allEntities.addEntity(entity, sectionIndex);
- for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
- this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) {
- final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
+ if (entity.hardCollides()) {
+ this.hardCollidingEntities.addEntity(entity, sectionIndex);
+ }
- if (entry.getKey().isInstance(entity)) {
- entry.getValue().addEntity(entity, sectionIndex);
+ for (final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry : this.entitiesByClass.reference2ObjectEntrySet()) {
+ if (entry.getKey().isInstance(entity)) {
+ entry.getValue().addEntity(entity, sectionIndex);
+ }
}
+ } finally {
+ this.accessLock.unlockWrite(id);
}
-
return true;
}
public boolean removeEntity(final Entity entity, final int chunkSection) {
- if (!this.entities.remove(entity)) {
- return false;
- }
- entity.chunkStatus = null;
- final int sectionIndex = chunkSection - this.minSection;
-
- this.allEntities.removeEntity(entity, sectionIndex);
+ long id = this.accessLock.writeLock();
+ try {
+ if (!this.entities.remove(entity)) {
+ return false;
+ }
+ entity.chunkStatus = null;
+ final int sectionIndex = chunkSection - this.minSection;
- if (entity.hardCollides()) {
- this.hardCollidingEntities.removeEntity(entity, sectionIndex);
- }
+ this.allEntities.removeEntity(entity, sectionIndex);
- for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
- this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) {
- final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
+ if (entity.hardCollides()) {
+ this.hardCollidingEntities.removeEntity(entity, sectionIndex);
+ }
- if (entry.getKey().isInstance(entity)) {
- entry.getValue().removeEntity(entity, sectionIndex);
+ for (final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry : this.entitiesByClass.reference2ObjectEntrySet()) {
+ if (entry.getKey().isInstance(entity)) {
+ entry.getValue().removeEntity(entity, sectionIndex);
+ }
}
+ } finally {
+ this.accessLock.unlockWrite(id);
}
-
return true;
}
public void getHardCollidingEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- this.hardCollidingEntities.getEntities(except, box, into, predicate);
+ final long id = this.accessLock.readLock();
+ try {
+ this.hardCollidingEntities.getEntities(except, box, into, predicate);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate);
+ final long id = this.accessLock.readLock();
+ try {
+ this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+
}
public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- this.allEntities.getEntities(except, box, into, predicate);
+ final long id = this.accessLock.readLock();
+ try {
+ this.allEntities.getEntities(except, box, into, predicate);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+
}
public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate) {
- this.allEntities.getEntities(type, box, (List)into, (Predicate)predicate);
+ final long id = this.accessLock.readLock();
+ try {
+ this.allEntities.getEntities(type, box, (List) into, (Predicate) predicate);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+
}
protected EntityCollectionBySection initClass(final Class<? extends Entity> clazz) {
@@ -278,12 +340,17 @@ public final class ChunkEntitySlices {
public <T extends Entity> void getEntities(final Class<? extends T> clazz, final Entity except, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate) {
- EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
- if (collection != null) {
- collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate);
- } else {
- this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz));
- collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate);
+ final long id = this.accessLock.readLock();
+ try {
+ EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
+ if (collection != null) {
+ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate);
+ } else {
+ this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz));
+ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate);
+ }
+ } finally {
+ this.accessLock.unlockRead(id);
}
}
@@ -300,7 +367,7 @@ public final class ChunkEntitySlices {
}
public BasicEntityList(final int cap) {
- this.storage = (E[])(cap <= 0 ? EMPTY : new Entity[cap]);
+ this.storage = (E[]) (cap <= 0 ? EMPTY : new Entity[cap]);
}
public synchronized boolean isEmpty() {
@@ -313,7 +380,7 @@ public final class ChunkEntitySlices {
private void resize() {
if (this.storage == EMPTY) {
- this.storage = (E[])new Entity[DEFAULT_CAPACITY];
+ this.storage = (E[]) new Entity[DEFAULT_CAPACITY];
} else {
this.storage = Arrays.copyOf(this.storage, this.storage.length * 2);
}
@@ -396,7 +463,7 @@ public final class ChunkEntitySlices {
list.add(entity);
++this.count;
- }finally {
+ } finally {
this.listLock.unlockWrite(id);
}
}
@@ -416,7 +483,7 @@ public final class ChunkEntitySlices {
this.entitiesBySection[sectionIndex] = null;
this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1)));
}
- }finally {
+ } finally {
this.listLock.unlockWrite(id);
}
}
@@ -459,65 +526,65 @@ public final class ChunkEntitySlices {
into.add(entity);
}
}
- }finally {
+ } finally {
this.listLock.unlockRead(id);
}
}
public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List<Entity> into,
final Predicate<? super Entity> predicate) {
- final long id = this.listLock.readLock();
- try {
- 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);
- }
- }
- }
- }
- }finally {
- this.listLock.unlockRead(id);
- }
+ into.add(part);
+ }
+ }
+ }
+ }
+ } finally {
+ this.listLock.unlockRead(id);
+ }
}
public void getEntitiesWithEnderDragonParts(final Entity except, final Class<?> clazz, final AABB box, final List<Entity> into,
@@ -557,7 +624,7 @@ public final class ChunkEntitySlices {
} // else: continue to test the ender dragon parts
if (entity instanceof EnderDragon) {
- for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) {
+ for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) {
if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) {
continue;
}
@@ -571,7 +638,7 @@ public final class ChunkEntitySlices {
}
}
}
- }finally {
+ } finally {
this.listLock.unlockRead(id);
}
}
@@ -608,14 +675,14 @@ public final class ChunkEntitySlices {
continue;
}
- if (predicate != null && !predicate.test((T)entity)) {
+ if (predicate != null && !predicate.test((T) entity)) {
continue;
}
- into.add((T)entity);
+ into.add((T) entity);
}
}
- }finally {
+ } finally {
this.listLock.unlockRead(id);
}
}

View File

@@ -1,460 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Thu, 12 Jan 2023 20:18:22 +0800
Subject: [PATCH] Hearse: Remove some lock in ChunkEntitySlices
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index 1012b8d1d192a946b0982c88c12a0fc0e6051972..b2eb6feffb2191c450175547c1371623ce5185eb 100644
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
@@ -191,15 +191,19 @@ public final class ChunkEntitySlices {
public void mergeInto(final ChunkEntitySlices slices) {
final long id = this.accessLock.readLock();
+ final List<Entity> cop = new ArrayList<>();
try {
final Entity[] entities = this.entities.getRawData();
for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
final Entity entity = entities[i];
- slices.addEntity(entity, entity.sectionY);
+ cop.add(entity);
}
} finally {
this.accessLock.unlockRead(id);
}
+ for (Entity entity : cop){
+ slices.addEntity(entity, entity.sectionY);
+ }
}
public void updateStatus(final ChunkHolder.FullChunkStatus status, final EntityLookup lookup) {
@@ -370,11 +374,11 @@ public final class ChunkEntitySlices {
this.storage = (E[]) (cap <= 0 ? EMPTY : new Entity[cap]);
}
- public synchronized boolean isEmpty() {
+ public boolean isEmpty() {
return this.size == 0;
}
- public synchronized int size() {
+ public int size() {
return this.size;
}
@@ -386,7 +390,7 @@ public final class ChunkEntitySlices {
}
}
- public synchronized void add(final E entity) {
+ public void add(final E entity) {
final int idx = this.size++;
if (idx >= this.storage.length) {
this.resize();
@@ -396,7 +400,7 @@ public final class ChunkEntitySlices {
}
}
- public synchronized int indexOf(final E entity) {
+ public int indexOf(final E entity) {
final E[] storage = this.storage;
for (int i = 0, len = Math.min(this.storage.length, this.size); i < len; ++i) {
@@ -408,7 +412,7 @@ public final class ChunkEntitySlices {
return -1;
}
- public synchronized boolean remove(final E entity) {
+ public boolean remove(final E entity) {
final int idx = this.indexOf(entity);
if (idx == -1) {
return false;
@@ -425,7 +429,7 @@ public final class ChunkEntitySlices {
return true;
}
- public synchronized boolean has(final E entity) {
+ public boolean has(final E entity) {
return this.indexOf(entity) != -1;
}
}
@@ -436,7 +440,6 @@ 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;
@@ -448,242 +451,212 @@ public final class ChunkEntitySlices {
}
public void addEntity(final Entity entity, final int sectionIndex) {
- final long id = this.listLock.writeLock();
- try {
- BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
-
- if (list != null && list.has(entity)) {
- return;
- }
+ BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
- if (list == null) {
- this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>();
- this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1)));
- }
+ if (list != null && list.has(entity)) {
+ return;
+ }
- list.add(entity);
- ++this.count;
- } finally {
- this.listLock.unlockWrite(id);
+ if (list == null) {
+ this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>();
+ this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1)));
}
+
+ list.add(entity);
+ ++this.count;
}
public void removeEntity(final Entity entity, final int sectionIndex) {
- final long id = this.listLock.writeLock();
- try {
- final BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
+ 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)));
- }
- } finally {
- this.listLock.unlockWrite(id);
+ if (list.isEmpty()) {
+ this.entitiesBySection[sectionIndex] = null;
+ this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1)));
}
}
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- final long id = this.listLock.readLock();
- try {
- if (this.count == 0) {
- return;
- }
-
- final int minSection = this.manager.minSection;
- final int maxSection = this.manager.maxSection;
+ if (this.count == 0) {
+ return;
+ }
- 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 minSection = this.manager.minSection;
+ final int maxSection = this.manager.maxSection;
- final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
+ 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);
- for (int section = min; section <= max; ++section) {
- final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
+ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
- if (list == null) {
- continue;
- }
+ for (int section = min; section <= max; ++section) {
+ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
- final Entity[] storage = list.storage;
+ if (list == null) {
+ continue;
+ }
- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
- final Entity entity = storage[i];
+ final Entity[] storage = list.storage;
- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
- continue;
- }
+ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
+ final Entity entity = storage[i];
- if (predicate != null && !predicate.test(entity)) {
- continue;
- }
+ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
+ continue;
+ }
- into.add(entity);
+ if (predicate != null && !predicate.test(entity)) {
+ continue;
}
+
+ 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) {
- final long id = this.listLock.readLock();
- try {
- if (this.count == 0) {
- return;
- }
-
- final int minSection = this.manager.minSection;
- final int maxSection = this.manager.maxSection;
+ if (this.count == 0) {
+ return;
+ }
- 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 minSection = this.manager.minSection;
+ final int maxSection = this.manager.maxSection;
- final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
+ 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);
- for (int section = min; section <= max; ++section) {
- final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
+ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
- if (list == null) {
- continue;
- }
+ for (int section = min; section <= max; ++section) {
+ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
- final Entity[] storage = list.storage;
+ if (list == null) {
+ continue;
+ }
- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
- final Entity entity = storage[i];
+ final Entity[] storage = list.storage;
- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
- continue;
- }
+ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
+ final Entity entity = storage[i];
- if (predicate == null || predicate.test(entity)) {
- into.add(entity);
- } // else: continue to test the ender dragon parts
+ if (entity == null || entity == except || !entity.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(entity)) {
+ into.add(entity);
+ } // else: continue to test the ender dragon parts
- if (predicate != null && !predicate.test(part)) {
- continue;
- }
+ if (entity instanceof EnderDragon) {
+ for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) {
+ if (part == except || !part.getBoundingBox().intersects(box)) {
+ continue;
+ }
- into.add(part);
+ if (predicate != null && !predicate.test(part)) {
+ continue;
}
+
+ 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) {
- final long id = this.listLock.readLock();
- try {
- if (this.count == 0) {
- return;
- }
+ 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;
- }
-
- final Entity[] storage = list.storage;
+ if (list == null) {
+ continue;
+ }
- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
- final Entity entity = storage[i];
+ final Entity[] storage = list.storage;
- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
- continue;
- }
+ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
+ final Entity entity = storage[i];
- if (predicate == null || predicate.test(entity)) {
- into.add(entity);
- } // else: continue to test the ender dragon parts
+ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
+ 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(entity)) {
+ into.add(entity);
+ } // else: continue to test the ender dragon parts
- if (predicate != null && !predicate.test(part)) {
- continue;
- }
+ if (entity instanceof EnderDragon) {
+ for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) {
+ if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) {
+ continue;
+ }
- into.add(part);
+ if (predicate != null && !predicate.test(part)) {
+ continue;
}
+
+ 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) {
- final long id = this.listLock.readLock();
- try {
- if (this.count == 0) {
- return;
- }
-
- final int minSection = this.manager.minSection;
- final int maxSection = this.manager.maxSection;
+ if (this.count == 0) {
+ return;
+ }
- 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 minSection = this.manager.minSection;
+ final int maxSection = this.manager.maxSection;
- final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
+ 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);
- for (int section = min; section <= max; ++section) {
- final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
+ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
- if (list == null) {
- continue;
- }
+ for (int section = min; section <= max; ++section) {
+ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
- final Entity[] storage = list.storage;
+ if (list == null) {
+ continue;
+ }
- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
- final Entity entity = storage[i];
+ final Entity[] storage = list.storage;
- if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) {
- continue;
- }
+ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
+ final Entity entity = storage[i];
- if (predicate != null && !predicate.test((T) entity)) {
- continue;
- }
+ if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) {
+ continue;
+ }
- into.add((T) entity);
+ if (predicate != null && !predicate.test((T) entity)) {
+ continue;
}
+
+ into.add((T) entity);
}
- } finally {
- this.listLock.unlockRead(id);
}
}
}

View File

@@ -1,91 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Fri, 13 Jan 2023 09:01:48 +0800
Subject: [PATCH] Hearse: Fix a dead lock
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index b2eb6feffb2191c450175547c1371623ce5185eb..78bd56f150543d2e38cf1606b133d682194424d2 100644
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
@@ -39,9 +39,9 @@ public final class ChunkEntitySlices {
protected final Reference2ObjectMap<Class<? extends Entity>, EntityCollectionBySection> entitiesByClass;
protected final EntityList entities = new EntityList();
- public ChunkHolder.FullChunkStatus status;
+ public volatile ChunkHolder.FullChunkStatus status;
- protected boolean isTransient;
+ protected volatile boolean isTransient;
public boolean isTransient() {
return this.isTransient;
@@ -119,33 +119,33 @@ public final class ChunkEntitySlices {
// returns true if this chunk has transient entities remaining
public boolean unload() {
+ Entity[] collectedEntities;
+ int len;
long id = this.accessLock.readLock();
try {
- final int len = this.entities.size();
- final Entity[] collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
-
- for (int i = 0; i < len; ++i) {
- final Entity entity = collectedEntities[i];
- if (entity.isRemoved()) {
- // removed by us below
- continue;
- }
- if (entity.shouldBeSaved()) {
- entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
- if (entity.isVehicle()) {
- // we cannot assume that these entities are contained within this chunk, because entities can
- // desync - so we need to remove them all
- for (final Entity passenger : entity.getIndirectPassengers()) {
- passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
- }
+ len = this.entities.size();
+ collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
+ for (int i = 0; i < len; ++i) {
+ final Entity entity = collectedEntities[i];
+ if (entity.isRemoved()) {
+ // removed by us below
+ continue;
+ }
+ if (entity.shouldBeSaved()) {
+ entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
+ if (entity.isVehicle()) {
+ // we cannot assume that these entities are contained within this chunk, because entities can
+ // desync - so we need to remove them all
+ for (final Entity passenger : entity.getIndirectPassengers()) {
+ passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
}
}
}
-
- return this.entities.size() != 0;
- } finally {
- this.accessLock.unlockRead(id);
}
+ return this.entities.size() != 0;
}
private List<Entity> getAllEntities() {
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 483c19ac24b074cbdb924d684c0892ddc546af3e..4216b4bba7c601660b7c39733f626f95d9b20507 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -4367,6 +4367,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
// Paper start
this.setPosRaw(x, y, z, false);
}
+
public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
// Paper start - block invalid positions
if (!checkPosition(this, x, y, z)) {

View File

@@ -1,67 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Fri, 13 Jan 2023 09:35:26 +0800
Subject: [PATCH] Hearse: Fix a concurrent problem
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index 78bd56f150543d2e38cf1606b133d682194424d2..122699937606f5e00e356f5c1ea12db0563508a3 100644
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
@@ -2,7 +2,6 @@ package io.papermc.paper.world;
import com.destroystokyo.paper.util.maplist.EntityList;
import io.papermc.paper.chunk.system.entity.EntityLookup;
-import io.papermc.paper.util.TickThread;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
@@ -21,7 +20,6 @@ import net.minecraft.world.phys.AABB;
import org.bukkit.craftbukkit.event.CraftEventFactory;
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;
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
index e254b2d04e4fc1dc76c26f61ea38aeb27755143f..948abd93c64a5b3679f3552945d7a9a2edaf7c3f 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -17,6 +17,9 @@ import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.annotation.Nullable;
+
+import it.unimi.dsi.fastutil.shorts.ShortLists;
+import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentShortHashSet;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
@@ -384,7 +387,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
public static ShortList getOrCreateOffsetList(ShortList[] lists, int index) {
if (lists[index] == null) {
- lists[index] = new ShortArrayList();
+ lists[index] = ShortLists.synchronize(new ShortArrayList());
}
return lists[index];
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 a78f04ae5eb2d0f11579cb0a40384f3f103371af..f1aae5aa6d2951e21e5e0f84fc87117d41876695 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -1075,10 +1075,8 @@ public class LevelChunk extends ChunkAccess {
for (int i = 0; i < this.postProcessing.length; ++i) {
if (this.postProcessing[i] != null) {
- ShortListIterator shortlistiterator = this.postProcessing[i].iterator();
- while (shortlistiterator.hasNext()) {
- Short oshort = (Short) shortlistiterator.next();
+ for (Short oshort : this.postProcessing[i]) {
BlockPos blockposition = ProtoChunk.unpackOffsetCoordinates(oshort, this.getSectionYFromSectionIndex(i), chunkcoordintpair);
BlockState iblockdata = this.getBlockState(blockposition);
FluidState fluid = iblockdata.getFluidState();

View File

@@ -1,25 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Fri, 13 Jan 2023 15:22:42 +0800
Subject: [PATCH] Hearse: Remove a NPE fix
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
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 d6951b05128fea7eb5f1b40837cea77e0c209165..26e1b4060f2a93cb659170f83e6ce64086e0eb0c 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,10 +423,8 @@ 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!=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 (!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)) {

View File

@@ -1,120 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Fri, 13 Jan 2023 15:49:11 +0800
Subject: [PATCH] Hearse: Add new command
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/co/earthme/hearse/Hearse.java b/src/main/java/co/earthme/hearse/Hearse.java
index 79116449c221e0748e938f40366af03f93a4ab9f..692fef51b2f15dd1ddc28773a381b9da3b42725e 100644
--- a/src/main/java/co/earthme/hearse/Hearse.java
+++ b/src/main/java/co/earthme/hearse/Hearse.java
@@ -1,5 +1,6 @@
package co.earthme.hearse;
+import co.earthme.hearse.commands.EntityCountCommand;
import co.earthme.hearse.commands.WorkerCommand;
import co.earthme.hearse.server.ServerEntityTickHook;
import co.earthme.hearse.workers.WorkerThreadPoolManager;
@@ -12,6 +13,7 @@ public class Hearse {
HearseConfig.init();
ServerEntityTickHook.init();
MinecraftServer.getServer().server.getCommandMap().register("workers","hearse",new WorkerCommand());
+ MinecraftServer.getServer().server.getCommandMap().register("entitycount","hearse",new EntityCountCommand());
}
public static void onServerStop(){
diff --git a/src/main/java/co/earthme/hearse/commands/EntityCountCommand.java b/src/main/java/co/earthme/hearse/commands/EntityCountCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..563485b8c657a7a96ff3a391c8fa19159e6dd8f6
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/commands/EntityCountCommand.java
@@ -0,0 +1,36 @@
+package co.earthme.hearse.commands;
+
+import com.google.common.collect.Maps;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import java.util.Map;
+
+public class EntityCountCommand extends Command {
+ public EntityCountCommand() {
+ super("entitycount");
+ }
+
+ @Override
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
+ final Map<String,Integer> counts = Maps.newHashMap();
+ for (ServerLevel level : MinecraftServer.getServer().getAllLevels()){
+ for (Entity entity : level.entityTickList.entities){
+ final String name = entity.getType().getName();
+ if (!counts.containsKey(name)){
+ counts.put(name,0);
+ }
+ counts.replace(name,counts.get(name));
+ }
+ }
+ sender.sendMessage("Exists entity Counts:");
+ for (Map.Entry<String,Integer> entry : counts.entrySet()){
+ sender.sendMessage(ChatColor.BLUE+String.format("%s:%s",entry.getKey(),entry.getValue()));
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index ac92d1b36590bcc491d56a1eb442477c8f6e2d11..39c3aaf91514bd8a2f9f04496e25a6253442939f 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -146,7 +146,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
public final ServerChunkCache chunkSource;
private final MinecraftServer server;
public final PrimaryLevelData serverLevelData; // CraftBukkit - type
- final EntityTickList entityTickList;
+ public final EntityTickList entityTickList;
//public final PersistentEntitySectionManager<Entity> entityManager; // Paper - rewrite chunk system
private final GameEventDispatcher gameEventDispatcher;
public boolean noSave;
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
index f7e8b6e1872a397c96afc938754726b0d4e493b4..2448673ee847fe3bc05f1269737aae5b43ae8291 100644
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
@@ -308,6 +308,16 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
return (EntityType) Registry.register(BuiltInRegistries.ENTITY_TYPE, id, (EntityType<T>) type.build(id)); // CraftBukkit - decompile error
}
+ // Purpur start
+ public static EntityType<?> getFromBukkitType(org.bukkit.entity.EntityType bukkitType) {
+ return getFromKey(new ResourceLocation(bukkitType.getKey().toString()));
+ }
+
+ public static EntityType<?> getFromKey(ResourceLocation location) {
+ return BuiltInRegistries.ENTITY_TYPE.get(location);
+ }
+ // Purpur end
+
public static ResourceLocation getKey(EntityType<?> type) {
return BuiltInRegistries.ENTITY_TYPE.getKey(type);
}
@@ -522,6 +532,16 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
return this.category;
}
+ // Purpur start
+ public String getName() {
+ return BuiltInRegistries.ENTITY_TYPE.getKey(this).getPath();
+ }
+
+ public String getTranslatedName() {
+ return getDescription().getString();
+ }
+ // Purpur end
+
public String getDescriptionId() {
if (this.descriptionId == null) {
this.descriptionId = Util.makeDescriptionId("entity", BuiltInRegistries.ENTITY_TYPE.getKey(this));

View File

@@ -1,225 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 14 Jan 2023 09:27:14 +0800
Subject: [PATCH] Hearse: Fix a NoSuchElementError in Delayed8 and Delayed26
distance propagators
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
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 762f09c8f374fbccc9f5be985401ad334e1655a0..e831738a2988746fe4e065f6ded811a8bdf5dabe 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,42 @@ public final class Delayed26WayDistancePropagator3D {
protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[level];
- queue.queuedCoordinates.add(coordinate);
- queue.queuedLevels.add(level);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
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.add(coordinate);
- queue.queuedLevels.add(level);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
this.levelIncreaseWorkQueueBitset |= (1L << index);
}
protected final void addToRemoveWorkQueue(final long coordinate, final byte level) {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[level];
- queue.queuedCoordinates.add(coordinate);
- queue.queuedLevels.add(level);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
this.levelRemoveWorkQueueBitset |= (1L << level);
}
@@ -163,9 +181,20 @@ public final class Delayed26WayDistancePropagator3D {
this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex];
- while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirst();
- byte level = queue.queuedLevels.removeFirst();
+ while (true) {
+
+ long coordinate;
+ byte level;
+ final long id = queue.lock.writeLock();
+ try {
+ if (queue.queuedLevels.isEmpty()){
+ break;
+ }
+ coordinate = queue.queuedCoordinates.removeFirst();
+ level = queue.queuedLevels.removeFirst();
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
final boolean neighbourCheck = level < 0;
@@ -232,9 +261,19 @@ public final class Delayed26WayDistancePropagator3D {
this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) {
final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[queueIndex];
- while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirst();
- final byte level = queue.queuedLevels.removeFirst();
+ while (true) {
+ long coordinate;
+ byte level;
+ final long id = queue.lock.writeLock();
+ try {
+ if (queue.queuedLevels.isEmpty()){
+ break;
+ }
+ coordinate = queue.queuedCoordinates.removeFirst();
+ level = queue.queuedLevels.removeFirst();
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
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 8c5a51b5992eccf3627f326e164288b5f6bbcff6..0fa95d81bafc7fe5c1bede7a0608b54795a78fa0 100644
--- a/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java
+++ b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java
@@ -8,6 +8,7 @@ import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.locks.StampedLock;
public final class Delayed8WayDistancePropagator2D {
@@ -357,24 +358,42 @@ public final class Delayed8WayDistancePropagator2D {
protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) {
final WorkQueue queue = this.levelIncreaseWorkQueues[level];
- queue.queuedCoordinates.add(coordinate);
- queue.queuedLevels.add(level);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
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.add(coordinate);
- queue.queuedLevels.add(level);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
this.levelIncreaseWorkQueueBitset |= (1L << index);
}
protected final void addToRemoveWorkQueue(final long coordinate, final byte level) {
final WorkQueue queue = this.levelRemoveWorkQueues[level];
- queue.queuedCoordinates.add(coordinate);
- queue.queuedLevels.add(level);
+
+ final long id = queue.lock.writeLock();
+ try {
+ queue.queuedCoordinates.add(coordinate);
+ queue.queuedLevels.add(level);
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
this.levelRemoveWorkQueueBitset |= (1L << level);
}
@@ -426,9 +445,19 @@ public final class Delayed8WayDistancePropagator2D {
this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) {
final WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex];
- while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirst();
- byte level = queue.queuedLevels.removeFirst();
+ while (true) {
+ byte level;
+ long coordinate;
+ final long id = queue.lock.writeLock();
+ try {
+ if (queue.queuedLevels.isEmpty()){
+ break;
+ }
+ coordinate = queue.queuedCoordinates.removeFirst();
+ level = queue.queuedLevels.removeFirst();
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
final boolean neighbourCheck = level < 0;
@@ -492,9 +521,20 @@ public final class Delayed8WayDistancePropagator2D {
this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) {
final WorkQueue queue = this.levelRemoveWorkQueues[queueIndex];
- while (!queue.queuedLevels.isEmpty()) {
- final long coordinate = queue.queuedCoordinates.removeFirst();
- final byte level = queue.queuedLevels.removeFirst();
+ while (true) {
+ long coordinate;
+ byte level;
+
+ final long id = queue.lock.writeLock();
+ try {
+ if (queue.queuedLevels.isEmpty()){
+ break;
+ }
+ coordinate = queue.queuedCoordinates.removeFirst();
+ level = queue.queuedLevels.removeFirst();
+ }finally {
+ queue.lock.unlockWrite(id);
+ }
final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level);
if (currentLevel == 0) {
@@ -681,6 +721,7 @@ public final class Delayed8WayDistancePropagator2D {
protected static final class WorkQueue {
public final Deque<Long> queuedCoordinates = new ConcurrentLinkedDeque<>();
public final Deque<Byte> queuedLevels = new ConcurrentLinkedDeque<>();
+ public final StampedLock lock = new StampedLock();
}
}

View File

@@ -1,317 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 14 Jan 2023 10:14:54 +0800
Subject: [PATCH] Hearse: Optimized some locks in ChunkEntitySlices
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index 122699937606f5e00e356f5c1ea12db0563508a3..b12c02962e9dad92ae79d762887c65db10765488 100644
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
@@ -66,50 +66,67 @@ public final class ChunkEntitySlices {
this.status = status;
}
+ private org.bukkit.entity.Entity[] getChunkEntitiesUnsafe(){
+ List<org.bukkit.entity.Entity> ret = new java.util.ArrayList<>();
+ final Entity[] entities = this.entities.getRawData();
+ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
+ final Entity entity = entities[i];
+ if (entity == null) {
+ continue;
+ }
+ final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity();
+ if (bukkit != null && bukkit.isValid()) {
+ ret.add(bukkit);
+ }
+ }
+ return ret.toArray(new org.bukkit.entity.Entity[0]);
+ }
+
// Paper start - optimise CraftChunk#getEntities
public org.bukkit.entity.Entity[] getChunkEntities() {
- final long id = this.accessLock.readLock();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)) {
+ return this.getChunkEntitiesUnsafe();
+ }
+
+ id = this.accessLock.readLock();
try {
- List<org.bukkit.entity.Entity> ret = new java.util.ArrayList<>();
- final Entity[] entities = this.entities.getRawData();
- for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
- final Entity entity = entities[i];
- if (entity == null) {
- continue;
- }
- final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity();
- if (bukkit != null && bukkit.isValid()) {
- ret.add(bukkit);
- }
- }
- return ret.toArray(new org.bukkit.entity.Entity[0]);
+ return this.getChunkEntitiesUnsafe();
} finally {
this.accessLock.unlockRead(id);
}
}
- public CompoundTag save() {
- final long id = this.accessLock.readLock();
- try {
- final int len = this.entities.size();
- if (len == 0) {
- return null;
- }
+ private CompoundTag saveUnsafe(){
+ final int len = this.entities.size();
+ if (len == 0) {
+ return null;
+ }
- final Entity[] rawData = this.entities.getRawData();
- final List<Entity> collectedEntities = new ArrayList<>(len);
- for (int i = 0; i < len; ++i) {
- final Entity entity = rawData[i];
- if (entity.shouldBeSaved()) {
- collectedEntities.add(entity);
- }
+ final Entity[] rawData = this.entities.getRawData();
+ final List<Entity> collectedEntities = new ArrayList<>(len);
+ for (int i = 0; i < len; ++i) {
+ final Entity entity = rawData[i];
+ if (entity.shouldBeSaved()) {
+ collectedEntities.add(entity);
}
+ }
- if (collectedEntities.isEmpty()) {
- return null;
- }
+ if (collectedEntities.isEmpty()) {
+ return null;
+ }
+
+ return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world);
+ }
- return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world);
+ public CompoundTag save() {
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ return this.saveUnsafe();
+ }
+ id = this.accessLock.readLock();
+ try {
+ return this.saveUnsafe();
} finally {
this.accessLock.unlockRead(id);
}
@@ -119,12 +136,18 @@ public final class ChunkEntitySlices {
public boolean unload() {
Entity[] collectedEntities;
int len;
- long id = this.accessLock.readLock();
- try {
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
len = this.entities.size();
collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
- } finally {
- this.accessLock.unlockRead(id);
+ }else {
+ id = this.accessLock.readLock();
+ try {
+ len = this.entities.size();
+ collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
for (int i = 0; i < len; ++i) {
final Entity entity = collectedEntities[i];
@@ -160,7 +183,12 @@ public final class ChunkEntitySlices {
}
public void callEntitiesLoadEvent() {
- final long id = this.accessLock.readLock();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ return;
+ }
+ id = this.accessLock.readLock();
try {
CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
} finally {
@@ -169,7 +197,12 @@ public final class ChunkEntitySlices {
}
public void callEntitiesUnloadEvent() {
- final long id = this.accessLock.readLock();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
+ return;
+ }
+ id = this.accessLock.readLock();
try {
CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
} finally {
@@ -179,7 +212,11 @@ public final class ChunkEntitySlices {
// Paper end - optimise CraftChunk#getEntities
public boolean isEmpty() {
- final long id = this.accessLock.readLock();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ return this.entities.size() == 0;
+ }
+ id = this.accessLock.readLock();
try {
return this.entities.size() == 0;
} finally {
@@ -188,16 +225,25 @@ public final class ChunkEntitySlices {
}
public void mergeInto(final ChunkEntitySlices slices) {
- final long id = this.accessLock.readLock();
final List<Entity> cop = new ArrayList<>();
- try {
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
final Entity[] entities = this.entities.getRawData();
for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
final Entity entity = entities[i];
cop.add(entity);
}
- } finally {
- this.accessLock.unlockRead(id);
+ }else {
+ id = this.accessLock.readLock();
+ try {
+ final Entity[] entities = this.entities.getRawData();
+ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
+ final Entity entity = entities[i];
+ cop.add(entity);
+ }
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
for (Entity entity : cop){
slices.addEntity(entity, entity.sectionY);
@@ -209,13 +255,19 @@ public final class ChunkEntitySlices {
Entity[] entities;
- final long id = this.accessLock.readLock();
- try {
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
entities = Arrays.copyOf(this.entities.getRawData(), this.entities.getRawData().length);
- } finally {
- this.accessLock.unlockRead(id);
+ }else {
+ id = this.accessLock.readLock();
+ try {
+ entities = Arrays.copyOf(this.entities.getRawData(), this.entities.getRawData().length);
+ } finally {
+ this.accessLock.unlockRead(id);
+ }
}
+
for (final Entity entity : entities) {
final Visibility oldVisibility = EntityLookup.getEntityStatus(entity);
entity.chunkStatus = status;
@@ -278,7 +330,12 @@ public final class ChunkEntitySlices {
}
public void getHardCollidingEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- final long id = this.accessLock.readLock();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ this.hardCollidingEntities.getEntities(except, box, into, predicate);
+ return;
+ }
+ id = this.accessLock.readLock();
try {
this.hardCollidingEntities.getEntities(except, box, into, predicate);
} finally {
@@ -287,34 +344,46 @@ public final class ChunkEntitySlices {
}
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- final long id = this.accessLock.readLock();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate);
+ return;
+ }
+ id = this.accessLock.readLock();
try {
this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate);
} finally {
this.accessLock.unlockRead(id);
}
-
}
public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
- final long id = this.accessLock.readLock();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ this.allEntities.getEntities(except, box, into, predicate);
+ return;
+ }
+ id = this.accessLock.readLock();
try {
this.allEntities.getEntities(except, box, into, predicate);
} finally {
this.accessLock.unlockRead(id);
}
-
}
public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate) {
- final long id = this.accessLock.readLock();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ this.allEntities.getEntities(type, box, (List) into, (Predicate) predicate);
+ return;
+ }
+ id = this.accessLock.readLock();
try {
this.allEntities.getEntities(type, box, (List) into, (Predicate) predicate);
} finally {
this.accessLock.unlockRead(id);
}
-
}
protected EntityCollectionBySection initClass(final Class<? extends Entity> clazz) {
@@ -342,7 +411,18 @@ public final class ChunkEntitySlices {
public <T extends Entity> void getEntities(final Class<? extends T> clazz, final Entity except, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate) {
- final long id = this.accessLock.readLock();
+ long id = this.accessLock.tryOptimisticRead();
+ if (this.accessLock.validate(id)){
+ EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
+ if (collection != null) {
+ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate);
+ } else {
+ this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz));
+ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate);
+ }
+ return;
+ }
+ id = this.accessLock.readLock();
try {
EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
if (collection != null) {

View File

@@ -1,102 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 14 Jan 2023 14:23:39 +0800
Subject: [PATCH] Hearse: Remove a lock in ServerChunkCache
Original license: MIT
Original project: https://github.com/NaturalCodeClub/HearseRewrite
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 85c03dc7c1e714fab281374a177cd4c54e97d939..e311724d2e723115bc9549a61e6206a8aed835d8 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -4,6 +4,9 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.util.Either;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import net.minecraft.Util;
@@ -57,8 +60,7 @@ public class ServerChunkCache extends ChunkSource {
@VisibleForDebug
private NaturalSpawner.SpawnState lastSpawnState;
// Paper start
- final com.destroystokyo.paper.util.concurrent.WeakSeqLock loadedChunkMapSeqLock = new com.destroystokyo.paper.util.concurrent.WeakSeqLock();
- final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<LevelChunk> loadedChunkMap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(8192, 0.5f);
+ private final Long2ObjectMap<LevelChunk> loadedChunkMap = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(8192, 0.5f));
private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4];
@@ -70,12 +72,7 @@ public class ServerChunkCache extends ChunkSource {
}
public void addLoadedChunk(LevelChunk chunk) {
- this.loadedChunkMapSeqLock.acquireWrite();
- try {
- this.loadedChunkMap.put(chunk.coordinateKey, chunk);
- } finally {
- this.loadedChunkMapSeqLock.releaseWrite();
- }
+ this.loadedChunkMap.put(chunk.coordinateKey, chunk);
// rewrite cache if we have to
// we do this since we also cache null chunks
@@ -85,13 +82,7 @@ public class ServerChunkCache extends ChunkSource {
}
public void removeLoadedChunk(LevelChunk chunk) {
- this.loadedChunkMapSeqLock.acquireWrite();
- try {
- this.loadedChunkMap.remove(chunk.coordinateKey);
- } finally {
- this.loadedChunkMapSeqLock.releaseWrite();
- }
-
+ this.loadedChunkMap.remove(chunk.coordinateKey);
// rewrite cache if we have to
// we do this since we also cache null chunks
int cacheKey = getChunkCacheKey(chunk.locX, chunk.locZ);
@@ -360,22 +351,7 @@ public class ServerChunkCache extends ChunkSource {
return this.getChunkAtIfLoadedMainThread(x, z);
}
- LevelChunk ret = null;
- long readlock;
- do {
- readlock = this.loadedChunkMapSeqLock.acquireRead();
- try {
- ret = this.loadedChunkMap.get(k);
- } catch (Throwable thr) {
- if (thr instanceof ThreadDeath) {
- throw (ThreadDeath)thr;
- }
- // re-try, this means a CME occurred...
- continue;
- }
- } while (!this.loadedChunkMapSeqLock.tryReleaseRead(readlock));
-
- return ret;
+ return this.loadedChunkMap.get(k);
}
// Paper end
// Paper start - async chunk io
@@ -436,7 +412,7 @@ public class ServerChunkCache extends ChunkSource {
// Paper end
com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info
//this.level.timings.syncChunkLoad.startTiming(); // Paper // Purpur
- chunkproviderserver_b.managedBlock(completablefuture::isDone);
+ chunkproviderserver_b.managedBlock(completablefuture::isDone);
io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
//this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur
} // Paper
@@ -495,6 +471,7 @@ public class ServerChunkCache extends ChunkSource {
// Paper start - add isUrgent - old sig left in place for dirty nms plugins
return getChunkFutureMainThread(chunkX, chunkZ, leastStatus, create, false);
}
+
private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create, boolean isUrgent) {
// Paper start - rewrite chunk system
io.papermc.paper.util.TickThread.ensureTickThread(this.level, chunkX, chunkZ, "Scheduling chunk load off-main");