9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-22 16:39:22 +00:00

MikuServer: Use MikuServer instead of Hearse

This commit is contained in:
Dreeam
2023-01-30 03:26:45 -05:00
parent d5a4653dc8
commit d866c2dcc0
49 changed files with 3631 additions and 13500 deletions

View File

@@ -1,79 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Tue, 24 Jan 2023 09:32:37 +0800
Subject: [PATCH] Hearse: Fix some threading issue in bukkit event system
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 8eb8a788dc3dbc0d5ac24089a57c730089fd8dbe..91064280782956049f9f31fab97e567f3758e354 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -1489,6 +1489,12 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*/
boolean isPrimaryThread();
+ //Hearse start
+ /**
+ * Get current thread is worker or other
+ */
+ boolean isWorkerThread();
+
// Paper start
/**
* Gets the message that is displayed on the server list.
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index ba869354adc59db2fc547c481c1ed4d5d0af23b7..6a0a1e95b81da0087c2d56b905f1de1d0225ae5b 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -646,8 +646,11 @@ public final class SimplePluginManager implements PluginManager {
defaultPerms.get(false).clear();
}
}
+
private void fireEvent(Event event) { callEvent(event); } // Paper - support old method incase plugin uses reflection
+ private final Object eventLock = new Object();//Hearse ensure event system thread safe
+
/**
* Calls an event with the given details.
*
@@ -666,6 +669,20 @@ public final class SimplePluginManager implements PluginManager {
}
// KTP end - optimize spigot event bus
+ //Hearse start
+ if (server.isWorkerThread()) {
+ synchronized (this.eventLock) {
+ this.fireCallEvent(event);
+ }
+ return;
+ }
+
+ this.fireCallEvent(event);
+ //Hearse end
+ }
+
+ //Hearse start - split to a new method
+ private void fireCallEvent(Event event) {
HandlerList handlers = event.getHandlers();
RegisteredListener[] listeners = handlers.getRegisteredListeners();
@@ -687,7 +704,7 @@ public final class SimplePluginManager implements PluginManager {
plugin.getDescription().getAuthors(),
plugin.getDescription().getFullName(),
ex.getMessage()
- ));
+ ));
}
} catch (Throwable ex) {
gg.pufferfish.pufferfish.sentry.SentryContext.setEventContext(event, registration); // Pufferfish
@@ -702,6 +719,7 @@ public final class SimplePluginManager implements PluginManager {
}
}
}
+ //Hearse end
@Override
public void registerEvents(@NotNull Listener listener, @NotNull Plugin plugin) {

View File

@@ -0,0 +1,61 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 28 Jan 2023 13:05:13 +0800
Subject: [PATCH] MikuServer: Fix threading issue in event system
Original license: MIT
Original project: https://github.com/MikuMC/MikuServer
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 8eb8a788dc3dbc0d5ac24089a57c730089fd8dbe..b4c6a3d57ea21aa9ebc05f6aaf6a1b750b04545c 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -113,6 +113,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
@NotNull
public String getBukkitVersion();
+ public boolean isCurrentThreadWorker();
+
// Paper start - expose game version
/**
* Gets the version of game this server implements
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index ba869354adc59db2fc547c481c1ed4d5d0af23b7..d7064142c0aafbe17af263c4ecad6f1e879bb9d7 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -648,6 +648,8 @@ public final class SimplePluginManager implements PluginManager {
}
private void fireEvent(Event event) { callEvent(event); } // Paper - support old method incase plugin uses reflection
+ private final Object eventLock = new Object();
+
/**
* Calls an event with the given details.
*
@@ -666,6 +668,17 @@ public final class SimplePluginManager implements PluginManager {
}
// KTP end - optimize spigot event bus
+ if (server.isCurrentThreadWorker()) {
+ synchronized (this.eventLock) {
+ this.callEventInternal(event);
+ return;
+ }
+ }
+
+ this.callEventInternal(event);
+ }
+
+ private void callEventInternal(Event event) {
HandlerList handlers = event.getHandlers();
RegisteredListener[] listeners = handlers.getRegisteredListeners();
@@ -687,7 +700,7 @@ public final class SimplePluginManager implements PluginManager {
plugin.getDescription().getAuthors(),
plugin.getDescription().getFullName(),
ex.getMessage()
- ));
+ ));
}
} catch (Throwable ex) {
gg.pufferfish.pufferfish.sentry.SentryContext.setEventContext(event, registration); // Pufferfish

View File

@@ -1,8 +1,11 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com> From: =?UTF-8?q?=E3=84=97=E3=84=A0=CB=8B=20=E3=84=91=E3=84=A7=CB=8A?=
<tsao-chi@the-lingo.org>
Date: Thu, 5 Jan 2023 09:08:17 +0800 Date: Thu, 5 Jan 2023 09:08:17 +0800
Subject: [PATCH] Akarin: Async userlist saving Subject: [PATCH] Akarin: Save Json list asynchronously
Original license: GPL v3
Original project: https://github.com/Akarin-project/Akarin
diff --git a/src/main/java/net/minecraft/server/players/StoredUserList.java b/src/main/java/net/minecraft/server/players/StoredUserList.java diff --git a/src/main/java/net/minecraft/server/players/StoredUserList.java b/src/main/java/net/minecraft/server/players/StoredUserList.java
index 4fd709a550bf8da1e996894a1ca6b91206c31e9e..b43a7c3aea930805ea917176d4a7b5564be084cb 100644 index 4fd709a550bf8da1e996894a1ca6b91206c31e9e..b43a7c3aea930805ea917176d4a7b5564be084cb 100644

View File

@@ -0,0 +1,791 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Fri, 27 Jan 2023 19:38:48 +0800
Subject: [PATCH] MikuServer: Async entity traveling
Original license: MIT
Original project: https://github.com/MikuMC/MikuServer
diff --git a/src/main/java/co/m2ek4u/aoame/AnotherTickThread.java b/src/main/java/co/m2ek4u/aoame/AnotherTickThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5fe52beb25e7a95549cdf0ae19edf6029f10642
--- /dev/null
+++ b/src/main/java/co/m2ek4u/aoame/AnotherTickThread.java
@@ -0,0 +1,13 @@
+package co.m2ek4u.aoame;
+
+import io.papermc.paper.util.TickThread;
+
+public class AnotherTickThread extends TickThread {
+ public AnotherTickThread(String name) {
+ super(name);
+ }
+
+ public AnotherTickThread(Runnable run, String name) {
+ super(run, name);
+ }
+}
diff --git a/src/main/java/co/m2ek4u/aoame/CallbackExecutor.java b/src/main/java/co/m2ek4u/aoame/CallbackExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..b861405a7626ba8fa677c455bf6507253b33c157
--- /dev/null
+++ b/src/main/java/co/m2ek4u/aoame/CallbackExecutor.java
@@ -0,0 +1,99 @@
+package co.m2ek4u.aoame;
+
+import org.jetbrains.annotations.NotNull;
+import java.util.Queue;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.LockSupport;
+
+public class CallbackExecutor extends ThreadPoolExecutor {
+ private final AtomicBoolean isSubmittingStarted = new AtomicBoolean(false);
+ private final Queue<TaskEntry> submittedTasks = new ConcurrentLinkedDeque<>();
+
+ public CallbackExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
+ }
+
+ public CallbackExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull ThreadFactory threadFactory) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
+ }
+
+ public CallbackExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
+ }
+
+ public CallbackExecutor(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 void startSubmitting(){
+ if (this.isSubmittingStarted.get()){
+ throw new IllegalStateException();
+ }
+ this.isSubmittingStarted.set(true);
+ }
+
+ public void awaitSubmittingEnd(){
+ while (this.isSubmittingStarted.get()){
+ LockSupport.parkNanos(this,1);
+ }
+ }
+
+ public void executeWithCallBack(Runnable command,Runnable callBack){
+ if (this.isSubmittingStarted.get()){
+ TaskEntry newTask = new TaskEntry(command,callBack);
+ this.execute(newTask::runMainTask);
+ this.submittedTasks.add(newTask);
+ return;
+ }
+ throw new IllegalStateException();
+ }
+
+ public boolean isSubmittingStarted(){
+ return this.isSubmittingStarted.get();
+ }
+
+ public void stopSubmitting(){
+ if (!this.isSubmittingStarted.get()){
+ throw new IllegalStateException();
+ }
+ this.isSubmittingStarted.set(false);
+ TaskEntry task;
+ while ((task = this.submittedTasks.poll())!=null){
+ while (!task.runSubTask()){
+ LockSupport.parkNanos(this,1);
+ }
+ }
+ }
+
+ private static class TaskEntry{
+ private final Runnable mainTask;
+ private final Runnable subTask;
+ private volatile boolean mainTaskFinished = false;
+
+ public TaskEntry(Runnable mainTask,Runnable subTask){
+ this.mainTask = mainTask;
+ this.subTask = subTask;
+ }
+
+ public void runMainTask(){
+ try {
+ this.mainTask.run();
+ }finally {
+ this.mainTaskFinished = true;
+ }
+ }
+
+ public boolean runSubTask(){
+ if (!this.mainTaskFinished){
+ return false;
+ }
+ try {
+ this.subTask.run();
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return true;
+ }
+ }
+}
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..b128433d2888a98bce55052e821626c0478748dd 100644
--- a/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java
+++ b/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java
@@ -1,6 +1,9 @@
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 java.util.Iterator;
@@ -12,117 +15,42 @@ import java.util.NoSuchElementException;
*/
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> objectList = ObjectLists.synchronize(new ObjectArrayList<>());
public int size() {
- return this.count;
+ return this.objectList.size();
}
public boolean contains(final Entity entity) {
- return this.entityToIndex.containsKey(entity.getId());
+ return this.objectList.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.objectList.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.objectList.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.objectList.get(index);
}
public Entity getUnchecked(final int index) {
- return this.entities[index];
+ return this.objectList.get(index);
}
public Entity[] getRawData() {
- return this.entities;
+ return this.objectList.toArray(Entity[]::new);
}
public void clear() {
- this.entityToIndex.clear();
- Arrays.fill(this.entities, 0, this.count, null);
- this.count = 0;
+ this.objectList.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;
- }
- };
+ return this.objectList.iterator();
}
}
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..ec90ff5c2581706180498b74dbbf960d52d47209 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;
@@ -26,11 +32,8 @@ import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.UUID;
+
+import java.util.*;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -46,15 +49,15 @@ 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);
+ 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 Map<Integer,Entity> entityById = Int2ReferenceMaps.synchronize(new Int2ReferenceOpenHashMap<>());
+ private final Object2ReferenceMap<UUID, Entity> entityByUUID = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenHashMap<>());
private final EntityList accessibleEntities = new EntityList();
public EntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
@@ -208,8 +211,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 +220,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;
}
@@ -231,14 +234,14 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
if (entity.updatingSectionStatus) {
// recursive status update
- LOGGER.error("Cannot recursively update entity chunk status for entity " + entity, new Throwable());
+ LOGGER.warn("Cannot recursively update entity chunk status for entity " + entity);
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());
+ LOGGER.warn("Cannot update chunk status for entity " + entity + " since entity chunk (" + slices.chunkX + "," + slices.chunkZ + ") is receiving update");
return;
}
@@ -347,7 +350,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
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());
+ LOGGER.warn("Entity " + entity + " is currently prevented from being added/removed to world since it is processing section status updates");
return false;
}
diff --git a/src/main/java/io/papermc/paper/util/CachedLists.java b/src/main/java/io/papermc/paper/util/CachedLists.java
index e08f4e39db4ee3fed62e37364d17dcc5c5683504..22a5470862acfa470c9acd97c96fea29aede0b68 100644
--- a/src/main/java/io/papermc/paper/util/CachedLists.java
+++ b/src/main/java/io/papermc/paper/util/CachedLists.java
@@ -1,5 +1,6 @@
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;
@@ -8,50 +9,18 @@ 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;
- }
+ public static void returnTempGetEntitiesList(List<Entity> list) {}
// Paper end - optimise collisions
- 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/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 3b7e4b724e86518ea57f5ed5ef0b8b3741d10f6f..9601de5775667c2d94e07b89bb7605b1e03d413d 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1,5 +1,7 @@
package net.minecraft.server;
+import co.m2ek4u.aoame.AnotherTickThread;
+import co.m2ek4u.aoame.CallbackExecutor;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import co.aikar.timings.Timings;
@@ -41,9 +43,8 @@ import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -284,6 +285,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
protected WorldData worldData;
private volatile boolean isSaving;
+ private final AtomicInteger threadId = new AtomicInteger();
+ public final CallbackExecutor asyncExecutor = new CallbackExecutor(
+ Runtime.getRuntime().availableProcessors(),
+ Runtime.getRuntime().availableProcessors(),
+ 50,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ task -> {
+ AnotherTickThread worker = new AnotherTickThread(task,"Entity-Async-Worker # "+threadId.getAndIncrement());
+ worker.setDaemon(true);
+ return worker;
+ }
+ );
+
// CraftBukkit start
public final WorldLoader.DataLoadContext worldLoader;
public org.bukkit.craftbukkit.CraftServer server;
@@ -1333,17 +1348,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
} else {
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
if (this.haveTime()) {
- Iterator iterator = this.getAllLevels().iterator();
-
- while (iterator.hasNext()) {
- ServerLevel worldserver = (ServerLevel) iterator.next();
-
+ for (ServerLevel worldserver : this.getAllLevels()) {
if (worldserver.getChunkSource().pollTask()) {
ret = true; // Paper - force execution of all worlds, do not just bias the first
}
}
}
-
return ret; // Paper - force execution of all worlds, do not just bias the first
}
}
@@ -1393,6 +1403,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public void onServerExit() {}
public void tickServer(BooleanSupplier shouldKeepTicking) {
+ if (this.asyncExecutor.isSubmittingStarted()){
+ this.asyncExecutor.stopSubmitting();
+ }
//co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Paper // Purpur
long i = Util.getNanos();
@@ -1406,6 +1419,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper
++this.tickCount;
+ this.asyncExecutor.startSubmitting();
this.tickChildren(shouldKeepTicking);
if (i - this.lastServerStatus >= 5000000000L) {
this.lastServerStatus = i;
@@ -1522,11 +1536,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
//MinecraftTimings.timeUpdateTimer.stopTiming(); // Spigot // Paper // Purpur
this.isIteratingOverLevels = true; // Paper
- Iterator iterator = this.getAllLevels().iterator(); // Paper - move down
- while (iterator.hasNext()) {
- ServerLevel worldserver = (ServerLevel) iterator.next();
- worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
- worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
+ for (ServerLevel worldserver : this.getAllLevels()) {
+ worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
+ worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
/*this.profiler.push(() -> { // Purpur
@@ -1557,7 +1569,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
try {
crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
} catch (Throwable t) {
- if (throwable instanceof ThreadDeath) { throw (ThreadDeath)throwable; } // Paper
+ if (throwable instanceof ThreadDeath) {
+ throw (ThreadDeath) throwable;
+ } // Paper
throw new RuntimeException("Error generating crash report", t);
}
// Spigot End
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 03b8ef3409fd5f7a4d4b06e13cf8eb22b3bbf8a1..824f26db01750866a2d9bbeefe17457f84d19504 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1,5 +1,6 @@
package net.minecraft.server.level;
+import co.m2ek4u.aoame.AnotherTickThread;
import com.google.common.annotations.VisibleForTesting;
import co.aikar.timings.TimingHistory; // Paper
import com.google.common.collect.Lists;
@@ -269,7 +270,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 && !(Thread.currentThread() instanceof AnotherTickThread)) {
this.getChunkSource().mainThreadProcessor.execute(() -> {
this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad);
});
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 40dbf933504a46aa358ca4280c578fabf0651315..619730e2013b4c8907ba7b7e6927b57b4ac76ec7 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -44,6 +44,7 @@ import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
@@ -3420,51 +3421,102 @@ public abstract class LivingEntity extends Entity {
this.updateFallFlying();
AABB axisalignedbb = this.getBoundingBox();
- // SpigotTimings.timerEntityAIMove.startTiming(); // Spigot // Paper
- this.travel(new Vec3((double) this.xxa, (double) this.yya, (double) this.zza));
- // SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot // Paper
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("freezing"); // Purpur
- boolean flag1 = this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES);
- int i;
+ if (this instanceof net.minecraft.world.entity.player.Player) {
+ // SpigotTimings.timerEntityAIMove.startTiming(); // Spigot // Paper
+ this.travel(new Vec3((double) this.xxa, (double) this.yya, (double) this.zza));
+ // SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot // Paper
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("freezing"); // Purpur
+ boolean flag1 = this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES);
+ int i;
- if (!this.level.isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API
- i = this.getTicksFrozen();
- if (this.isInPowderSnow && this.canFreeze()) {
- this.setTicksFrozen(Math.min(this.getTicksRequiredToFreeze(), i + 1));
- } else {
- this.setTicksFrozen(Math.max(0, i - 2));
+ if (!this.level.isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API
+ i = this.getTicksFrozen();
+ if (this.isInPowderSnow && this.canFreeze()) {
+ this.setTicksFrozen(Math.min(this.getTicksRequiredToFreeze(), i + 1));
+ } else {
+ this.setTicksFrozen(Math.max(0, i - 2));
+ }
}
- }
- this.removeFrost();
- this.tryAddFrost();
- if (!this.level.isClientSide && this.tickCount % 40 == 0 && this.isFullyFrozen() && this.canFreeze()) {
- i = flag1 ? 5 : 1;
- this.hurt(DamageSource.FREEZE, (float) i);
- }
+ this.removeFrost();
+ this.tryAddFrost();
+ if (!this.level.isClientSide && this.tickCount % 40 == 0 && this.isFullyFrozen() && this.canFreeze()) {
+ i = flag1 ? 5 : 1;
+ this.hurt(DamageSource.FREEZE, (float) i);
+ }
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("push"); // Purpur
- if (this.autoSpinAttackTicks > 0) {
- --this.autoSpinAttackTicks;
- this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox());
- }
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("push"); // Purpur
+ if (this.autoSpinAttackTicks > 0) {
+ --this.autoSpinAttackTicks;
+ this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox());
+ }
- this.pushEntities();
- //this.level.getProfiler().pop(); // Purpur
- // Paper start
- if (((ServerLevel) this.level).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
- if (this.xo != getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
- Location from = new Location(this.level.getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
- Location to = new Location (this.level.getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
- io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
- if (!event.callEvent()) {
- absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
- } else if (!to.equals(event.getTo())) {
- absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
+ this.pushEntities();
+ //this.level.getProfiler().pop(); // Purpur
+ // Paper start
+ if (((ServerLevel) this.level).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
+ if (this.xo != getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
+ Location from = new Location(this.level.getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
+ Location to = new Location(this.level.getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
+ io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
+ if (!event.callEvent()) {
+ absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
+ } else if (!to.equals(event.getTo())) {
+ absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
+ }
}
}
+ } else {
+ MinecraftServer.getServer().asyncExecutor.executeWithCallBack(() -> {
+ // SpigotTimings.timerEntityAIMove.startTiming(); // Spigot // Paper
+ this.travel(new Vec3((double) this.xxa, (double) this.yya, (double) this.zza));
+ // SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot // Paper
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("freezing"); // Purpur
+ boolean flag1 = this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES);
+ int i;
+
+ if (!this.level.isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API
+ i = this.getTicksFrozen();
+ if (this.isInPowderSnow && this.canFreeze()) {
+ this.setTicksFrozen(Math.min(this.getTicksRequiredToFreeze(), i + 1));
+ } else {
+ this.setTicksFrozen(Math.max(0, i - 2));
+ }
+ }
+
+ this.removeFrost();
+ this.tryAddFrost();
+ if (!this.level.isClientSide && this.tickCount % 40 == 0 && this.isFullyFrozen() && this.canFreeze()) {
+ i = flag1 ? 5 : 1;
+ this.hurt(DamageSource.FREEZE, (float) i);
+ }
+
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("push"); // Purpur
+ if (this.autoSpinAttackTicks > 0) {
+ --this.autoSpinAttackTicks;
+ this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox());
+ }
+
+ this.pushEntities();
+ //this.level.getProfiler().pop(); // Purpur
+ }, () -> {
+ if (((ServerLevel) this.level).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
+ if (this.xo != getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
+ Location from = new Location(this.level.getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
+ Location to = new Location(this.level.getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
+ io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
+ if (!event.callEvent()) {
+ absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
+ } else if (!to.equals(event.getTo())) {
+ absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
+ }
+ }
+ }
+ });
}
// Paper end
if (!this.level.isClientSide && this.isSensitiveToWater() && this.isInWaterRainOrBubble()) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
index 9f138bc471b5c2a4fa813ff943dbe34018b8df74..5c8a90f8536c9291df5891d8c75de963b75ec4bd 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
@@ -7,6 +7,7 @@ import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
+import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import java.util.List;
import java.util.Map;
@@ -25,8 +26,9 @@ import org.slf4j.Logger;
public class PoiSection {
private static final Logger LOGGER = LogUtils.getLogger();
- private final Short2ObjectMap<PoiRecord> records = new Short2ObjectOpenHashMap<>();
- private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newHashMap(); public final Map<Holder<PoiType>, Set<PoiRecord>> getData() { return this.byType; } // Paper - public accessor
+ private final Short2ObjectMap<PoiRecord> records = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>());
+ private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newConcurrentMap();
+ public final Map<Holder<PoiType>, Set<PoiRecord>> getData() { return this.byType; } // Paper - public accessor
private final Runnable setDirty;
private boolean isValid;
public final Optional<PoiSection> noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system
diff --git a/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java b/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java
index daa03360dd7044f10b20f36023b305dc7e0bb7df..f11cf0c0701247692075da2f2db7602e72ef1ec8 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java
@@ -19,17 +19,17 @@ public class LegacyRandomSource implements BitRandomSource {
}
@Override
- public RandomSource fork() {
+ public synchronized RandomSource fork() {
return new LegacyRandomSource(this.nextLong());
}
@Override
- public PositionalRandomFactory forkPositional() {
+ public synchronized PositionalRandomFactory forkPositional() {
return new LegacyRandomSource.LegacyPositionalRandomFactory(this.nextLong());
}
@Override
- public void setSeed(long seed) {
+ public synchronized void setSeed(long seed) {
if (!this.seed.compareAndSet(this.seed.get(), (seed ^ 25214903917L) & 281474976710655L)) {
throw ThreadingDetector.makeThreadingException("LegacyRandomSource", (Thread)null);
} else {
@@ -38,7 +38,7 @@ public class LegacyRandomSource implements BitRandomSource {
}
@Override
- public int next(int bits) {
+ public synchronized int next(int bits) {
long l = this.seed.get();
long m = l * 25214903917L + 11L & 281474976710655L;
if (!this.seed.compareAndSet(l, m)) {
@@ -49,7 +49,7 @@ public class LegacyRandomSource implements BitRandomSource {
}
@Override
- public double nextGaussian() {
+ public synchronized double nextGaussian() {
return this.gaussianSource.nextGaussian();
}

View File

@@ -1,763 +0,0 @@
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/Era4FunMC/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

@@ -0,0 +1,620 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Fri, 27 Jan 2023 19:43:43 +0800
Subject: [PATCH] MikuServer: Async entity ai
Original license: MIT
Original project: https://github.com/MikuMC/MikuServer
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();
}
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index f597d65d56964297eeeed6c7e77703764178fee0..b3bcafc8bafe1e4a1a2b690499b91e5316a604f1 100644
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
@@ -4,6 +4,7 @@ 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;
@@ -34,7 +35,7 @@ public final class ChunkEntitySlices {
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;
@@ -61,7 +62,7 @@ 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;
}
@@ -209,7 +210,7 @@ public final class ChunkEntitySlices {
}
for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
+ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) {
final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
if (entry.getKey().isInstance(entity)) {
@@ -234,7 +235,7 @@ public final class ChunkEntitySlices {
}
for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
+ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) {
final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
if (entry.getKey().isInstance(entity)) {
@@ -312,11 +313,11 @@ public final class ChunkEntitySlices {
this.storage = (E[])(cap <= 0 ? EMPTY : new Entity[cap]);
}
- public boolean isEmpty() {
+ public synchronized boolean isEmpty() {
return this.size == 0;
}
- public int size() {
+ public synchronized int size() {
return this.size;
}
@@ -328,7 +329,7 @@ public final class ChunkEntitySlices {
}
}
- public void add(final E entity) {
+ public synchronized void add(final E entity) {
final int idx = this.size++;
if (idx >= this.storage.length) {
this.resize();
@@ -338,7 +339,7 @@ public final class ChunkEntitySlices {
}
}
- public int indexOf(final E entity) {
+ public synchronized 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) {
@@ -350,7 +351,7 @@ public final class ChunkEntitySlices {
return -1;
}
- public boolean remove(final E entity) {
+ public synchronized boolean remove(final E entity) {
final int idx = this.indexOf(entity);
if (idx == -1) {
return false;
@@ -367,7 +368,7 @@ public final class ChunkEntitySlices {
return true;
}
- public boolean has(final E entity) {
+ public synchronized boolean has(final E entity) {
return this.indexOf(entity) != -1;
}
}
@@ -388,7 +389,7 @@ public final class ChunkEntitySlices {
this.entitiesBySection = new BasicEntityList[sectionCount];
}
- public void addEntity(final Entity entity, final int sectionIndex) {
+ public synchronized void addEntity(final Entity entity, final int sectionIndex) {
BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
if (list != null && list.has(entity)) {
@@ -404,7 +405,7 @@ public final class ChunkEntitySlices {
++this.count;
}
- public void removeEntity(final Entity entity, final int sectionIndex) {
+ public synchronized void removeEntity(final Entity entity, final int sectionIndex) {
final BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
if (list == null || !list.remove(entity)) {
@@ -419,7 +420,7 @@ public final class ChunkEntitySlices {
}
}
- public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
+ public synchronized void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
if (this.count == 0) {
return;
}
@@ -457,7 +458,7 @@ public final class ChunkEntitySlices {
}
}
- public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List<Entity> into,
+ public synchronized void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List<Entity> into,
final Predicate<? super Entity> predicate) {
if (this.count == 0) {
return;
@@ -508,7 +509,7 @@ public final class ChunkEntitySlices {
}
}
- public void getEntitiesWithEnderDragonParts(final Entity except, final Class<?> clazz, final AABB box, final List<Entity> into,
+ public synchronized void getEntitiesWithEnderDragonParts(final Entity except, final Class<?> clazz, final AABB box, final List<Entity> into,
final Predicate<? super Entity> predicate) {
if (this.count == 0) {
return;
@@ -559,7 +560,7 @@ public final class ChunkEntitySlices {
}
}
- public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
+ public synchronized <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate) {
if (this.count == 0) {
return;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index beb7c22cb63021f26c06f91050361e1b25fcc72d..07b2cd85956dda47ed0a026cd837c5b9498213ac 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -25,8 +25,8 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
-import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator;
-import it.unimi.dsi.fastutil.objects.ObjectIterator;
+import it.unimi.dsi.fastutil.objects.*;
+
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Path;
@@ -52,6 +52,7 @@ import java.util.function.IntSupplier;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
+
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
@@ -113,7 +114,6 @@ import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
import org.bukkit.entity.Player;
// CraftBukkit end
-import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider {
@@ -153,7 +153,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
private final Queue<Runnable> unloadQueue;
int viewDistance;
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
- public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
+ public final ReferenceSet<ChunkHolder> needsChangeBroadcasting = ReferenceSets.synchronize(new ReferenceOpenHashSet<>());
// Paper - rewrite chunk system
// Paper start - optimise checkDespawn
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 0ae45cf5a084fd412305e8b2f5dabe608b4eb1c1..686852f5cb6303381c45a673a0daf79a2d3a9dfe 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -6,12 +6,7 @@ import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.util.Either;
import java.io.File;
import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
+import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BooleanSupplier;
@@ -803,7 +798,7 @@ public class ServerChunkCache extends ChunkSource {
//gameprofilerfiller.popPush("broadcast"); // Purpur
//this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur
if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
- ReferenceOpenHashSet<ChunkHolder> copy = this.chunkMap.needsChangeBroadcasting.clone();
+ List<ChunkHolder> copy = new ArrayList<>(this.chunkMap.needsChangeBroadcasting);
this.chunkMap.needsChangeBroadcasting.clear();
for (ChunkHolder holder : copy) {
holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded
diff --git a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java
index 50a9f33aa31e9273c7c52d4bb2b02f0f884f7ba5..b7cd7b4bb7a2fb58dd907428f7e03662ef5e5c29 100644
--- a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java
+++ b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java
@@ -13,9 +13,9 @@ import java.util.Map;
import java.util.stream.Collectors;
public class ClassInstanceMultiMap<T> extends AbstractCollection<T> {
- private final Map<Class<?>, List<T>> byClass = Maps.newHashMap();
+ private final Map<Class<?>, List<T>> byClass = Maps.newConcurrentMap();
private final Class<T> baseClass;
- private final List<T> allInstances = Lists.newArrayList();
+ private final List<T> allInstances = Lists.newCopyOnWriteArrayList();
public ClassInstanceMultiMap(Class<T> elementType) {
this.baseClass = elementType;
@@ -23,7 +23,7 @@ public class ClassInstanceMultiMap<T> extends AbstractCollection<T> {
}
@Override
- public boolean add(T object) {
+ public synchronized boolean add(T object) {
boolean bl = false;
for(Map.Entry<Class<?>, List<T>> entry : this.byClass.entrySet()) {
@@ -36,7 +36,7 @@ public class ClassInstanceMultiMap<T> extends AbstractCollection<T> {
}
@Override
- public boolean remove(Object object) {
+ public synchronized boolean remove(Object object) {
boolean bl = false;
for(Map.Entry<Class<?>, List<T>> entry : this.byClass.entrySet()) {
@@ -50,32 +50,32 @@ public class ClassInstanceMultiMap<T> extends AbstractCollection<T> {
}
@Override
- public boolean contains(Object object) {
+ public synchronized boolean contains(Object object) {
return this.find(object.getClass()).contains(object);
}
- public <S> Collection<S> find(Class<S> type) {
+ public synchronized <S> Collection<S> find(Class<S> type) {
if (!this.baseClass.isAssignableFrom(type)) {
throw new IllegalArgumentException("Don't know how to search for " + type);
} else {
List<? extends T> list = this.byClass.computeIfAbsent(type, (typeClass) -> {
return this.allInstances.stream().filter(typeClass::isInstance).collect(Collectors.toList());
});
- return Collections.unmodifiableCollection(list);
+ return (Collection<S>) Collections.unmodifiableCollection(list);
}
}
@Override
- public Iterator<T> iterator() {
+ public synchronized Iterator<T> iterator() {
return (Iterator<T>)(this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator()));
}
- public List<T> getAllInstances() {
+ public synchronized List<T> getAllInstances() {
return ImmutableList.copyOf(this.allInstances);
}
@Override
- public int size() {
+ public synchronized int size() {
return this.allInstances.size();
}
}
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index 29fa9ad2223de668c15a5e5b433704b2c4765610..9071abd67c421a528514a6437a1b8dde02e068ed 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -23,6 +23,7 @@ import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvent;
@@ -216,12 +217,14 @@ public abstract class Mob extends LivingEntity {
public void inactiveTick() {
super.inactiveTick();
boolean isThrottled = gg.pufferfish.pufferfish.PufferfishConfig.throttleInactiveGoalSelectorTick && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking
- if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking
- this.goalSelector.tick();
- }
- if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority
- this.targetSelector.tick();
- }
+ MinecraftServer.getServer().asyncExecutor.executeWithCallBack(()->{
+ if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking
+ this.goalSelector.tick();
+ }
+ if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority
+ this.targetSelector.tick();
+ }
+ },()->{});
}
// Paper end
@@ -878,42 +881,43 @@ public abstract class Mob extends LivingEntity {
if (i % 10 == 0) this.sensing.tick(); // petal - only refresh line of sight cache every half second
//this.level.getProfiler().pop(); // Purpur
- if (i % 2 != 0 && this.tickCount > 1) {
- //this.level.getProfiler().push("targetSelector"); // Purpur
- if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
- this.targetSelector.tickRunningGoals(false);
+ MinecraftServer.getServer().asyncExecutor.executeWithCallBack(() -> {
+ if (i % 2 != 0 && this.tickCount > 1) {
+ //this.level.getProfiler().push("targetSelector"); // Purpur
+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+ this.targetSelector.tickRunningGoals(false);
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("goalSelector"); // Purpur
+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+ this.goalSelector.tickRunningGoals(false);
+ //this.level.getProfiler().pop(); // Purpur
+ } else {
+ //this.level.getProfiler().push("targetSelector"); // Purpur
+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+ this.targetSelector.tick();
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("goalSelector"); // Purpur
+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+ this.goalSelector.tick();
+ //this.level.getProfiler().pop(); // Purpur
+ }
+
+ //this.level.getProfiler().push("navigation"); // Purpur
+ this.navigation.tick();
//this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("goalSelector"); // Purpur
- if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
- this.goalSelector.tickRunningGoals(false);
+ //this.level.getProfiler().push("mob tick"); // Purpur
+ this.customServerAiStep();
//this.level.getProfiler().pop(); // Purpur
- } else {
- //this.level.getProfiler().push("targetSelector"); // Purpur
- if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
- this.targetSelector.tick();
+ //this.level.getProfiler().push("controls"); // Purpur
+ //this.level.getProfiler().push("move"); // Purpur
+ this.moveControl.tick();
+ //this.level.getProfiler().popPush("look"); // Purpur
+ this.lookControl.tick();
+ //this.level.getProfiler().popPush("jump"); // Purpur
+ this.jumpControl.tick();
//this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("goalSelector"); // Purpur
- if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
- this.goalSelector.tick();
//this.level.getProfiler().pop(); // Purpur
- }
-
- //this.level.getProfiler().push("navigation"); // Purpur
- this.navigation.tick();
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("mob tick"); // Purpur
- this.customServerAiStep();
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("controls"); // Purpur
- //this.level.getProfiler().push("move"); // Purpur
- this.moveControl.tick();
- //this.level.getProfiler().popPush("look"); // Purpur
- this.lookControl.tick();
- //this.level.getProfiler().popPush("jump"); // Purpur
- this.jumpControl.tick();
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().pop(); // Purpur
- this.sendDebugPackets();
+ }, this::sendDebugPackets);
}
protected void sendDebugPackets() {
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
index 2d222e383d48a1a32eebdb722d770b4fc6c0aca7..eb5257fabef397566e784247922fb27e61f96313 100644
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
@@ -274,16 +274,18 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
if (this.getUnhappyCounter() > 0) {
this.setUnhappyCounter(this.getUnhappyCounter() - 1);
}
- if (this.isEffectiveAi()) {
- if (level.spigotConfig.tickInactiveVillagers) {
- this.customServerAiStep();
- } else {
- this.mobTick(true);
+ MinecraftServer.getServer().asyncExecutor.executeWithCallBack(()->{
+ if (this.isEffectiveAi()) {
+ if (level.spigotConfig.tickInactiveVillagers) {
+ this.customServerAiStep();
+ } else {
+ this.mobTick(true);
+ }
}
- }
- maybeDecayGossip();
+ },()->{
+ maybeDecayGossip();
+ });
// Paper end
-
super.inactiveTick();
}
// Spigot End
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 b1c594dc6a6b8a6c737b99272acab9e7dbd0ed63..4aedee56077159aaf613033b688d2be6833f1ad1 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,10 @@ 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 javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@@ -16,8 +19,8 @@ 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 List<CollectingNeighborUpdater.NeighborUpdates> addedThisLayer = new ArrayList<>();
+ private final Deque<NeighborUpdates> stack = new ConcurrentLinkedDeque<>();
+ private final List<CollectingNeighborUpdater.NeighborUpdates> addedThisLayer = new CopyOnWriteArrayList<>();
private int count = 0;
public CollectingNeighborUpdater(Level world, int maxChainDepth) {
@@ -26,22 +29,22 @@ 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));
}

View File

@@ -1,475 +0,0 @@
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/Era4FunMC/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

@@ -1,13 +1,13 @@
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: Sun, 15 Jan 2023 09:52:27 +0800 Date: Fri, 27 Jan 2023 19:52:49 +0800
Subject: [PATCH] Hearse: Paper chunk system changes Subject: [PATCH] MikuServer: Concurrent problems fixes
Original license: MIT Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse Original project: https://github.com/MikuMC/MikuServer
diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java 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 index 0b060183429f4c72ec767075538477b4302bbf0d..23c32a06dce8f0c45647c3619c98ba95290cfa7d 100644
--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java --- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
+++ b/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; @@ -7,9 +7,9 @@ import io.papermc.paper.util.CoordinateUtils;
@@ -81,7 +81,13 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
} }
protected int getMaxConcurrentChunkSends() { protected int getMaxConcurrentChunkSends() {
@@ -523,17 +523,16 @@ public final class PlayerChunkLoader { @@ -518,22 +518,20 @@ public final class PlayerChunkLoader {
protected static final AtomicInteger concurrentChunkSends = new AtomicInteger();
protected final Reference2IntOpenHashMap<PlayerLoaderData> sendingChunkCounts = new Reference2IntOpenHashMap<>();
private static long nextChunkSend;
+
private void trySendChunks() {
final long time = System.nanoTime();
if (time < nextChunkSend) { if (time < nextChunkSend) {
return; return;
} }
@@ -93,17 +99,18 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
- if (data.nextChunkSendTarget > time) { - if (data.nextChunkSendTarget > time) {
+ while ((data1 = this.chunkSendWaitQueue.pollFirst())!=null) { + while ((data1 = this.chunkSendWaitQueue.pollFirst())!=null) {
+ if (data1.nextChunkSendTarget > time) { + if (data1.nextChunkSendTarget > time) {
+ this.chunkSendWaitQueue.add(data1);
break; break;
} }
-
this.chunkSendWaitQueue.pollFirst(); - this.chunkSendWaitQueue.pollFirst();
-
- this.chunkSendQueue.add(data); - this.chunkSendQueue.add(data);
+ this.chunkSendQueue.add(data1); + this.chunkSendQueue.add(data1);
} }
if (this.chunkSendQueue.isEmpty()) { if (this.chunkSendQueue.isEmpty()) {
@@ -542,10 +541,9 @@ public final class PlayerChunkLoader { @@ -542,10 +540,9 @@ public final class PlayerChunkLoader {
final int maxSends = this.getMaxConcurrentChunkSends(); final int maxSends = this.getMaxConcurrentChunkSends();
final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time; final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time;
@@ -117,7 +124,7 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
final int currSends = concurrentChunkSends.get(); final int currSends = concurrentChunkSends.get();
if (currSends >= maxSends) { if (currSends >= maxSends) {
break; break;
@@ -554,19 +552,12 @@ public final class PlayerChunkLoader { @@ -554,24 +551,17 @@ public final class PlayerChunkLoader {
if (!concurrentChunkSends.compareAndSet(currSends, currSends + 1)) { if (!concurrentChunkSends.compareAndSet(currSends, currSends + 1)) {
continue; continue;
} }
@@ -138,7 +145,13 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
continue; continue;
} }
@@ -581,17 +572,18 @@ public final class PlayerChunkLoader { if (!this.isChunkPlayerLoaded(queuedSend.chunkX, queuedSend.chunkZ)) {
- throw new IllegalStateException();
+ continue;
}
data.nextChunkSendTarget = nextPlayerDeadline;
@@ -581,17 +571,18 @@ public final class PlayerChunkLoader {
this.sendingChunkCounts.addTo(data, 1); this.sendingChunkCounts.addTo(data, 1);
} }
@@ -160,7 +173,7 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
} }
} }
@@ -618,16 +610,12 @@ public final class PlayerChunkLoader { @@ -618,16 +609,12 @@ public final class PlayerChunkLoader {
final int maxLoads = this.getMaxChunkLoads(); final int maxLoads = this.getMaxChunkLoads();
final long time = System.nanoTime(); final long time = System.nanoTime();
boolean updatedCounters = false; boolean updatedCounters = false;
@@ -179,7 +192,7 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
continue; continue;
} }
@@ -673,7 +661,7 @@ public final class PlayerChunkLoader { @@ -673,7 +660,7 @@ public final class PlayerChunkLoader {
final int currentChunkLoads = this.concurrentChunkLoads; 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)) 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))
@@ -188,6 +201,24 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
// don't poll, we didn't load it // don't poll, we didn't load it
this.chunkLoadQueue.add(data); this.chunkLoadQueue.add(data);
break; break;
@@ -736,12 +723,13 @@ public final class PlayerChunkLoader {
}
}
+
public void tickMidTick() {
- // try to send more chunks
- this.trySendChunks();
+ // try to send more chunks
+ this.trySendChunks();
- // try to queue more chunks to load
- this.tryLoadChunks();
+ // try to queue more chunks to load
+ this.tryLoadChunks();
}
static final class ChunkPriorityHolder {
@@ -786,11 +774,11 @@ public final class PlayerChunkLoader { @@ -786,11 +774,11 @@ public final class PlayerChunkLoader {
// warning: modifications of this field must be aware that the loadQueue inside PlayerChunkLoader uses this field // warning: modifications of this field must be aware that the loadQueue inside PlayerChunkLoader uses this field
@@ -226,48 +257,67 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
// has our decision about look priority changed? // has our decision about look priority changed?
&& this.usingLookingPriority == useLookPriority && 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 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 index ec90ff5c2581706180498b74dbbf960d52d47209..387d07868301877dd7fca5d8dfd21e1331f4793e 100644
--- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java --- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
+++ b/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; @@ -6,14 +6,15 @@ import io.papermc.paper.util.CoordinateUtils;
import io.papermc.paper.util.TickThread; import io.papermc.paper.util.TickThread;
import io.papermc.paper.util.WorldUtil; import io.papermc.paper.util.WorldUtil;
import io.papermc.paper.world.ChunkEntitySlices; import io.papermc.paper.world.ChunkEntitySlices;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps; 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.ints.Int2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectArrayMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; 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.longs.Long2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceMaps; import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
-import it.unimi.dsi.fastutil.objects.Object2ReferenceMaps;
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import io.papermc.paper.chunk.system.ChunkSystem; import io.papermc.paper.chunk.system.ChunkSystem;
@@ -45,16 +51,16 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -32,8 +33,13 @@ import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
-
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -48,16 +54,16 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public final ServerLevel world; public final ServerLevel world;
- private final StampedLock stateLock = new StampedLock(); - private final StampedLock stateLock = new StampedLock();
- protected final Long2ObjectOpenHashMap<ChunkSlicesRegion> regions = new Long2ObjectOpenHashMap<>(128, 0.5f); - protected final Long2ObjectMap<ChunkSlicesRegion> regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f));
+ private final StampedLock entityByLock = new StampedLock(); + private final StampedLock entityByLock = new StampedLock();
+ private final Object chunkEntitySlicesLock = new Object(); + private final Lock regionLoadLock = new ReentrantLock(true);
+ +
+ protected final Long2ObjectMap<ChunkSlicesRegion> regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f)); + protected final Long2ObjectMap<ChunkSlicesRegion> regions = Long2ObjectMaps.synchronize(new Long2ObjectArrayMap<>());
private final int minSection; // inclusive private final int minSection; // inclusive
private final int maxSection; // inclusive private final int maxSection; // inclusive
private final LevelCallback<Entity> worldCallback; private final LevelCallback<Entity> worldCallback;
- -
- private final StampedLock entityByLock = new StampedLock(); - private final StampedLock entityByLock = new StampedLock();
- private final Int2ReferenceOpenHashMap<Entity> entityById = new Int2ReferenceOpenHashMap<>(); - private final Map<Integer,Entity> entityById = Int2ReferenceMaps.synchronize(new Int2ReferenceOpenHashMap<>());
- private final Object2ReferenceOpenHashMap<UUID, Entity> entityByUUID = new Object2ReferenceOpenHashMap<>(); - private final Object2ReferenceMap<UUID, Entity> entityByUUID = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenHashMap<>());
+ private final Int2ReferenceMap<Entity> entityById = new Int2ReferenceOpenHashMap<>(); + private final Int2ReferenceMap<Entity> entityById = new Int2ReferenceArrayMap<>();
+ private final Object2ReferenceMap<UUID, Entity> entityByUUID = new Object2ReferenceOpenHashMap<>(); + private final Object2ReferenceMap<UUID, Entity> entityByUUID = new Object2ReferenceArrayMap<>();
private final EntityList accessibleEntities = new EntityList(); private final EntityList accessibleEntities = new EntityList();
public EntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) { public EntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
@@ -105,7 +111,6 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -108,7 +114,6 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
if (attempt != 0L) { if (attempt != 0L) {
try { try {
final Entity ret = this.entityByUUID.get(id); final Entity ret = this.entityByUUID.get(id);
@@ -275,7 +325,7 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
if (this.entityByLock.validate(attempt)) { if (this.entityByLock.validate(attempt)) {
return maskNonAccessible(ret); return maskNonAccessible(ret);
} }
@@ -166,12 +171,12 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -169,12 +174,12 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
} }
@Override @Override
@@ -290,52 +340,21 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
if (this.off >= this.length) { if (this.off >= this.length) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
@@ -179,7 +184,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -231,75 +236,50 @@ 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, public void entityStatusChange(final Entity entity, final ChunkEntitySlices slices, final Visibility oldVisibility, final Visibility newVisibility, final boolean moved,
final boolean created, final boolean destroyed) { final boolean created, final boolean destroyed) {
TickThread.ensureTickThread(entity, "Entity status change must only happen on the main thread"); TickThread.ensureTickThread(entity, "Entity status change must only happen on the main thread");
- -
- if (entity.updatingSectionStatus) { - if (entity.updatingSectionStatus) {
- // recursive status update - // recursive status update
- LOGGER.error("Cannot recursively update entity chunk status for entity " + entity, new Throwable()); - LOGGER.warn("Cannot recursively update entity chunk status for entity " + entity);
- return; - return;
- } - }
- -
- final boolean entityStatusUpdateBefore = slices == null ? false : slices.startPreventingStatusUpdates(); - final boolean entityStatusUpdateBefore = slices == null ? false : slices.startPreventingStatusUpdates();
- -
- if (entityStatusUpdateBefore) { - if (entityStatusUpdateBefore) {
- LOGGER.error("Cannot update chunk status for entity " + entity + " since entity chunk (" + slices.chunkX + "," + slices.chunkZ + ") is receiving update", new Throwable()); - LOGGER.warn("Cannot update chunk status for entity " + entity + " since entity chunk (" + slices.chunkX + "," + slices.chunkZ + ") is receiving update");
- return; - return;
- } - }
- -
@@ -432,43 +451,52 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
} }
} }
@@ -346,11 +326,6 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -349,11 +329,6 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
return false; return false;
} }
- if (entity.updatingSectionStatus) { - 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()); - LOGGER.warn("Entity " + entity + " is currently prevented from being added/removed to world since it is processing section status updates");
- return false; - return false;
- } - }
- -
if (fromDisk) { if (fromDisk) {
ChunkSystem.onEntityPreAdd(this.world, entity); ChunkSystem.onEntityPreAdd(this.world, entity);
if (entity.isRemoved()) { if (entity.isRemoved()) {
@@ -398,7 +373,10 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -401,7 +376,14 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
if (!entity.isRemoved()) { if (!entity.isRemoved()) {
throw new IllegalStateException("Only call Entity#setRemoved to remove an entity"); throw new IllegalStateException("Only call Entity#setRemoved to remove an entity");
} }
- final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ); - final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ);
+ ChunkEntitySlices slices; + ChunkEntitySlices slices;
+ synchronized (this.chunkEntitySlicesLock){ + this.regionLoadLock.lock();
+ try {
+ slices = this.getChunk(sectionX, sectionZ); + slices = this.getChunk(sectionX, sectionZ);
+ }finally {
+ this.regionLoadLock.unlock();
+ } + }
+
// all entities should be in a chunk // all entities should be in a chunk
if (slices == null) { if (slices == null) {
LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")"); LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")");
@@ -441,7 +419,10 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -444,7 +426,15 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
// ensure the old section is owned by this tick thread // ensure the old section is owned by this tick thread
TickThread.ensureTickThread(this.world, entity.sectionX, entity.sectionZ, "Cannot move entity off-main"); TickThread.ensureTickThread(this.world, entity.sectionX, entity.sectionZ, "Cannot move entity off-main");
- final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ); - final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ);
+ ChunkEntitySlices old; + ChunkEntitySlices old;
+ synchronized (this.chunkEntitySlicesLock){ +
+ this.regionLoadLock.lock();
+ try {
+ old = this.getChunk(entity.sectionX, entity.sectionZ); + old = this.getChunk(entity.sectionX, entity.sectionZ);
+ }finally {
+ this.regionLoadLock.unlock();
+ } + }
+
final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ); final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
if (!old.removeEntity(entity, entity.sectionY)) { if (!old.removeEntity(entity, entity.sectionY)) {
@@ -609,7 +590,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -612,7 +602,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
continue; continue;
} }
@@ -477,7 +505,7 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
} }
} }
} }
@@ -660,21 +641,24 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -663,18 +653,22 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot load in entity section off-main"); TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot load in entity section off-main");
synchronized (this) { synchronized (this) {
final ChunkEntitySlices curr = this.getChunk(chunkX, chunkZ); final ChunkEntitySlices curr = this.getChunk(chunkX, chunkZ);
@@ -489,7 +517,8 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
- this.addChunk(chunkX, chunkZ, slices); - this.addChunk(chunkX, chunkZ, slices);
- } else { - } else {
- this.addChunk(chunkX, chunkZ, slices); - this.addChunk(chunkX, chunkZ, slices);
+ synchronized (this.chunkEntitySlicesLock){ + this.regionLoadLock.lock();
+ try {
+ if (curr != null) { + if (curr != null) {
+ this.removeChunk(chunkX, chunkZ); + this.removeChunk(chunkX, chunkZ);
+ curr.mergeInto(slices); + curr.mergeInto(slices);
@@ -497,6 +526,8 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
+ } else { + } else {
+ this.addChunk(chunkX, chunkZ, slices); + this.addChunk(chunkX, chunkZ, slices);
+ } + }
+ } finally {
+ this.regionLoadLock.unlock();
} }
} }
} }
@@ -504,14 +535,8 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
+ +
public void entitySectionUnload(final int chunkX, final int chunkZ) { public void entitySectionUnload(final int chunkX, final int chunkZ) {
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot unload entity section off-main"); TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot unload entity section off-main");
- this.removeChunk(chunkX, chunkZ); this.removeChunk(chunkX, chunkZ);
+ synchronized (this.chunkEntitySlicesLock){ @@ -702,27 +696,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
+ 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) { public ChunkSlicesRegion getRegion(final int regionX, final int regionZ) {
final long key = CoordinateUtils.getChunkKey(regionX, regionZ); final long key = CoordinateUtils.getChunkKey(regionX, regionZ);
@@ -540,7 +565,7 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
} }
private synchronized void removeChunk(final int chunkX, final int chunkZ) { private synchronized void removeChunk(final int chunkX, final int chunkZ) {
@@ -730,12 +694,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -733,12 +707,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
final int remaining = region.remove(relIndex); final int remaining = region.remove(relIndex);
if (remaining == 0) { if (remaining == 0) {
@@ -554,7 +579,7 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
} }
} }
@@ -749,12 +708,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -752,12 +721,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
} else { } else {
region = new ChunkSlicesRegion(); region = new ChunkSlicesRegion();
region.add(relIndex, slices); region.add(relIndex, slices);
@@ -568,7 +593,7 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
} }
} }
@@ -831,9 +785,11 @@ public final class EntityLookup implements LevelEntityGetter<Entity> { @@ -834,9 +798,11 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public static final NoOpCallback INSTANCE = new NoOpCallback(); public static final NoOpCallback INSTANCE = new NoOpCallback();
@Override @Override
@@ -686,3 +711,581 @@ index 830d863cd9665d58875bfa5ca2bcd22f89ab2d49..15eeea40bb7a44470f6f3f0e2473cb45
boolean ret = false; boolean ret = false;
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index b3bcafc8bafe1e4a1a2b690499b91e5316a604f1..b12c02962e9dad92ae79d762887c65db10765488 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,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 {
@@ -32,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 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;
@@ -67,8 +66,7 @@ public final class ChunkEntitySlices {
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) {
@@ -81,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;
@@ -107,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()) {
@@ -129,7 +166,6 @@ public final class ChunkEntitySlices {
}
}
}
-
return this.entities.size() != 0;
}
@@ -141,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);
@@ -197,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().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);
+ 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) {
@@ -288,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);
}
}
@@ -310,26 +449,26 @@ 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() {
+ public boolean isEmpty() {
return this.size == 0;
}
- public synchronized int size() {
+ public int size() {
return this.size;
}
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);
}
}
- 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();
@@ -339,7 +478,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) {
@@ -351,7 +490,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;
@@ -368,7 +507,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;
}
}
@@ -389,7 +528,7 @@ public final class ChunkEntitySlices {
this.entitiesBySection = new BasicEntityList[sectionCount];
}
- public synchronized void addEntity(final Entity entity, final int sectionIndex) {
+ public void addEntity(final Entity entity, final int sectionIndex) {
BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
if (list != null && list.has(entity)) {
@@ -405,7 +544,7 @@ public final class ChunkEntitySlices {
++this.count;
}
- public synchronized void removeEntity(final Entity entity, final int sectionIndex) {
+ public void removeEntity(final Entity entity, final int sectionIndex) {
final BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
if (list == null || !list.remove(entity)) {
@@ -420,7 +559,7 @@ public final class ChunkEntitySlices {
}
}
- public synchronized void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
+ public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
if (this.count == 0) {
return;
}
@@ -458,7 +597,7 @@ public final class ChunkEntitySlices {
}
}
- public synchronized void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List<Entity> into,
+ public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List<Entity> into,
final Predicate<? super Entity> predicate) {
if (this.count == 0) {
return;
@@ -493,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;
}
@@ -509,7 +648,7 @@ public final class ChunkEntitySlices {
}
}
- public synchronized void getEntitiesWithEnderDragonParts(final Entity except, final Class<?> clazz, final AABB box, final List<Entity> into,
+ public void getEntitiesWithEnderDragonParts(final Entity except, final Class<?> clazz, final AABB box, final List<Entity> into,
final Predicate<? super Entity> predicate) {
if (this.count == 0) {
return;
@@ -544,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;
}
@@ -560,7 +699,7 @@ public final class ChunkEntitySlices {
}
}
- public synchronized <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
+ public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate) {
if (this.count == 0) {
return;
@@ -590,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,685 +0,0 @@
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/Era4FunMC/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

@@ -0,0 +1,306 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Fri, 27 Jan 2023 19:55:14 +0800
Subject: [PATCH] MikuServer: Code error fix
Original license: MIT
Original project: https://github.com/MikuMC/MikuServer
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 15eeea40bb7a44470f6f3f0e2473cb451812eec1..c256b6175f8b859883b849c6cd623f680b78be32 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
@@ -2,7 +2,6 @@ package io.papermc.paper.chunk.system.scheduling;
import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
import ca.spottedleaf.concurrentutil.map.SWMRLong2ObjectHashTable;
-import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
@@ -29,8 +28,6 @@ import net.minecraft.server.level.TicketType;
import net.minecraft.util.SortedArraySet;
import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos;
-import net.minecraft.world.level.chunk.ChunkAccess;
-import net.minecraft.world.level.chunk.ChunkStatus;
import org.bukkit.plugin.Plugin;
import org.slf4j.Logger;
import java.io.IOException;
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/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
index 8950b220b9a3512cd4667beb7bdec0e82e07edc6..91fe5898270495aa89586b74ebfd649ef1e0b342 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
@@ -4,8 +4,7 @@ import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.longs.Long2ByteMap;
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
-import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
-import it.unimi.dsi.fastutil.longs.LongSet;
+
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,254 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Fri, 27 Jan 2023 20:31:46 +0800
Subject: [PATCH] MikuServer: Bug fixes in CPS and Pathfinding
Original license: MIT
Original project: https://github.com/MikuMC/MikuServer
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 686852f5cb6303381c45a673a0daf79a2d3a9dfe..d023e6347e78bde17c67ed299504a081fe895858 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -843,12 +843,11 @@ 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);
});
}
@@ -1031,9 +1030,11 @@ public class ServerChunkCache extends ChunkSource {
@Override
// CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
public boolean pollTask() {
- ServerChunkCache.this.chunkMap.playerChunkManager.tickMidTick();
- if (ServerChunkCache.this.runDistanceManagerUpdates()) {
- return true;
+ synchronized (this){
+ ServerChunkCache.this.chunkMap.playerChunkManager.tickMidTick();
+ if (ServerChunkCache.this.runDistanceManagerUpdates()) {
+ return true;
+ }
}
return super.pollTask() | ServerChunkCache.this.level.chunkTaskScheduler.executeMainThreadTask(); // Paper - rewrite chunk system
}
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 619730e2013b4c8907ba7b7e6927b57b4ac76ec7..f5aff6d724185e1ada8736bdb6dd12b143812e83 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3029,7 +3029,7 @@ public abstract class LivingEntity extends Entity {
}
if (!this.isRemoved()) {
- this.aiStep();
+ MinecraftServer.getServer().asyncExecutor.executeWithCallBack(this::aiStep,()->{});
}
double d0 = this.getX() - this.xo;
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index 9071abd67c421a528514a6437a1b8dde02e068ed..ed645b74bfc6e3cffe75cb4e30277cd5e98ca9c1 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -881,43 +881,42 @@ public abstract class Mob extends LivingEntity {
if (i % 10 == 0) this.sensing.tick(); // petal - only refresh line of sight cache every half second
//this.level.getProfiler().pop(); // Purpur
- MinecraftServer.getServer().asyncExecutor.executeWithCallBack(() -> {
- if (i % 2 != 0 && this.tickCount > 1) {
- //this.level.getProfiler().push("targetSelector"); // Purpur
- if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
- this.targetSelector.tickRunningGoals(false);
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("goalSelector"); // Purpur
- if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
- this.goalSelector.tickRunningGoals(false);
- //this.level.getProfiler().pop(); // Purpur
- } else {
- //this.level.getProfiler().push("targetSelector"); // Purpur
- if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
- this.targetSelector.tick();
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("goalSelector"); // Purpur
- if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
- this.goalSelector.tick();
- //this.level.getProfiler().pop(); // Purpur
- }
-
- //this.level.getProfiler().push("navigation"); // Purpur
- this.navigation.tick();
+ if (i % 2 != 0 && this.tickCount > 1) {
+ //this.level.getProfiler().push("targetSelector"); // Purpur
+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+ this.targetSelector.tickRunningGoals(false);
//this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("mob tick"); // Purpur
- this.customServerAiStep();
+ //this.level.getProfiler().push("goalSelector"); // Purpur
+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+ this.goalSelector.tickRunningGoals(false);
//this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("controls"); // Purpur
- //this.level.getProfiler().push("move"); // Purpur
- this.moveControl.tick();
- //this.level.getProfiler().popPush("look"); // Purpur
- this.lookControl.tick();
- //this.level.getProfiler().popPush("jump"); // Purpur
- this.jumpControl.tick();
+ } else {
+ //this.level.getProfiler().push("targetSelector"); // Purpur
+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+ this.targetSelector.tick();
//this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("goalSelector"); // Purpur
+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+ this.goalSelector.tick();
//this.level.getProfiler().pop(); // Purpur
- }, this::sendDebugPackets);
+ }
+
+ //this.level.getProfiler().push("navigation"); // Purpur
+ this.navigation.tick();
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("mob tick"); // Purpur
+ this.customServerAiStep();
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("controls"); // Purpur
+ //this.level.getProfiler().push("move"); // Purpur
+ this.moveControl.tick();
+ //this.level.getProfiler().popPush("look"); // Purpur
+ this.lookControl.tick();
+ //this.level.getProfiler().popPush("jump"); // Purpur
+ this.jumpControl.tick();
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().pop(); // Purpur
+ this.sendDebugPackets();
}
protected void sendDebugPackets() {
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java.rej b/src/main/java/net/minecraft/world/entity/Mob.java.rej
new file mode 100644
index 0000000000000000000000000000000000000000..68eddcd9c30a0b4ab690fc54de481847107cef00
--- /dev/null
+++ b/src/main/java/net/minecraft/world/entity/Mob.java.rej
@@ -0,0 +1,59 @@
+diff a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java (rejected hunks)
+@@ -910,32 +910,31 @@ public abstract class Mob extends LivingEntity {
+ //this.level.getProfiler().pop(); // Purpur
+ int i = this.level.getServer().getTickCount() + this.getId();
+
+- MinecraftServer.getServer().asyncExecutor.executeWithCallBack(()->{
+- if (i % 2 != 0 && this.tickCount > 1) {
+- //this.level.getProfiler().push("targetSelector"); // Purpur
+- if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+- this.targetSelector.tickRunningGoals(false);
+- //this.level.getProfiler().pop(); // Purpur
+- //this.level.getProfiler().push("goalSelector"); // Purpur
+- if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+- this.goalSelector.tickRunningGoals(false);
+- //this.level.getProfiler().pop(); // Purpur
+- } else {
+- //this.level.getProfiler().push("targetSelector"); // Purpur
+- if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+- this.targetSelector.tick();
+- //this.level.getProfiler().pop(); // Purpur
+- //this.level.getProfiler().push("goalSelector"); // Purpur
+- if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
+- this.goalSelector.tick();
+- //this.level.getProfiler().pop(); // Purpur
+- }
+- this.navigation.tick();
+- this.customServerAiStep();
+- this.moveControl.tick();
+- this.lookControl.tick();
+- this.jumpControl.tick();
+- }, this::sendDebugPackets);
++ if (i % 2 != 0 && this.tickCount > 1) {
++ //this.level.getProfiler().push("targetSelector"); // Purpur
++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
++ this.targetSelector.tickRunningGoals(false);
++ //this.level.getProfiler().pop(); // Purpur
++ //this.level.getProfiler().push("goalSelector"); // Purpur
++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
++ this.goalSelector.tickRunningGoals(false);
++ //this.level.getProfiler().pop(); // Purpur
++ } else {
++ //this.level.getProfiler().push("targetSelector"); // Purpur
++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
++ this.targetSelector.tick();
++ //this.level.getProfiler().pop(); // Purpur
++ //this.level.getProfiler().push("goalSelector"); // Purpur
++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
++ this.goalSelector.tick();
++ //this.level.getProfiler().pop(); // Purpur
++ }
++ this.navigation.tick();
++ this.customServerAiStep();
++ this.moveControl.tick();
++ this.lookControl.tick();
++ this.jumpControl.tick();
++ this.sendDebugPackets();
+ }
+
+ protected void sendDebugPackets() {
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 57fcf3910f45ce371ac2e237b277b1034caaac4e..13f5950bfffe49ffc3b9e57b5f6f674c70ba5e80 100644
--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
@@ -1,14 +1,18 @@
package net.minecraft.world.level.entity;
+import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
+
+import java.util.Iterator;
+import java.util.List;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
public class EntityTickList {
- public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Entity> entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? // Pufferfish - private->public
+ public final List<Entity> entities = Lists.newCopyOnWriteArrayList();
private void ensureActiveIsNotIterated() {
// Paper - replace with better logic, do not delay removals
@@ -36,13 +40,9 @@ public class EntityTickList {
// Paper start - replace with better logic, do not delay removals/additions
// To ensure nothing weird happens with dimension travelling, do not iterate over new entries...
// (by dfl iterator() is configured to not iterate over new entries)
- io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Entity> iterator = this.entities.iterator();
- try {
- while (iterator.hasNext()) {
- action.accept(iterator.next());
- }
- } finally {
- iterator.finishedIterating();
+ Iterator<Entity> iterator = this.entities.iterator();
+ while (iterator.hasNext()) {
+ action.accept(iterator.next());
}
// Paper end - replace with better logic, do not delay removals/additions
}
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();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,627 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 28 Jan 2023 12:23:31 +0800
Subject: [PATCH] MikuServer: Fix some issue in Entity sensing and aiStep
Original license: MIT
Original project: https://github.com/MikuMC/MikuServer
diff --git a/src/main/java/co/m2ek4u/aoame/CallbackExecutor.java b/src/main/java/co/mikumc/mikuserver/concurrent/CallbackExecutor.java
similarity index 98%
rename from src/main/java/co/m2ek4u/aoame/CallbackExecutor.java
rename to src/main/java/co/mikumc/mikuserver/concurrent/CallbackExecutor.java
index b861405a7626ba8fa677c455bf6507253b33c157..678b389ef829bcda55ea06f5f19b96c81f78bd9b 100644
--- a/src/main/java/co/m2ek4u/aoame/CallbackExecutor.java
+++ b/src/main/java/co/mikumc/mikuserver/concurrent/CallbackExecutor.java
@@ -1,4 +1,4 @@
-package co.m2ek4u.aoame;
+package co.mikumc.mikuserver.concurrent;
import org.jetbrains.annotations.NotNull;
import java.util.Queue;
diff --git a/src/main/java/co/m2ek4u/aoame/AnotherTickThread.java b/src/main/java/co/mikumc/mikuserver/utils/AnotherTickThread.java
similarity index 87%
rename from src/main/java/co/m2ek4u/aoame/AnotherTickThread.java
rename to src/main/java/co/mikumc/mikuserver/utils/AnotherTickThread.java
index d5fe52beb25e7a95549cdf0ae19edf6029f10642..8dd4423fb1477c901e88c07537ca736eb4f9a8cc 100644
--- a/src/main/java/co/m2ek4u/aoame/AnotherTickThread.java
+++ b/src/main/java/co/mikumc/mikuserver/utils/AnotherTickThread.java
@@ -1,4 +1,4 @@
-package co.m2ek4u.aoame;
+package co.mikumc.mikuserver.utils;
import io.papermc.paper.util.TickThread;
diff --git a/src/main/java/co/mikumc/mikuserver/utils/EntityPositionCache.java b/src/main/java/co/mikumc/mikuserver/utils/EntityPositionCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..245fd68d040a46f58bebbbebe11ab6fa2072f08b
--- /dev/null
+++ b/src/main/java/co/mikumc/mikuserver/utils/EntityPositionCache.java
@@ -0,0 +1,60 @@
+package co.mikumc.mikuserver.utils;
+
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.phys.Vec3;
+import org.jetbrains.annotations.NotNull;
+
+public class EntityPositionCache {
+ private final double x;
+ private final double y;
+ private final double z;
+ private final LivingEntity currentEntity;
+
+ public EntityPositionCache(@NotNull LivingEntity entity){
+ this.x = entity.getX();
+ this.y = entity.getY();
+ this.z = entity.getZ();
+ this.currentEntity = entity;
+ }
+
+ public LivingEntity getCurrentEntity() {
+ return this.currentEntity;
+ }
+
+ public double getX() {
+ return this.x;
+ }
+
+ public double getY() {
+ return this.y;
+ }
+
+ public double getZ() {
+ return this.z;
+ }
+
+ public double distanceToSqr(double x, double y, double z) {
+ double d3 = this.x - x;
+ double d4 = this.y - y;
+ double d5 = this.z - z;
+
+ return d3 * d3 + d4 * d4 + d5 * d5;
+ }
+
+ public double distanceToSqr(Entity entity) {
+ return this.distanceToSqr(entity.position());
+ }
+
+ public double distanceToSqr(Vec3 vector) {
+ double d0 = this.x - vector.x;
+ double d1 = this.y - vector.y;
+ double d2 = this.z - vector.z;
+
+ return d0 * d0 + d1 * d1 + d2 * d2;
+ }
+
+ public double distanceToSqr(EntityPositionCache entityPositionCache) {
+ return this.distanceToSqr(entityPositionCache.getX(),entityPositionCache.getY(),entityPositionCache.getZ());
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 9601de5775667c2d94e07b89bb7605b1e03d413d..28af12b2e670ab2a2d4dc96f340da49da0071737 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1,12 +1,9 @@
package net.minecraft.server;
-import co.m2ek4u.aoame.AnotherTickThread;
-import co.m2ek4u.aoame.CallbackExecutor;
+import co.mikumc.mikuserver.utils.AnotherTickThread;
+import co.mikumc.mikuserver.concurrent.CallbackExecutor;
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 +83,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 +106,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 +177,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
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 824f26db01750866a2d9bbeefe17457f84d19504..32ce19ff00d6a31b7dc994d3320ad967f96d4f61 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1,6 +1,6 @@
package net.minecraft.server.level;
-import co.m2ek4u.aoame.AnotherTickThread;
+import co.mikumc.mikuserver.utils.AnotherTickThread;
import com.google.common.annotations.VisibleForTesting;
import co.aikar.timings.TimingHistory; // Paper
import com.google.common.collect.Lists;
@@ -51,7 +51,6 @@ 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;
@@ -77,7 +76,6 @@ 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.world.DifficultyInstance;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
@@ -129,12 +127,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;
@@ -161,16 +157,13 @@ 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
@@ -994,7 +987,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");
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index f5aff6d724185e1ada8736bdb6dd12b143812e83..cb5a4e6a071977fcf82b6b0384735a4bec3564fc 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3029,7 +3029,13 @@ public abstract class LivingEntity extends Entity {
}
if (!this.isRemoved()) {
- MinecraftServer.getServer().asyncExecutor.executeWithCallBack(this::aiStep,()->{});
+ //MikuServer start --async AI
+ if(this instanceof net.minecraft.world.entity.player.Player){
+ this.aiStep(); //Skip player
+ }else {
+ MinecraftServer.getServer().asyncExecutor.executeWithCallBack(this::aiStep,()->{});
+ }
+ //MikuServer end
}
double d0 = this.getX() - this.xo;
@@ -3406,9 +3412,11 @@ public abstract class LivingEntity extends Entity {
this.jumpInLiquid(FluidTags.LAVA);
} else if ((this.onGround || flag && d7 <= d8) && this.noJumpDelay == 0) {
if (new com.destroystokyo.paper.event.entity.EntityJumpEvent(getBukkitLivingEntity()).callEvent()) { // Paper
- this.jumpFromGround();
- this.noJumpDelay = 10;
- } else { this.setJumping(false); } // Paper - setJumping(false) stops a potential loop
+ this.jumpFromGround();
+ this.noJumpDelay = 10;
+ } else {
+ this.setJumping(false);
+ } // Paper - setJumping(false) stops a potential loop
}
} else {
this.noJumpDelay = 0;
@@ -3421,102 +3429,51 @@ public abstract class LivingEntity extends Entity {
this.updateFallFlying();
AABB axisalignedbb = this.getBoundingBox();
- if (this instanceof net.minecraft.world.entity.player.Player) {
- // SpigotTimings.timerEntityAIMove.startTiming(); // Spigot // Paper
- this.travel(new Vec3((double) this.xxa, (double) this.yya, (double) this.zza));
- // SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot // Paper
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("freezing"); // Purpur
- boolean flag1 = this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES);
- int i;
+ // SpigotTimings.timerEntityAIMove.startTiming(); // Spigot // Paper
+ this.travel(new Vec3((double) this.xxa, (double) this.yya, (double) this.zza));
+ // SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot // Paper
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("freezing"); // Purpur
+ boolean flag1 = this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES);
+ int i;
- if (!this.level.isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API
- i = this.getTicksFrozen();
- if (this.isInPowderSnow && this.canFreeze()) {
- this.setTicksFrozen(Math.min(this.getTicksRequiredToFreeze(), i + 1));
- } else {
- this.setTicksFrozen(Math.max(0, i - 2));
- }
+ if (!this.level.isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API
+ i = this.getTicksFrozen();
+ if (this.isInPowderSnow && this.canFreeze()) {
+ this.setTicksFrozen(Math.min(this.getTicksRequiredToFreeze(), i + 1));
+ } else {
+ this.setTicksFrozen(Math.max(0, i - 2));
}
+ }
- this.removeFrost();
- this.tryAddFrost();
- if (!this.level.isClientSide && this.tickCount % 40 == 0 && this.isFullyFrozen() && this.canFreeze()) {
- i = flag1 ? 5 : 1;
- this.hurt(DamageSource.FREEZE, (float) i);
- }
+ this.removeFrost();
+ this.tryAddFrost();
+ if (!this.level.isClientSide && this.tickCount % 40 == 0 && this.isFullyFrozen() && this.canFreeze()) {
+ i = flag1 ? 5 : 1;
+ this.hurt(DamageSource.FREEZE, (float) i);
+ }
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("push"); // Purpur
- if (this.autoSpinAttackTicks > 0) {
- --this.autoSpinAttackTicks;
- this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox());
- }
+ //this.level.getProfiler().pop(); // Purpur
+ //this.level.getProfiler().push("push"); // Purpur
+ if (this.autoSpinAttackTicks > 0) {
+ --this.autoSpinAttackTicks;
+ this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox());
+ }
- this.pushEntities();
- //this.level.getProfiler().pop(); // Purpur
- // Paper start
- if (((ServerLevel) this.level).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
- if (this.xo != getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
- Location from = new Location(this.level.getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
- Location to = new Location(this.level.getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
- io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
- if (!event.callEvent()) {
- absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
- } else if (!to.equals(event.getTo())) {
- absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
- }
+ this.pushEntities();
+ //this.level.getProfiler().pop(); // Purpur
+ // Paper start
+ if (((ServerLevel) this.level).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
+ if (this.xo != getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
+ Location from = new Location(this.level.getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
+ Location to = new Location(this.level.getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
+ io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
+ if (!event.callEvent()) {
+ absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
+ } else if (!to.equals(event.getTo())) {
+ absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
}
}
- } else {
- MinecraftServer.getServer().asyncExecutor.executeWithCallBack(() -> {
- // SpigotTimings.timerEntityAIMove.startTiming(); // Spigot // Paper
- this.travel(new Vec3((double) this.xxa, (double) this.yya, (double) this.zza));
- // SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot // Paper
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("freezing"); // Purpur
- boolean flag1 = this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES);
- int i;
-
- if (!this.level.isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API
- i = this.getTicksFrozen();
- if (this.isInPowderSnow && this.canFreeze()) {
- this.setTicksFrozen(Math.min(this.getTicksRequiredToFreeze(), i + 1));
- } else {
- this.setTicksFrozen(Math.max(0, i - 2));
- }
- }
-
- this.removeFrost();
- this.tryAddFrost();
- if (!this.level.isClientSide && this.tickCount % 40 == 0 && this.isFullyFrozen() && this.canFreeze()) {
- i = flag1 ? 5 : 1;
- this.hurt(DamageSource.FREEZE, (float) i);
- }
-
- //this.level.getProfiler().pop(); // Purpur
- //this.level.getProfiler().push("push"); // Purpur
- if (this.autoSpinAttackTicks > 0) {
- --this.autoSpinAttackTicks;
- this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox());
- }
-
- this.pushEntities();
- //this.level.getProfiler().pop(); // Purpur
- }, () -> {
- if (((ServerLevel) this.level).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
- if (this.xo != getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
- Location from = new Location(this.level.getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
- Location to = new Location(this.level.getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
- io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
- if (!event.callEvent()) {
- absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
- } else if (!to.equals(event.getTo())) {
- absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
- }
- }
- }
- });
}
// Paper end
if (!this.level.isClientSide && this.isSensitiveToWater() && this.isInWaterRainOrBubble()) {
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java.rej b/src/main/java/net/minecraft/world/entity/Mob.java.rej
deleted file mode 100644
index 68eddcd9c30a0b4ab690fc54de481847107cef00..0000000000000000000000000000000000000000
--- a/src/main/java/net/minecraft/world/entity/Mob.java.rej
+++ /dev/null
@@ -1,59 +0,0 @@
-diff a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java (rejected hunks)
-@@ -910,32 +910,31 @@ public abstract class Mob extends LivingEntity {
- //this.level.getProfiler().pop(); // Purpur
- int i = this.level.getServer().getTickCount() + this.getId();
-
-- MinecraftServer.getServer().asyncExecutor.executeWithCallBack(()->{
-- if (i % 2 != 0 && this.tickCount > 1) {
-- //this.level.getProfiler().push("targetSelector"); // Purpur
-- if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
-- this.targetSelector.tickRunningGoals(false);
-- //this.level.getProfiler().pop(); // Purpur
-- //this.level.getProfiler().push("goalSelector"); // Purpur
-- if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
-- this.goalSelector.tickRunningGoals(false);
-- //this.level.getProfiler().pop(); // Purpur
-- } else {
-- //this.level.getProfiler().push("targetSelector"); // Purpur
-- if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
-- this.targetSelector.tick();
-- //this.level.getProfiler().pop(); // Purpur
-- //this.level.getProfiler().push("goalSelector"); // Purpur
-- if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
-- this.goalSelector.tick();
-- //this.level.getProfiler().pop(); // Purpur
-- }
-- this.navigation.tick();
-- this.customServerAiStep();
-- this.moveControl.tick();
-- this.lookControl.tick();
-- this.jumpControl.tick();
-- }, this::sendDebugPackets);
-+ if (i % 2 != 0 && this.tickCount > 1) {
-+ //this.level.getProfiler().push("targetSelector"); // Purpur
-+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
-+ this.targetSelector.tickRunningGoals(false);
-+ //this.level.getProfiler().pop(); // Purpur
-+ //this.level.getProfiler().push("goalSelector"); // Purpur
-+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
-+ this.goalSelector.tickRunningGoals(false);
-+ //this.level.getProfiler().pop(); // Purpur
-+ } else {
-+ //this.level.getProfiler().push("targetSelector"); // Purpur
-+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
-+ this.targetSelector.tick();
-+ //this.level.getProfiler().pop(); // Purpur
-+ //this.level.getProfiler().push("goalSelector"); // Purpur
-+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
-+ this.goalSelector.tick();
-+ //this.level.getProfiler().pop(); // Purpur
-+ }
-+ this.navigation.tick();
-+ this.customServerAiStep();
-+ this.moveControl.tick();
-+ this.lookControl.tick();
-+ this.jumpControl.tick();
-+ this.sendDebugPackets();
- }
-
- protected void sendDebugPackets() {
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
index d8cf99a3014a4b8152ae819fa663c2fdf34dce57..e14d02c04dde5030c0dff3faea9294c87c40910d 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
@@ -1,9 +1,13 @@
package net.minecraft.world.entity.ai.sensing;
-
+import co.mikumc.mikuserver.utils.EntityPositionCache;
import com.google.common.collect.ImmutableSet;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import com.google.common.collect.Lists;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.Brain;
@@ -12,16 +16,29 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities;
import net.minecraft.world.phys.AABB;
public class NearestLivingEntitySensor<T extends LivingEntity> extends Sensor<T> {
+ private final List<EntityPositionCache> entitiesCache = ObjectLists.synchronize(new ObjectArrayList<>());
+ private final AtomicBoolean calling = new AtomicBoolean(false);
+
@Override
protected void doTick(ServerLevel world, T entity) {
- AABB aABB = entity.getBoundingBox().inflate((double)this.radiusXZ(), (double)this.radiusY(), (double)this.radiusXZ());
- List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, aABB, (e) -> {
- return e != entity && e.isAlive();
- });
- list.sort(Comparator.comparingDouble(entity::distanceToSqr));
- Brain<?> brain = entity.getBrain();
- brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, list);
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(entity, list));
+ if (!this.calling.get()){
+ this.calling.set(true);
+ try {
+ AABB aABB = entity.getBoundingBox().inflate(this.radiusXZ(), this.radiusY(), this.radiusXZ());
+ this.entitiesCache.clear();
+ this.entitiesCache.addAll(world.getEntitiesOfClass(LivingEntity.class, aABB, (e) -> e != entity && e.isAlive()).stream().map(EntityPositionCache::new).toList());
+ final EntityPositionCache compareCache = new EntityPositionCache(entity);
+ this.entitiesCache.sort(Comparator.comparingDouble(compareCache::distanceToSqr));
+
+ Brain<?> brain = entity.getBrain();
+ final List<LivingEntity> list = Lists.newCopyOnWriteArrayList();
+ list.addAll(this.entitiesCache.stream().map(EntityPositionCache::getCurrentEntity).toList());
+ brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES,list);
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(entity, list));
+ }finally {
+ this.calling.set(false);
+ }
+ }
}
protected int radiusXZ() {
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
index 75d9c4f011b5a97def215784c92bb57bbb35d06b..2a3d5085b47824c310cfaa5feec3a50965f804b0 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
@@ -1,11 +1,11 @@
package net.minecraft.world.entity.ai.sensing;
+import co.mikumc.mikuserver.utils.EntityPositionCache;
import com.google.common.collect.ImmutableSet;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.LivingEntity;
@@ -14,6 +14,9 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.player.Player;
public class PlayerSensor extends Sensor<LivingEntity> {
+ private final List<Player> playerList = ObjectLists.synchronize(new ObjectArrayList<>());
+ private final AtomicBoolean calling = new AtomicBoolean();
+
@Override
public Set<MemoryModuleType<?>> requires() {
return ImmutableSet.of(MemoryModuleType.NEAREST_PLAYERS, MemoryModuleType.NEAREST_VISIBLE_PLAYER, MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER);
@@ -21,30 +24,51 @@ public class PlayerSensor extends Sensor<LivingEntity> {
@Override
protected void doTick(ServerLevel world, LivingEntity entity) {
- // Paper start - remove streams
- List<Player> players = (List)world.getNearbyPlayers(entity, entity.getX(), entity.getY(), entity.getZ(), 16.0D, EntitySelector.NO_SPECTATORS);
- players.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2)));
- Brain<?> brain = entity.getBrain();
-
- brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, players);
-
- Player firstTargetable = null;
- Player firstAttackable = null;
- for (int index = 0, len = players.size(); index < len; ++index) {
- Player player = players.get(index);
- if (firstTargetable == null && isEntityTargetable(entity, player)) {
- firstTargetable = player;
- }
- if (firstAttackable == null && isEntityAttackable(entity, player)) {
- firstAttackable = player;
- }
+ if (this.calling.get()){
+ return;
+ }
+
+ this.calling.set(true);
+ try {
+ // Paper start - remove streams
+ List<EntityPositionCache> playersPosCaches = new ArrayList<>(List.of(world
+ .getNearbyPlayers(entity, entity.getX(), entity.getY(), entity.getZ(), 16.0D, EntitySelector.NO_SPECTATORS)
+ .stream()
+ .map(EntityPositionCache::new)
+ .toArray(EntityPositionCache[]::new)));
+
+ final EntityPositionCache entityPositionCache = new EntityPositionCache(entity);
+
+ playersPosCaches.sort(Comparator.comparingDouble(entityPositionCache::distanceToSqr));
+
+ final List<Player> players = playersPosCaches
+ .stream()
+ .map(cache -> ((Player) cache.getCurrentEntity()))
+ .toList();
+
+ Brain<?> brain = entity.getBrain();
+
+ brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, players);
+
+ Player firstTargetable = null;
+ Player firstAttackable = null;
+ for (Player player : players) {
+ if (firstTargetable == null && isEntityTargetable(entity, player)) {
+ firstTargetable = player;
+ }
+ if (firstAttackable == null && isEntityAttackable(entity, player)) {
+ firstAttackable = player;
+ }
- if (firstAttackable != null && firstTargetable != null) {
- break;
+ if (firstAttackable != null && firstTargetable != null) {
+ break;
+ }
}
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, firstTargetable);
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, Optional.ofNullable(firstAttackable));
+ // Paper end - remove streams
+ }finally {
+ this.calling.set(false);
}
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, firstTargetable);
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, Optional.ofNullable(firstAttackable));
- // Paper end - remove streams
}
}

View File

@@ -1,39 +0,0 @@
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/Era4FunMC/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,195 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 28 Jan 2023 12:47:47 +0800
Subject: [PATCH] MikuServer: Refactor and fix a CME in Gossip
Original license: MIT
Original project: https://github.com/MikuMC/MikuServer
diff --git a/src/main/java/co/mikumc/mikuserver/PublicConstants.java b/src/main/java/co/mikumc/mikuserver/PublicConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..c6da0ce6b7181d3fb9b6ea8c2a600741d92e8f18
--- /dev/null
+++ b/src/main/java/co/mikumc/mikuserver/PublicConstants.java
@@ -0,0 +1,24 @@
+package co.mikumc.mikuserver;
+
+import co.mikumc.mikuserver.concurrent.CallbackExecutor;
+import co.mikumc.mikuserver.utils.AnotherTickThread;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class PublicConstants {
+ public static final AtomicInteger threadId = new AtomicInteger();
+ public static final CallbackExecutor asyncExecutor = new CallbackExecutor(
+ Runtime.getRuntime().availableProcessors(),
+ Runtime.getRuntime().availableProcessors(),
+ 50,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ task -> {
+ AnotherTickThread worker = new AnotherTickThread(task,"MikuServer-Async-Worker # "+threadId.getAndIncrement());
+ worker.setDaemon(true);
+ return worker;
+ }
+ );
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 28af12b2e670ab2a2d4dc96f340da49da0071737..76bc84864c306ff14c28312d9a0e923e8589ebe0 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1,7 +1,6 @@
package net.minecraft.server;
-import co.mikumc.mikuserver.utils.AnotherTickThread;
-import co.mikumc.mikuserver.concurrent.CallbackExecutor;
+import co.mikumc.mikuserver.PublicConstants;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -41,7 +40,6 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -271,21 +269,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private final StructureTemplateManager structureTemplateManager;
protected WorldData worldData;
private volatile boolean isSaving;
-
- private final AtomicInteger threadId = new AtomicInteger();
- public final CallbackExecutor asyncExecutor = new CallbackExecutor(
- Runtime.getRuntime().availableProcessors(),
- Runtime.getRuntime().availableProcessors(),
- 50,
- TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<>(),
- task -> {
- AnotherTickThread worker = new AnotherTickThread(task,"Entity-Async-Worker # "+threadId.getAndIncrement());
- worker.setDaemon(true);
- return worker;
- }
- );
-
// CraftBukkit start
public final WorldLoader.DataLoadContext worldLoader;
public org.bukkit.craftbukkit.CraftServer server;
@@ -1390,8 +1373,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public void onServerExit() {}
public void tickServer(BooleanSupplier shouldKeepTicking) {
- if (this.asyncExecutor.isSubmittingStarted()){
- this.asyncExecutor.stopSubmitting();
+ if (PublicConstants.asyncExecutor.isSubmittingStarted()){
+ PublicConstants.asyncExecutor.stopSubmitting();
}
//co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Paper // Purpur
long i = Util.getNanos();
@@ -1406,7 +1389,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper
++this.tickCount;
- this.asyncExecutor.startSubmitting();
+ PublicConstants.asyncExecutor.startSubmitting();
this.tickChildren(shouldKeepTicking);
if (i - this.lastServerStatus >= 5000000000L) {
this.lastServerStatus = i;
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index cb5a4e6a071977fcf82b6b0384735a4bec3564fc..3aac9b26d5e94b4c3cf8951f0806458be0920c57 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -1,5 +1,6 @@
package net.minecraft.world.entity;
+import co.mikumc.mikuserver.PublicConstants;
import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; // Paper
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
@@ -44,7 +45,6 @@ import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
@@ -3033,7 +3033,7 @@ public abstract class LivingEntity extends Entity {
if(this instanceof net.minecraft.world.entity.player.Player){
this.aiStep(); //Skip player
}else {
- MinecraftServer.getServer().asyncExecutor.executeWithCallBack(this::aiStep,()->{});
+ PublicConstants.asyncExecutor.executeWithCallBack(this::aiStep,()->{});
}
//MikuServer end
}
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index ed645b74bfc6e3cffe75cb4e30277cd5e98ca9c1..70800e37d8d89fda17adefc6e1cb470413b24864 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -1,5 +1,6 @@
package net.minecraft.world.entity;
+import co.mikumc.mikuserver.PublicConstants;
import com.google.common.collect.Maps;
import java.util.Arrays;
import java.util.Iterator;
@@ -23,7 +24,6 @@ import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvent;
@@ -217,7 +217,7 @@ public abstract class Mob extends LivingEntity {
public void inactiveTick() {
super.inactiveTick();
boolean isThrottled = gg.pufferfish.pufferfish.PufferfishConfig.throttleInactiveGoalSelectorTick && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking
- MinecraftServer.getServer().asyncExecutor.executeWithCallBack(()->{
+ PublicConstants.asyncExecutor.executeWithCallBack(()->{
if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking
this.goalSelector.tick();
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
index 097007c1c25ba55d9916fc820dd1d1149d81f6f4..16eec12db529dd513e0971289a9326652369de58 100644
--- a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
+++ b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
@@ -30,11 +30,11 @@ import org.slf4j.Logger;
public class GossipContainer {
private static final Logger LOGGER = LogUtils.getLogger();
public static final int DISCARD_THRESHOLD = 2;
- public final Map<UUID, GossipContainer.EntityGossips> gossips = Maps.newHashMap();
+ public final Map<UUID, GossipContainer.EntityGossips> gossips = Maps.newConcurrentMap();
@VisibleForDebug
public Map<UUID, Object2IntMap<GossipType>> getGossipEntries() {
- Map<UUID, Object2IntMap<GossipType>> map = Maps.newHashMap();
+ Map<UUID, Object2IntMap<GossipType>> map = Maps.newConcurrentMap();
this.gossips.keySet().forEach((uuid) -> {
GossipContainer.EntityGossips entityGossips = this.gossips.get(uuid);
map.put(uuid, entityGossips.entries);
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
index eb5257fabef397566e784247922fb27e61f96313..69da89c25d23f071497eca9f26cd0fbc404035f5 100644
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
@@ -1,5 +1,6 @@
package net.minecraft.world.entity.npc;
+import co.mikumc.mikuserver.PublicConstants;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -274,7 +275,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
if (this.getUnhappyCounter() > 0) {
this.setUnhappyCounter(this.getUnhappyCounter() - 1);
}
- MinecraftServer.getServer().asyncExecutor.executeWithCallBack(()->{
+ PublicConstants.asyncExecutor.executeWithCallBack(()->{
if (this.isEffectiveAi()) {
if (level.spigotConfig.tickInactiveVillagers) {
this.customServerAiStep();

View File

@@ -1,23 +0,0 @@
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/Era4FunMC/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

@@ -0,0 +1,53 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 28 Jan 2023 12:51:08 +0800
Subject: [PATCH] MikuServer: Add shutdown hook to async executor and refactor
thread id counter
Original license: MIT
Original project: https://github.com/MikuMC/MikuServer
diff --git a/src/main/java/co/mikumc/mikuserver/PublicConstants.java b/src/main/java/co/mikumc/mikuserver/PublicConstants.java
index c6da0ce6b7181d3fb9b6ea8c2a600741d92e8f18..3f2e2e2be1d53b9257f99fb19a50491efc629925 100644
--- a/src/main/java/co/mikumc/mikuserver/PublicConstants.java
+++ b/src/main/java/co/mikumc/mikuserver/PublicConstants.java
@@ -8,17 +8,22 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class PublicConstants {
- public static final AtomicInteger threadId = new AtomicInteger();
+ private static final AtomicInteger asyncWorkerThreadIdCounter = new AtomicInteger();
public static final CallbackExecutor asyncExecutor = new CallbackExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors(),
50,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
- task -> {
- AnotherTickThread worker = new AnotherTickThread(task,"MikuServer-Async-Worker # "+threadId.getAndIncrement());
- worker.setDaemon(true);
- return worker;
- }
+ task -> new AnotherTickThread(task,"MikuServer-Async-Worker # "+ asyncWorkerThreadIdCounter.getAndIncrement())
);
+
+ static {
+ Runtime.getRuntime().addShutdownHook(new Thread(()->{
+ if (asyncExecutor.isSubmittingStarted()){
+ asyncExecutor.stopSubmitting();
+ }
+ asyncExecutor.shutdownNow();
+ }));
+ }
}
diff --git a/src/main/java/co/mikumc/mikuserver/utils/AnotherTickThread.java b/src/main/java/co/mikumc/mikuserver/utils/AnotherTickThread.java
index 8dd4423fb1477c901e88c07537ca736eb4f9a8cc..b3ea36c67e9eb5dafd1c7ff5abdf1233dfbd562b 100644
--- a/src/main/java/co/mikumc/mikuserver/utils/AnotherTickThread.java
+++ b/src/main/java/co/mikumc/mikuserver/utils/AnotherTickThread.java
@@ -9,5 +9,6 @@ public class AnotherTickThread extends TickThread {
public AnotherTickThread(Runnable run, String name) {
super(run, name);
+ this.setPriority(Thread.NORM_PRIORITY - 2);
}
}

View File

@@ -1,451 +0,0 @@
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/Era4FunMC/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

@@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 28 Jan 2023 13:05:12 +0800
Subject: [PATCH] MikuServer: Fix threading issue in event system
Original license: MIT
Original project: https://github.com/MikuMC/MikuServer
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 96cde1f86ca073e7e9e5799bcb12a10adf9230b2..30509d85391ce2fb170751170f3d4310ef856aea 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.mikumc.mikuserver.utils.AnotherTickThread;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
@@ -415,6 +416,11 @@ public final class CraftServer implements Server {
}
}
+ @Override
+ public boolean isCurrentThreadWorker(){
+ return Thread.currentThread() instanceof AnotherTickThread;
+ }
+
private void saveCommandsConfig() {
try {
this.commandsConfiguration.save(this.getCommandsConfigFile());

View File

@@ -1,317 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 15 Jan 2023 22:35:45 +0800
Subject: [PATCH] Hearse: Use a new queue in thread pool
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
index c0e7a9cf79ddf00827daba0aa9c7a32fa76b0c7c..8baccccee52b6e47bf51e51d976ad76920270ef4 100644
--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -5,6 +5,7 @@ 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 co.earthme.hearse.util.ArrayListBlockingQueue;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
@@ -40,7 +41,7 @@ public class ServerEntityTickHook {
workerCount,
0L,
TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<>(),
+ new ArrayListBlockingQueue<>(),
defFactory
);
Hearse.getWorkerManager().addWorker("entity",worker);
diff --git a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
index 8085eb700d8e5c20ebb5bfeceb78198c6e973019..987c98ea108d49c1335238bc529f782d3ec5b5e6 100644
--- a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
@@ -5,6 +5,7 @@ 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 co.earthme.hearse.util.ArrayListBlockingQueue;
import net.minecraft.CrashReport;
import net.minecraft.ReportedException;
import net.minecraft.server.MinecraftServer;
@@ -12,6 +13,7 @@ import net.minecraft.server.level.ServerLevel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -34,7 +36,7 @@ public class ServerLevelTickHook {
MinecraftServer.getServer().levels.size(),
Long.MAX_VALUE,
TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<>(),
+ new ArrayListBlockingQueue<>(),
workerFactory
);
worker.allowCoreThreadTimeOut(true);
diff --git a/src/main/java/co/earthme/hearse/util/ArrayListBlockingQueue.java b/src/main/java/co/earthme/hearse/util/ArrayListBlockingQueue.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2cea65ecfb8a41248e7ee74357b4127106f1d0a
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/util/ArrayListBlockingQueue.java
@@ -0,0 +1,253 @@
+package co.earthme.hearse.util;
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.*;
+
+public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
+ private final List<T> internalList = new ArrayList<>();
+ private final StampedLock editLock = new StampedLock();
+
+ @Override
+ public boolean add(T t) {
+ final long id = this.editLock.writeLock();
+ try {
+ return this.internalList.add(t);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public boolean offer(T t) {
+ final long id = this.editLock.writeLock();
+ try {
+ return this.internalList.add(t);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public T remove() {
+ final long id = this.editLock.writeLock();
+ try {
+ return this.internalList.remove(0);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public T poll() {
+ final long id = this.editLock.writeLock();
+ try {
+ return this.internalList.isEmpty() ? null : this.internalList.remove(0);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public T element() {
+ long id = this.editLock.readLock();
+ try {
+ if (this.internalList.isEmpty()){
+ throw new NoSuchElementException();
+ }
+ return this.internalList.get(0);
+ }finally {
+ this.editLock.unlockRead(id);
+ }
+ }
+
+ @Override
+ public T peek() {
+ long id = this.editLock.readLock();
+ try {
+ if (this.internalList.isEmpty()){
+ throw new NoSuchElementException();
+ }
+ return this.internalList.get(0);
+ }finally {
+ this.editLock.unlockRead(id);
+ }
+ }
+
+ @Override
+ public void put(T t) {
+ final long id = this.editLock.writeLock();
+ try {
+ this.internalList.add(t);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public boolean offer(T t, long timeout, TimeUnit unit) {
+ final long id = this.editLock.writeLock();
+ try {
+ return this.internalList.add(t);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public T take() throws InterruptedException {
+ T t;
+ while ((t = this.poll()) == null){
+ synchronized (this){
+ this.wait(1);
+ }
+ }
+ return t;
+ }
+
+ @Override
+ public T poll(long timeout, TimeUnit unit) throws InterruptedException {
+ T t;
+ while ((t = this.poll()) == null){
+ if (timeout == 0){
+ break;
+ }
+ synchronized (this){
+ unit.sleep(1);
+ }
+ timeout--;
+ }
+ return t;
+ }
+
+ @Override
+ public int remainingCapacity() {
+ throw new UnsupportedOperationException("remainingCapacity");
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ final long id = this.editLock.writeLock();
+ try {
+ return this.internalList.remove(o);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ final long id = this.editLock.writeLock();
+ try {
+ return new HashSet<>(this.internalList).containsAll(c);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends T> c) {
+ final long id = this.editLock.writeLock();
+ try {
+ return this.internalList.addAll(c);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ final long id = this.editLock.writeLock();
+ try {
+ return this.internalList.removeAll(c);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ final long id = this.editLock.writeLock();
+ try {
+ return this.internalList.retainAll(c);
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public void clear() {
+ final long id = this.editLock.writeLock();
+ try {
+ this.internalList.clear();
+ }finally {
+ this.editLock.unlockWrite(id);
+ }
+ }
+
+ @Override
+ public int size() {
+ long id = this.editLock.readLock();
+ try {
+ return this.internalList.size();
+ }finally {
+ this.editLock.unlockRead(id);
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ long id = this.editLock.readLock();
+ try {
+ return this.internalList.isEmpty();
+ }finally {
+ this.editLock.unlockRead(id);
+ }
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ long id = this.editLock.readLock();
+ try {
+ return this.internalList.contains(o);
+ }finally {
+ this.editLock.unlockRead(id);
+ }
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ throw new UnsupportedOperationException("Iterator");
+ }
+
+ @Override
+ public Object[] toArray() {
+ long id = this.editLock.readLock();
+ try {
+ return this.internalList.toArray();
+ }finally {
+ this.editLock.unlockRead(id);
+ }
+ }
+
+ @Override
+ public <T1> T1[] toArray(T1[] a) {
+ long id = this.editLock.readLock();
+ try {
+ return this.internalList.toArray(a);
+ }finally {
+ this.editLock.unlockRead(id);
+ }
+ }
+
+ @Override
+ public int drainTo(Collection<? super T> c) {
+ throw new UnsupportedOperationException("drainTo");
+ }
+
+ @Override
+ public int drainTo(Collection<? super T> c, int maxElements) {
+ throw new UnsupportedOperationException("drainTo");
+ }
+}
+

View File

@@ -1,359 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 16 Jan 2023 12:09:59 +0800
Subject: [PATCH] Hearse: Refactor some codes
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java
index e65b1eba68003a9f7ce5080d07a521817831ff48..5ffca8dac85fcbe50a4445ebc375b33d8228d690 100644
--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java
@@ -1,5 +1,7 @@
package co.earthme.hearse.concurrent;
+import co.earthme.hearse.concurrent.thread.WorkerThread;
+
public interface WorkerThreadFactory {
WorkerThread getNewThread(Runnable task);
}
diff --git a/src/main/java/co/earthme/hearse/concurrent/thread/Worker.java b/src/main/java/co/earthme/hearse/concurrent/thread/Worker.java
new file mode 100644
index 0000000000000000000000000000000000000000..e7a944bd515af644bad37a23e012a5a1997e110d
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/thread/Worker.java
@@ -0,0 +1,7 @@
+package co.earthme.hearse.concurrent.thread;
+
+public interface Worker {
+ static boolean isWorker(){
+ return Thread.currentThread() instanceof Worker;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/thread/WorkerThread.java
similarity index 50%
rename from src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
rename to src/main/java/co/earthme/hearse/concurrent/thread/WorkerThread.java
index 421d4926ac674b5eb12d9613ceb6d20185ea557d..f27bfd7ab3ce10e6c318de0c6376a80fa9014d2a 100644
--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
+++ b/src/main/java/co/earthme/hearse/concurrent/thread/WorkerThread.java
@@ -1,8 +1,8 @@
-package co.earthme.hearse.concurrent;
+package co.earthme.hearse.concurrent.thread;
import io.papermc.paper.util.TickThread;
-public class WorkerThread extends TickThread {
+public class WorkerThread extends TickThread implements Worker{
public WorkerThread(String name) {
super(name);
@@ -11,8 +11,4 @@ public class WorkerThread extends TickThread {
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/threadfactory/DefaultWorkerFactory.java b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
index 03a29509821a17faac2dc8ab810a2693b03bfbc6..b88b4f2d3df7303252a3c02824be3514c2618673 100644
--- a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
+++ b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
@@ -1,11 +1,10 @@
package co.earthme.hearse.concurrent.threadfactory;
-import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.thread.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;
diff --git a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
index 987c98ea108d49c1335238bc529f782d3ec5b5e6..13ab82cf7d5b17768d9a83e6f92511c6d5fa60f3 100644
--- a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
@@ -2,19 +2,15 @@ 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 co.earthme.hearse.util.ArrayListBlockingQueue;
-import net.minecraft.CrashReport;
-import net.minecraft.ReportedException;
+import io.netty.handler.codec.serialization.ObjectEncoder;
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.ArrayBlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
diff --git a/src/main/java/co/earthme/hearse/util/ArrayListBlockingQueue.java b/src/main/java/co/earthme/hearse/util/ArrayListBlockingQueue.java
index b2cea65ecfb8a41248e7ee74357b4127106f1d0a..ec63191dca67c51f387ede9796a039210c8c3f99 100644
--- a/src/main/java/co/earthme/hearse/util/ArrayListBlockingQueue.java
+++ b/src/main/java/co/earthme/hearse/util/ArrayListBlockingQueue.java
@@ -1,5 +1,7 @@
package co.earthme.hearse.util;
+import org.jetbrains.annotations.NotNull;
+
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
@@ -9,7 +11,7 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
private final StampedLock editLock = new StampedLock();
@Override
- public boolean add(T t) {
+ public boolean add(@NotNull T t) {
final long id = this.editLock.writeLock();
try {
return this.internalList.add(t);
@@ -19,7 +21,7 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
}
@Override
- public boolean offer(T t) {
+ public boolean offer(@NotNull T t) {
final long id = this.editLock.writeLock();
try {
return this.internalList.add(t);
@@ -75,7 +77,7 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
}
@Override
- public void put(T t) {
+ public void put(@NotNull T t) {
final long id = this.editLock.writeLock();
try {
this.internalList.add(t);
@@ -85,7 +87,7 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
}
@Override
- public boolean offer(T t, long timeout, TimeUnit unit) {
+ public boolean offer(T t, long timeout, @NotNull TimeUnit unit) {
final long id = this.editLock.writeLock();
try {
return this.internalList.add(t);
@@ -99,23 +101,24 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
T t;
while ((t = this.poll()) == null){
synchronized (this){
- this.wait(1);
+ this.wait(0,1);
}
}
return t;
}
@Override
- public T poll(long timeout, TimeUnit unit) throws InterruptedException {
+ public T poll(long timeout, @NotNull TimeUnit unit) throws InterruptedException {
T t;
+ long countTime = unit.toNanos(timeout);
while ((t = this.poll()) == null){
- if (timeout == 0){
+ if (countTime == 0){
break;
}
synchronized (this){
- unit.sleep(1);
+ this.wait(0,1);
}
- timeout--;
+ countTime--;
}
return t;
}
@@ -136,7 +139,7 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
}
@Override
- public boolean containsAll(Collection<?> c) {
+ public boolean containsAll(@NotNull Collection<?> c) {
final long id = this.editLock.writeLock();
try {
return new HashSet<>(this.internalList).containsAll(c);
@@ -146,7 +149,7 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
}
@Override
- public boolean addAll(Collection<? extends T> c) {
+ public boolean addAll(@NotNull Collection<? extends T> c) {
final long id = this.editLock.writeLock();
try {
return this.internalList.addAll(c);
@@ -156,7 +159,7 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
}
@Override
- public boolean removeAll(Collection<?> c) {
+ public boolean removeAll(@NotNull Collection<?> c) {
final long id = this.editLock.writeLock();
try {
return this.internalList.removeAll(c);
@@ -166,7 +169,7 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
}
@Override
- public boolean retainAll(Collection<?> c) {
+ public boolean retainAll(@NotNull Collection<?> c) {
final long id = this.editLock.writeLock();
try {
return this.internalList.retainAll(c);
@@ -231,7 +234,7 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
}
@Override
- public <T1> T1[] toArray(T1[] a) {
+ public <T1> T1[] toArray(T1 @NotNull [] a) {
long id = this.editLock.readLock();
try {
return this.internalList.toArray(a);
@@ -241,12 +244,12 @@ public class ArrayListBlockingQueue<T> implements BlockingQueue<T> {
}
@Override
- public int drainTo(Collection<? super T> c) {
+ public int drainTo(@NotNull Collection<? super T> c) {
throw new UnsupportedOperationException("drainTo");
}
@Override
- public int drainTo(Collection<? super T> c, int maxElements) {
+ public int drainTo(@NotNull Collection<? super T> c, int maxElements) {
throw new UnsupportedOperationException("drainTo");
}
}
diff --git a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
index 527dba288e1988773fd5a89f076f92084034f421..8cb0d00fb3cd4282873c8b8db88c87e59f8ef9de 100644
--- a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
+++ b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
@@ -4,6 +4,7 @@ import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
public class WorkerThreadPoolManager {
@@ -18,7 +19,7 @@ public class WorkerThreadPoolManager {
if (!worker.isShutdown()){
worker.getQueue().clear(); //Clear the tasks.We don't need wait them
worker.shutdown();
- while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {}
+ while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)); {}
}
}
}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 39c3aaf91514bd8a2f9f04496e25a6253442939f..20a4fbbc25b670ba89b428dd4b14725469c9bc96 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1,7 +1,7 @@
package net.minecraft.server.level;
import co.aikar.timings.TimingHistory;
-import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.thread.Worker;
import co.earthme.hearse.server.ServerEntityTickHook;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
@@ -223,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 && !WorkerThread.isWorker()) {
+ if (Thread.currentThread() != this.thread && !Worker.isWorker()) {
this.getChunkSource().mainThreadProcessor.execute(() -> {
this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad);
});
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 4a75c2895f4f5e3229be3c7b177445611a1e70b6..2f06b3782c4a21ed244b66dc146ebc71de8dac34 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -1,11 +1,8 @@
package net.minecraft.world.level;
-import co.aikar.timings.Timing;
-import co.aikar.timings.Timings;
-import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.thread.Worker;
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
import com.destroystokyo.paper.exception.ServerInternalException;
-import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mojang.serialization.Codec;
@@ -19,7 +16,6 @@ import java.util.function.Supplier;
import javax.annotation.Nullable;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
-import net.minecraft.ReportedException;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
@@ -37,15 +33,12 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
-import net.minecraft.util.AbortableIterationConsumer;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
-import net.minecraft.world.entity.boss.EnderDragonPart;
-import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
@@ -83,7 +76,6 @@ import net.minecraft.world.phys.Vec3;
import net.minecraft.world.scores.Scoreboard;
// CraftBukkit start
-import java.util.HashMap;
import java.util.Map;
import net.minecraft.network.protocol.game.ClientboundSetBorderCenterPacket;
import net.minecraft.network.protocol.game.ClientboundSetBorderLerpSizePacket;
@@ -91,17 +83,14 @@ import net.minecraft.network.protocol.game.ClientboundSetBorderSizePacket;
import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDelayPacket;
import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDistancePacket;
import org.bukkit.Bukkit;
-import org.bukkit.Location;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.CapturedBlockState;
import org.bukkit.craftbukkit.block.CraftBlockState;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.util.CraftSpawnCategory;
-import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.entity.SpawnCategory;
import org.bukkit.event.block.BlockPhysicsEvent;
-import org.bukkit.event.world.GenericGameEvent;
// CraftBukkit end
public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -1118,7 +1107,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() && !WorkerThread.isWorker() ? 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() && !Worker.isWorker() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system
}
public void setBlockEntity(BlockEntity blockEntity) {

View File

@@ -1,89 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 16 Jan 2023 14:17:32 +0800
Subject: [PATCH] Hearse: Fix a deadlock in EntityLookup
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
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 ec8982b89cde24eefd3d392eed3e6cc926053dff..14e87135513c0f5fc1e16beae3ea596f9c23a542 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
@@ -37,6 +37,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.UUID;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -52,7 +54,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public final ServerLevel world;
private final StampedLock entityByLock = new StampedLock();
- private final Object chunkEntitySlicesLock = new Object();
+ private final Lock chunkEntitySlicesLock = new ReentrantLock(true);
protected final Long2ObjectMap<ChunkSlicesRegion> regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f));
@@ -374,8 +376,11 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
throw new IllegalStateException("Only call Entity#setRemoved to remove an entity");
}
ChunkEntitySlices slices;
- synchronized (this.chunkEntitySlicesLock){
+ this.chunkEntitySlicesLock.lock();
+ try {
slices = this.getChunk(sectionX, sectionZ);
+ }finally {
+ this.chunkEntitySlicesLock.unlock();
}
// all entities should be in a chunk
if (slices == null) {
@@ -420,8 +425,11 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
TickThread.ensureTickThread(this.world, entity.sectionX, entity.sectionZ, "Cannot move entity off-main");
ChunkEntitySlices old;
- synchronized (this.chunkEntitySlicesLock){
+ this.chunkEntitySlicesLock.lock();
+ try {
old = this.getChunk(entity.sectionX, entity.sectionZ);
+ }finally {
+ this.chunkEntitySlicesLock.unlock();
}
final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
@@ -641,14 +649,17 @@ 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);
- synchronized (this.chunkEntitySlicesLock){
- if (curr != null) {
+ if (curr != null) {
+ this.chunkEntitySlicesLock.lock();
+ try {
this.removeChunk(chunkX, chunkZ);
curr.mergeInto(slices);
this.addChunk(chunkX, chunkZ, slices);
- } else {
- this.addChunk(chunkX, chunkZ, slices);
+ }finally {
+ this.chunkEntitySlicesLock.unlock();
}
+ } else {
+ this.addChunk(chunkX, chunkZ, slices);
}
}
}
@@ -656,9 +667,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public void entitySectionUnload(final int chunkX, final int chunkZ) {
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot unload entity section off-main");
- synchronized (this.chunkEntitySlicesLock){
- this.removeChunk(chunkX, chunkZ);
- }
+ this.removeChunk(chunkX, chunkZ);
}
public ChunkEntitySlices getChunk(final int chunkX, final int chunkZ) {

View File

@@ -1,24 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 16 Jan 2023 14:17:57 +0800
Subject: [PATCH] Hearse: ForkJoin worker support :)
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/co/earthme/hearse/concurrent/thread/WorkerForkJoinThread.java b/src/main/java/co/earthme/hearse/concurrent/thread/WorkerForkJoinThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a2fb0643b6fc5921b24674940c2c3b92b9e4e88
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/thread/WorkerForkJoinThread.java
@@ -0,0 +1,10 @@
+package co.earthme.hearse.concurrent.thread;
+
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinWorkerThread;
+
+public class WorkerForkJoinThread extends ForkJoinWorkerThread implements Worker {
+ protected WorkerForkJoinThread(ForkJoinPool pool) {
+ super(pool);
+ }
+}

View File

@@ -1,42 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 16 Jan 2023 18:13:53 +0800
Subject: [PATCH] Hearse: Use LinedBlockingQueue XD
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
index 8baccccee52b6e47bf51e51d976ad76920270ef4..c0d235e7227db0be6c6f753d8a6e13ad2716f798 100644
--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -41,7 +41,7 @@ public class ServerEntityTickHook {
workerCount,
0L,
TimeUnit.MILLISECONDS,
- new ArrayListBlockingQueue<>(),
+ new LinkedBlockingQueue<>(),
defFactory
);
Hearse.getWorkerManager().addWorker("entity",worker);
diff --git a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
index 13ab82cf7d5b17768d9a83e6f92511c6d5fa60f3..759b6dc9c719c6ff63348f9eacc760f8cef3163e 100644
--- a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
@@ -11,6 +11,7 @@ import net.minecraft.server.level.ServerLevel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
@@ -32,7 +33,7 @@ public class ServerLevelTickHook {
MinecraftServer.getServer().levels.size(),
Long.MAX_VALUE,
TimeUnit.MILLISECONDS,
- new ArrayListBlockingQueue<>(),
+ new ArrayBlockingQueue<>(MinecraftServer.getServer().levels.size()),
workerFactory
);
worker.allowCoreThreadTimeOut(true);

View File

@@ -1,55 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 16 Jan 2023 18:14:15 +0800
Subject: [PATCH] Hearse: I gave up to fix them
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index af5956bd57141cae08fe839bb2176988a48cd9b8..c26e10ee64b60e8c73a07f24446f439d22167fa5 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -2,7 +2,6 @@ package org.bukkit.craftbukkit.event;
import com.google.common.base.Function;
import com.google.common.base.Functions;
-import com.google.common.collect.Lists;
import com.mojang.datafixers.util.Either;
import java.net.InetAddress;
import java.util.ArrayList;
@@ -230,7 +229,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 volatile Entity entityDamage; // For use in EntityDamageByEntityEvent
+ public static Entity entityDamage; // For use in EntityDamageByEntityEvent
// helper methods
private static boolean canBuild(ServerLevel world, Player player, int x, int z) {
@@ -1049,7 +1048,8 @@ public class CraftEventFactory {
} else if (source == DamageSource.IN_FIRE) {
cause = DamageCause.FIRE;
} else {
- throw new IllegalStateException(String.format("Unhandled damage of %s by %s from %s", entity, damager, source.msgId));
+ cause = DamageCause.CUSTOM;
+ LogManager.getLogger().warn(String.format("Unhandled damage of %s by %s from %s", entity, damager, source.msgId));
}
EntityDamageEvent event = new EntityDamageByBlockEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions);
event.setCancelled(cancelled);
@@ -1126,11 +1126,12 @@ public class CraftEventFactory {
cause = DamageCause.CUSTOM;
}
- if (cause != null) {
- return CraftEventFactory.callEntityDamageEvent(null, entity, cause, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API
+ if (cause == null) {
+ cause = DamageCause.CUSTOM;
}
- throw new IllegalStateException(String.format("Unhandled damage of %s from %s", entity, source.msgId));
+ LogManager.getLogger().warn(String.format("Unhandled damage of %s from %s", entity, source.msgId));
+ return CraftEventFactory.callEntityDamageEvent(null, entity, cause, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API
}
@Deprecated // Paper - Add critical damage API

View File

@@ -1,38 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 16 Jan 2023 19:05:17 +0800
Subject: [PATCH] Hearse: Add entity position cache to fix concurrent problem-1
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/co/earthme/hearse/util/EntityPositionCache.java b/src/main/java/co/earthme/hearse/util/EntityPositionCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ff6ef85758c4f5780860178572e128080470d04
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/util/EntityPositionCache.java
@@ -0,0 +1,24 @@
+package co.earthme.hearse.util;
+
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.phys.Vec3;
+
+public class EntityPositionCache {
+ private final double x;
+ private final double y;
+ private final double z;
+
+ public EntityPositionCache(Entity entity){
+ this.x = entity.getX();
+ this.y = entity.getY();
+ this.z = entity.getZ();
+ }
+
+ public double distanceToSqr(Vec3 vector) {
+ double d0 = this.x - vector.x;
+ double d1 = this.y - vector.y;
+ double d2 = this.z - vector.z;
+
+ return d0 * d0 + d1 * d1 + d2 * d2;
+ }
+}

View File

@@ -1,152 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 16 Jan 2023 19:41:50 +0800
Subject: [PATCH] Hearse: Fix a sorting exception in NearestLivingEntitySensor
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/co/earthme/hearse/util/EntityPositionCache.java b/src/main/java/co/earthme/hearse/util/EntityPositionCache.java
index 0ff6ef85758c4f5780860178572e128080470d04..b7ec86d5cb2bb8d62d1a54f5e7b394e992a2b870 100644
--- a/src/main/java/co/earthme/hearse/util/EntityPositionCache.java
+++ b/src/main/java/co/earthme/hearse/util/EntityPositionCache.java
@@ -1,17 +1,48 @@
package co.earthme.hearse.util;
import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public class EntityPositionCache {
private final double x;
private final double y;
private final double z;
+ private final LivingEntity currentEntity;
- public EntityPositionCache(Entity entity){
+ public EntityPositionCache(LivingEntity entity){
this.x = entity.getX();
this.y = entity.getY();
this.z = entity.getZ();
+ this.currentEntity = entity;
+ }
+
+ public LivingEntity getCurrentEntity() {
+ return this.currentEntity;
+ }
+
+ public double getX() {
+ return this.x;
+ }
+
+ public double getY() {
+ return this.y;
+ }
+
+ public double getZ() {
+ return this.z;
+ }
+
+ public double distanceToSqr(double x, double y, double z) {
+ double d3 = this.x - x;
+ double d4 = this.y - y;
+ double d5 = this.z - z;
+
+ return d3 * d3 + d4 * d4 + d5 * d5;
+ }
+
+ public double distanceToSqr(Entity entity) {
+ return this.distanceToSqr(entity.position());
}
public double distanceToSqr(Vec3 vector) {
@@ -21,4 +52,8 @@ public class EntityPositionCache {
return d0 * d0 + d1 * d1 + d2 * d2;
}
+
+ public double distanceToSqr(EntityPositionCache entityPositionCache) {
+ return this.distanceToSqr(entityPositionCache.getX(),entityPositionCache.getY(),entityPositionCache.getZ());
+ }
}
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index c72920a17178059a29d21e96aeef398f6e0bbbdc..2658334ba2a22e808c747bbf81475ecb18a958d2 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -1,5 +1,6 @@
package net.minecraft.world.entity;
+import co.earthme.hearse.util.EntityPositionCache;
import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; // Paper
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
@@ -878,6 +879,10 @@ public abstract class LivingEntity extends Entity {
private boolean isTickingEffects = false;
private List<ProcessableEffect> effectsToProcess = Lists.newCopyOnWriteArrayList();
+ public double distanceToSqr(EntityPositionCache entityPositionCache) {
+ return this.distanceToSqr(entityPositionCache.getX(),entityPositionCache.getY(),entityPositionCache.getZ());
+ }
+
private static class ProcessableEffect {
private MobEffect type;
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
index d8cf99a3014a4b8152ae819fa663c2fdf34dce57..714cc41d6358ea57a23e2e663422fdba28efcf4d 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
@@ -1,10 +1,17 @@
package net.minecraft.world.entity.ai.sensing;
+import co.earthme.hearse.util.EntityPositionCache;
import com.google.common.collect.ImmutableSet;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
@@ -12,16 +19,28 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities;
import net.minecraft.world.phys.AABB;
public class NearestLivingEntitySensor<T extends LivingEntity> extends Sensor<T> {
+ private final List<EntityPositionCache> entitiesCache = ObjectLists.synchronize(new ObjectArrayList<>());
+ private final AtomicBoolean calling = new AtomicBoolean(false);
+
@Override
protected void doTick(ServerLevel world, T entity) {
- AABB aABB = entity.getBoundingBox().inflate((double)this.radiusXZ(), (double)this.radiusY(), (double)this.radiusXZ());
- List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, aABB, (e) -> {
- return e != entity && e.isAlive();
- });
- list.sort(Comparator.comparingDouble(entity::distanceToSqr));
- Brain<?> brain = entity.getBrain();
- brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, list);
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(entity, list));
+ if (!this.calling.get()){
+ this.calling.set(true);
+ try {
+ AABB aABB = entity.getBoundingBox().inflate(this.radiusXZ(), this.radiusY(), this.radiusXZ());
+ this.entitiesCache.clear();
+ this.entitiesCache.addAll(world.getEntitiesOfClass(LivingEntity.class, aABB, (e) -> e != entity && e.isAlive()).stream().map(EntityPositionCache::new).toList());
+ final EntityPositionCache compareCache = new EntityPositionCache(entity);
+ this.entitiesCache.sort(Comparator.comparingDouble(compareCache::distanceToSqr));
+
+ Brain<?> brain = entity.getBrain();
+ final List<LivingEntity> list = this.entitiesCache.stream().map(EntityPositionCache::getCurrentEntity).toList();
+ brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES,list);
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(entity, list));
+ }finally {
+ this.calling.set(false);
+ }
+ }
}
protected int radiusXZ() {

View File

@@ -1,25 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Mon, 16 Jan 2023 20:01:38 +0800
Subject: [PATCH] Hearse: I am a idiot
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index c26e10ee64b60e8c73a07f24446f439d22167fa5..cd972ab7048319de10c304419cd55a6920055218 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -1127,10 +1127,10 @@ public class CraftEventFactory {
}
if (cause == null) {
+ LogManager.getLogger().warn(String.format("Unhandled damage of %s from %s", entity, source.msgId));
cause = DamageCause.CUSTOM;
}
-
- LogManager.getLogger().warn(String.format("Unhandled damage of %s from %s", entity, source.msgId));
+
return CraftEventFactory.callEntityDamageEvent(null, entity, cause, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API
}

View File

@@ -1,249 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Tue, 17 Jan 2023 16:30:58 +0800
Subject: [PATCH] Hearse: Fix some NPE
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
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 14e87135513c0f5fc1e16beae3ea596f9c23a542..d183eb5ba934236469855b374904ef72a09b869f 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
@@ -37,9 +37,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.UUID;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.locks.StampedLock;
+import java.util.concurrent.locks.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -54,9 +52,10 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public final ServerLevel world;
private final StampedLock entityByLock = new StampedLock();
- private final Lock chunkEntitySlicesLock = new ReentrantLock(true);
+ private final Lock entityGetChunkLock = new ReentrantLock();
+ private final ReadWriteLock chunkEntitySlicesLock = new ReentrantReadWriteLock();
- protected final Long2ObjectMap<ChunkSlicesRegion> regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f));
+ protected final Long2ObjectMap<ChunkSlicesRegion> regions = new Long2ObjectOpenHashMap<>(128, 0.5f);
private final int minSection; // inclusive
private final int maxSection; // inclusive
@@ -136,7 +135,14 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
public String getDebugInfo() {
- return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + this.regions.size();
+ int reginSize;
+ this.chunkEntitySlicesLock.readLock().lock();
+ try {
+ reginSize = this.regions.size();
+ }finally {
+ this.chunkEntitySlicesLock.readLock().unlock();
+ }
+ return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + reginSize;
}
static final class ArrayIterable<T> implements Iterable<T> {
@@ -173,12 +179,12 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
@Override
- public synchronized boolean hasNext() {
+ public boolean hasNext() {
return this.off < this.length;
}
@Override
- public synchronized T next() {
+ public T next() {
if (this.off >= this.length) {
throw new NoSuchElementException();
}
@@ -186,7 +192,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
@Override
- public synchronized void remove() {
+ public void remove() {
throw new UnsupportedOperationException();
}
}
@@ -355,7 +361,15 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
entity.sectionX = sectionX;
entity.sectionY = sectionY;
entity.sectionZ = sectionZ;
- final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ);
+ ChunkEntitySlices slices;
+
+ this.entityGetChunkLock.lock();
+ try {
+ slices = this.getOrCreateChunk(sectionX, sectionZ);
+ }finally {
+ this.entityGetChunkLock.unlock();
+ }
+
if (!slices.addEntity(entity, sectionY)) {
LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")");
}
@@ -376,11 +390,11 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
throw new IllegalStateException("Only call Entity#setRemoved to remove an entity");
}
ChunkEntitySlices slices;
- this.chunkEntitySlicesLock.lock();
+ this.entityGetChunkLock.lock();
try {
slices = this.getChunk(sectionX, sectionZ);
}finally {
- this.chunkEntitySlicesLock.unlock();
+ this.entityGetChunkLock.unlock();
}
// all entities should be in a chunk
if (slices == null) {
@@ -425,13 +439,16 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
TickThread.ensureTickThread(this.world, entity.sectionX, entity.sectionZ, "Cannot move entity off-main");
ChunkEntitySlices old;
- this.chunkEntitySlicesLock.lock();
+ ChunkEntitySlices slices;
+
+ this.entityGetChunkLock.lock();
try {
old = this.getChunk(entity.sectionX, entity.sectionZ);
+ slices = this.getOrCreateChunk(newSectionX, newSectionZ);
}finally {
- this.chunkEntitySlicesLock.unlock();
+ this.entityGetChunkLock.unlock();
}
- final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
+
if (!old.removeEntity(entity, entity.sectionY)) {
LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section");
@@ -647,20 +664,18 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public void entitySectionLoad(final int chunkX, final int chunkZ, final ChunkEntitySlices slices) {
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot load in entity section off-main");
- synchronized (this) {
- final ChunkEntitySlices curr = this.getChunk(chunkX, chunkZ);
+ final ChunkEntitySlices curr = this.getChunk(chunkX, chunkZ);
+ this.chunkEntitySlicesLock.writeLock().lock();
+ try {
if (curr != null) {
- this.chunkEntitySlicesLock.lock();
- try {
- this.removeChunk(chunkX, chunkZ);
- curr.mergeInto(slices);
- this.addChunk(chunkX, chunkZ, slices);
- }finally {
- this.chunkEntitySlicesLock.unlock();
- }
+ this.removeChunk(chunkX, chunkZ);
+ curr.mergeInto(slices);
+ this.addChunk(chunkX, chunkZ, slices);
} else {
this.addChunk(chunkX, chunkZ, slices);
}
+ } finally {
+ this.chunkEntitySlicesLock.writeLock().unlock();
}
}
@@ -675,7 +690,6 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
if (region == null) {
return null;
}
-
return region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT));
}
@@ -692,32 +706,46 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public ChunkSlicesRegion getRegion(final int regionX, final int regionZ) {
final long key = CoordinateUtils.getChunkKey(regionX, regionZ);
- return this.regions.get(key);
+ this.chunkEntitySlicesLock.readLock().lock();
+ try {
+ return this.regions.get(key);
+ }finally {
+ this.chunkEntitySlicesLock.readLock().unlock();
+ }
}
- private synchronized void removeChunk(final int chunkX, final int chunkZ) {
+ private void removeChunk(final int chunkX, final int chunkZ) {
final long key = CoordinateUtils.getChunkKey(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
final int relIndex = (chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT);
- final ChunkSlicesRegion region = this.regions.get(key);
- final int remaining = region.remove(relIndex);
-
- if (remaining == 0) {
- this.regions.remove(key);
+ this.chunkEntitySlicesLock.writeLock().lock();
+ try {
+ final ChunkSlicesRegion region = this.regions.get(key);
+ final int remaining = region.remove(relIndex);
+ if (remaining == 0) {
+ this.regions.remove(key);
+ }
+ }finally {
+ this.chunkEntitySlicesLock.writeLock().unlock();
}
}
- public synchronized void addChunk(final int chunkX, final int chunkZ, final ChunkEntitySlices slices) {
+ public void addChunk(final int chunkX, final int chunkZ, final ChunkEntitySlices slices) {
final long key = CoordinateUtils.getChunkKey(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
final int relIndex = (chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT);
- ChunkSlicesRegion region = this.regions.get(key);
- if (region != null) {
- region.add(relIndex, slices);
- } else {
- region = new ChunkSlicesRegion();
- region.add(relIndex, slices);
- this.regions.put(key, region);
+ this.chunkEntitySlicesLock.writeLock().lock();
+ try {
+ ChunkSlicesRegion region = this.regions.get(key);
+ if (region != null) {
+ region.add(relIndex, slices);
+ } else {
+ region = new ChunkSlicesRegion();
+ region.add(relIndex, slices);
+ this.regions.put(key, region);
+ }
+ } finally {
+ this.chunkEntitySlicesLock.writeLock().unlock();
}
}
@@ -726,11 +754,11 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
protected final ChunkEntitySlices[] slices = new ChunkEntitySlices[REGION_SIZE * REGION_SIZE];
protected int sliceCount;
- public ChunkEntitySlices get(final int index) {
+ public synchronized ChunkEntitySlices get(final int index) {
return this.slices[index];
}
- public int remove(final int index) {
+ public synchronized int remove(final int index) {
final ChunkEntitySlices slices = this.slices[index];
if (slices == null) {
throw new IllegalStateException();
@@ -741,7 +769,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
return --this.sliceCount;
}
- public void add(final int index, final ChunkEntitySlices slices) {
+ public synchronized void add(final int index, final ChunkEntitySlices slices) {
final ChunkEntitySlices curr = this.slices[index];
if (curr != null) {
throw new IllegalStateException();

View File

@@ -1,285 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Tue, 17 Jan 2023 18:41:51 +0800
Subject: [PATCH] Hearse: Refactor paper's EntityLookup and fix a
IndexOutOfBoundsException
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
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 d183eb5ba934236469855b374904ef72a09b869f..09a85daaf8cb40b49fe264e999778b49160fe12a 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
@@ -2,42 +2,31 @@ package io.papermc.paper.chunk.system.entity;
import com.destroystokyo.paper.util.maplist.EntityList;
import com.mojang.logging.LogUtils;
+import io.papermc.paper.chunk.system.ChunkSystem;
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.Long2ObjectArrayMap;
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 it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
import net.minecraft.core.BlockPos;
-import io.papermc.paper.chunk.system.ChunkSystem;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.AbortableIterationConsumer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
-import net.minecraft.world.level.entity.EntityInLevelCallback;
-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.Visibility;
+import net.minecraft.world.level.entity.*;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.UUID;
-import java.util.concurrent.locks.*;
+
+import java.util.*;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -51,17 +40,14 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public final ServerLevel world;
- private final StampedLock entityByLock = new StampedLock();
- private final Lock entityGetChunkLock = new ReentrantLock();
private final ReadWriteLock chunkEntitySlicesLock = new ReentrantReadWriteLock();
- protected final Long2ObjectMap<ChunkSlicesRegion> regions = new Long2ObjectOpenHashMap<>(128, 0.5f);
+ protected final Long2ObjectMap<ChunkSlicesRegion> regions = new Long2ObjectArrayMap<>();
private final int minSection; // inclusive
private final int maxSection; // inclusive
private final LevelCallback<Entity> worldCallback;
- private final Int2ReferenceMap<Entity> entityById = new Int2ReferenceOpenHashMap<>();
- private final Object2ReferenceMap<UUID, Entity> entityByUUID = new Object2ReferenceOpenHashMap<>();
+ private final List<Entity> addedEntities = ObjectLists.synchronize(new ObjectArrayList<>());
private final EntityList accessibleEntities = new EntityList();
public EntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
@@ -82,52 +68,23 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
@Nullable
@Override
public Entity get(final int id) {
- final long attempt = this.entityByLock.tryOptimisticRead();
- if (attempt != 0L) {
- try {
- final Entity ret = this.entityById.get(id);
-
- if (this.entityByLock.validate(attempt)) {
- return maskNonAccessible(ret);
- }
- } catch (final Error error) {
- throw error;
- } catch (final Throwable thr) {
- // ignore
+ for (Entity entity : this.addedEntities){
+ if (id == entity.getId()){
+ return entity;
}
}
-
- this.entityByLock.readLock();
- try {
- return maskNonAccessible(this.entityById.get(id));
- } finally {
- this.entityByLock.tryUnlockRead();
- }
+ return null;
}
@Nullable
@Override
public Entity get(final UUID id) {
- final long attempt = this.entityByLock.tryOptimisticRead();
- if (attempt != 0L) {
- try {
- final Entity ret = this.entityByUUID.get(id);
- if (this.entityByLock.validate(attempt)) {
- return maskNonAccessible(ret);
- }
- } catch (final Error error) {
- throw error;
- } catch (final Throwable thr) {
- // ignore
+ for (Entity entity : this.addedEntities){
+ if (entity.getUUID().equals(id)){
+ return entity;
}
}
-
- this.entityByLock.readLock();
- try {
- return maskNonAccessible(this.entityByUUID.get(id));
- } finally {
- this.entityByLock.tryUnlockRead();
- }
+ return null;
}
public boolean hasEntity(final UUID uuid) {
@@ -142,7 +99,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}finally {
this.chunkEntitySlicesLock.readLock().unlock();
}
- return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + reginSize;
+ return "count:" + this.addedEntities.size() +",region_count:" + reginSize;
}
static final class ArrayIterable<T> implements Iterable<T> {
@@ -205,7 +162,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
@Override
public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AbortableIterationConsumer<U> action) {
- for (final Entity entity : this.entityById.values()) {
+ for (final Entity entity : this.addedEntities) {
final Visibility visibility = EntityLookup.getEntityStatus(entity);
if (!visibility.isAccessible()) {
continue;
@@ -342,33 +299,16 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
}
- this.entityByLock.writeLock();
- try {
- if (this.entityById.containsKey(entity.getId())) {
- LOGGER.warn("Entity id already exists: " + entity.getId() + ", mapped to " + this.entityById.get(entity.getId()) + ", can't add " + entity);
- return false;
- }
- if (this.entityByUUID.containsKey(entity.getUUID())) {
- LOGGER.warn("Entity uuid already exists: " + entity.getUUID() + ", mapped to " + this.entityByUUID.get(entity.getUUID()) + ", can't add " + entity);
+ if (this.addedEntities.contains(entity)) {
+ LOGGER.warn("Entity already exists: " + entity);
return false;
}
- this.entityById.put(entity.getId(), entity);
- this.entityByUUID.put(entity.getUUID(), entity);
- } finally {
- this.entityByLock.tryUnlockWrite();
- }
+ this.addedEntities.add(entity);
entity.sectionX = sectionX;
entity.sectionY = sectionY;
entity.sectionZ = sectionZ;
- ChunkEntitySlices slices;
-
- this.entityGetChunkLock.lock();
- try {
- slices = this.getOrCreateChunk(sectionX, sectionZ);
- }finally {
- this.entityGetChunkLock.unlock();
- }
+ ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ);
if (!slices.addEntity(entity, sectionY)) {
LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")");
@@ -389,13 +329,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
if (!entity.isRemoved()) {
throw new IllegalStateException("Only call Entity#setRemoved to remove an entity");
}
- ChunkEntitySlices slices;
- this.entityGetChunkLock.lock();
- try {
- slices = this.getChunk(sectionX, sectionZ);
- }finally {
- this.entityGetChunkLock.unlock();
- }
+ ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ);
// all entities should be in a chunk
if (slices == null) {
LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")");
@@ -406,17 +340,9 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
entity.sectionX = entity.sectionY = entity.sectionZ = Integer.MIN_VALUE;
- this.entityByLock.writeLock();
- try {
- if (!this.entityById.remove(entity.getId(), entity)) {
- LOGGER.warn("Failed to remove entity " + entity + " by id, current entity mapped: " + this.entityById.get(entity.getId()));
+ if (!this.addedEntities.remove(entity)) {
+ LOGGER.warn("Failed to remove entity " + entity);
}
- if (!this.entityByUUID.remove(entity.getUUID(), entity)) {
- LOGGER.warn("Failed to remove entity " + entity + " by uuid, current entity mapped: " + this.entityByUUID.get(entity.getUUID()));
- }
- } finally {
- this.entityByLock.tryUnlockWrite();
- }
}
private ChunkEntitySlices moveEntity(final Entity entity) {
@@ -438,17 +364,8 @@ 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");
- ChunkEntitySlices old;
- ChunkEntitySlices slices;
-
- this.entityGetChunkLock.lock();
- try {
- old = this.getChunk(entity.sectionX, entity.sectionZ);
- slices = this.getOrCreateChunk(newSectionX, newSectionZ);
- }finally {
- this.entityGetChunkLock.unlock();
- }
-
+ ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ);
+ ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
if (!old.removeEntity(entity, entity.sectionY)) {
LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section");
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 20a4fbbc25b670ba89b428dd4b14725469c9bc96..91eb39a7e23149bff16129f9f7622c44cfcb7afe 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -400,7 +400,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
// Paper end
// Paper start - optimise checkDespawn
- public final List<ServerPlayer> playersAffectingSpawning = new java.util.ArrayList<>();
+ public final List<ServerPlayer> playersAffectingSpawning = Lists.newCopyOnWriteArrayList();
// Paper end - optimise checkDespawn
// Paper start - optimise get nearest players for entity AI
@Override
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index 29fa9ad2223de668c15a5e5b433704b2c4765610..eea8ac88339c7ef58b8411a9c5357db8ae9f9289 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -831,7 +831,9 @@ public abstract class Mob extends LivingEntity {
// Paper start - optimise checkDespawn
Player entityhuman = this.level.findNearbyPlayer(this, level.paperConfig().entities.spawning.despawnRanges.get(this.getType().getCategory()).hard() + 1, EntitySelector.PLAYER_AFFECTS_SPAWNING); // Paper
if (entityhuman == null) {
- entityhuman = ((ServerLevel)this.level).playersAffectingSpawning.isEmpty() ? null : ((ServerLevel)this.level).playersAffectingSpawning.get(0);
+ synchronized (((ServerLevel) this.level).playersAffectingSpawning) {
+ entityhuman = ((ServerLevel) this.level).playersAffectingSpawning.isEmpty() ? null : ((ServerLevel) this.level).playersAffectingSpawning.get(0);
+ }
}
// Paper end - optimise checkDespawn

View File

@@ -1,26 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Tue, 17 Jan 2023 21:47:31 +0800
Subject: [PATCH] Lithium ai.sensor.secondary_poi
Original license: GPL v3
Original project: https://github.com/CaffeineMC/lithium-fabric
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java
index cb1d91f9fe98f21c2afbe3894dfd9bca3bdd3ba6..c6a92edd7e7077437236d2d119ea6b1436f16e50 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java
@@ -22,6 +22,13 @@ public class SecondaryPoiSensor extends Sensor<Villager> {
@Override
protected void doTick(ServerLevel world, Villager entity) {
+
+ //lithium: ai.sensor.secondary_poi
+ if (entity.getVillagerData().getProfession().secondaryPoi().isEmpty()) {
+ entity.getBrain().eraseMemory(MemoryModuleType.SECONDARY_JOB_SITE);
+ return;
+ }
+
ResourceKey<Level> resourceKey = world.dimension();
BlockPos blockPos = entity.blockPosition();
List<GlobalPos> list = Lists.newArrayList();

View File

@@ -1,77 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Tue, 17 Jan 2023 21:57:25 +0800
Subject: [PATCH] Lithium: collections.entity_filtering
Original license: GPL v3
Original project: https://github.com/CaffeineMC/lithium-fabric
diff --git a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java
index d5802cfe08f92b55ff1fd41648abda9ef2b7dd20..d6748f84e64bdafeadee38a4073583e6ea932bbe 100644
--- a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java
+++ b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java
@@ -28,7 +28,7 @@ public class ClassInstanceMultiMap<T> extends AbstractCollection<T> {
public boolean add(T object) {
boolean bl = false;
- for(Map.Entry<Class<?>, List<T>> entry : this.byClass.entrySet()) {
+ for (Map.Entry<Class<?>, List<T>> entry : this.byClass.entrySet()) {
if (entry.getKey().isInstance(object)) {
bl |= entry.getValue().add(object);
}
@@ -41,7 +41,7 @@ public class ClassInstanceMultiMap<T> extends AbstractCollection<T> {
public boolean remove(Object object) {
boolean bl = false;
- for(Map.Entry<Class<?>, List<T>> entry : this.byClass.entrySet()) {
+ for (Map.Entry<Class<?>, List<T>> entry : this.byClass.entrySet()) {
if (entry.getKey().isInstance(object)) {
List<T> list = entry.getValue();
bl |= list.remove(object);
@@ -57,23 +57,36 @@ public class ClassInstanceMultiMap<T> extends AbstractCollection<T> {
}
public <S> Collection<S> find(Class<S> type) {
- if (!this.baseClass.isAssignableFrom(type)) {
- throw new IllegalArgumentException("Don't know how to search for " + type);
- } else {
- List<? extends T> list = this.byClass.computeIfAbsent(type, (typeClass) -> {
- return this.allInstances.stream().filter(typeClass::isInstance).collect(toList());
- });
- return (Collection<S>) Collections.unmodifiableCollection(list);
+ // JettPack start
+ Collection<T> collection = this.byClass.get(type);
+
+ if (collection == null) {
+ collection = this.createAllOfType(type);
}
+
+ return (Collection<S>) Collections.unmodifiableCollection(collection);
+ // JettPack end
}
- public static <T> Collector<T, ?, List<T>> toList() {
- return Collectors.toCollection(CopyOnWriteArrayList::new);
+ // JettPack start
+ private <S> Collection<T> createAllOfType(Class<S> type) {
+ List<T> list = new java.util.ArrayList<>();
+
+ for (T allElement : this.allInstances) {
+ if (type.isInstance(allElement)) {
+ list.add(allElement);
+ }
+ }
+
+ this.byClass.put(type, list);
+
+ return list;
}
+ // JettPack end
@Override
public Iterator<T> iterator() {
- return (Iterator<T>)(this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator()));
+ return (Iterator<T>) (this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator()));
}
public List<T> getAllInstances() {

View File

@@ -1,272 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 18 Jan 2023 14:39:00 +0800
Subject: [PATCH] Hearse: Multithreaded tracker
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index dfb747eba6bf7088af0ff400da169de00a076365..0f18f7ce182e35ee6f3f385553e1bdb5ee4ad587 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1,5 +1,7 @@
package net.minecraft.server.level;
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
import com.google.common.collect.*;
import com.google.common.collect.ImmutableList.Builder;
import com.google.gson.JsonElement;
@@ -13,10 +15,7 @@ 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.*;
-import it.unimi.dsi.fastutil.objects.ObjectIterator;
-import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
-import it.unimi.dsi.fastutil.objects.ReferenceSet;
-import it.unimi.dsi.fastutil.objects.ReferenceSets;
+import it.unimi.dsi.fastutil.objects.*;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
@@ -61,10 +60,7 @@ import java.io.IOException;
import java.io.Writer;
import java.nio.file.Path;
import java.util.*;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionException;
-import java.util.concurrent.Executor;
+import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*;
@@ -1194,78 +1190,64 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.tracker = null; // Paper - We're no longer tracked
}
+ private final Executor asyncTrackWorker = new WorkerThreadPoolExecutor(
+ 0,
+ 2,
+ 5L,
+ TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(),
+ new DefaultWorkerFactory("tracker-async"));
+ private final Executor concurrentTrackWorker = new WorkerThreadPoolExecutor(
+ Runtime.getRuntime().availableProcessors(),
+ Runtime.getRuntime().availableProcessors(),
+ 5L,
+ TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(),
+ new DefaultWorkerFactory("tracker-concurrent"));
+
+ private final AtomicInteger totalRunning = new AtomicInteger();
+
// Paper start - optimised tracker
private final void processTrackQueue() {
- //this.level.timings.tracker1.startTiming(); // Purpur
- try {
- for (TrackedEntity tracker : this.entityMap.values()) {
- // update tracker entry
- tracker.updatePlayers(tracker.entity.getPlayersInTrackRange());
- }
- } finally {
- //this.level.timings.tracker1.stopTiming(); // Purpur
+ if(this.totalRunning.get() > 0){
+ return;
}
+ this.totalRunning.set(2);
+
+ this.asyncTrackWorker.execute(()->{
+ //this.level.timings.tracker1.startTiming(); // Purpur
+ try {
+ CompletableFuture.allOf(this.entityMap.values()
+ .stream()
+ .map(tracker -> CompletableFuture.runAsync(()->{
+ tracker.updatePlayers(tracker.entity.getPlayersInTrackRange());
+ },this.concurrentTrackWorker))
+ .toArray(CompletableFuture[]::new)).join();
+ } finally {
+ //this.level.timings.tracker1.stopTiming(); // Purpur
+ this.totalRunning.getAndDecrement();
+ }
+ });
- //this.level.timings.tracker2.startTiming(); // Purpur
- try {
- for (TrackedEntity tracker : this.entityMap.values()) {
- tracker.serverEntity.sendChanges();
+ this.asyncTrackWorker.execute(()->{
+ //this.level.timings.tracker2.startTiming(); // Purpur
+ try {
+ for (TrackedEntity tracker : this.entityMap.values()) {
+ tracker.serverEntity.sendChanges();
+ }
+ } finally {
+ //this.level.timings.tracker2.stopTiming(); // Purpur
+ this.totalRunning.getAndDecrement();
}
- } finally {
- //this.level.timings.tracker2.stopTiming(); // Purpur
- }
+ });
}
// Paper end - optimised tracker
protected void tick() {
// Paper start - optimized tracker
- if (true) {
- this.processTrackQueue();
- return;
- }
+ this.processTrackQueue();
// Paper end - optimized tracker
- List<ServerPlayer> list = Lists.newArrayList();
- List<ServerPlayer> list1 = this.level.players();
- Iterator objectiterator = this.entityMap.values().iterator();
- //level.timings.tracker1.startTiming(); // Paper // Purpur
-
- ChunkMap.TrackedEntity playerchunkmap_entitytracker;
-
- while (objectiterator.hasNext()) {
- playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
- SectionPos sectionposition = playerchunkmap_entitytracker.lastSectionPos;
- SectionPos sectionposition1 = SectionPos.of((EntityAccess) playerchunkmap_entitytracker.entity);
- boolean flag = !Objects.equals(sectionposition, sectionposition1);
-
- if (flag) {
- playerchunkmap_entitytracker.updatePlayers(list1);
- Entity entity = playerchunkmap_entitytracker.entity;
-
- if (entity instanceof ServerPlayer) {
- list.add((ServerPlayer) entity);
- }
-
- playerchunkmap_entitytracker.lastSectionPos = sectionposition1;
- }
-
- if (flag || this.distanceManager.inEntityTickingRange(sectionposition1.chunk().toLong())) {
- playerchunkmap_entitytracker.serverEntity.sendChanges();
- }
- }
- //level.timings.tracker1.stopTiming(); // Paper // Purpur
-
- if (!list.isEmpty()) {
- objectiterator = this.entityMap.values().iterator();
-
- //level.timings.tracker2.startTiming(); // Paper // Purpur
- while (objectiterator.hasNext()) {
- playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
- playerchunkmap_entitytracker.updatePlayers(list);
- }
- //level.timings.tracker2.stopTiming(); // Paper // Purpur
- }
-
}
public void broadcast(Entity entity, Packet<?> packet) {
@@ -1446,7 +1428,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final Entity entity;
private final int range;
SectionPos lastSectionPos;
- public final Set<ServerPlayerConnection> seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl
+ public final Set<ServerPlayerConnection> seenBy = ReferenceSets.synchronize(new ReferenceOpenHashSet<>()); // Paper - optimise map impl //Hearse - multithread tracker
public TrackedEntity(Entity entity, int i, int j, boolean flag) {
this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit
@@ -1464,12 +1446,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (newTrackerCandidates != null) {
Object[] rawData = newTrackerCandidates.getBackingSet();
- for (int i = 0, len = rawData.length; i < len; ++i) {
- Object raw = rawData[i];
+ for (Object raw : rawData) {
if (!(raw instanceof ServerPlayer)) {
continue;
}
- ServerPlayer player = (ServerPlayer)raw;
+ ServerPlayer player = (ServerPlayer) raw;
this.updatePlayer(player);
}
}
@@ -1500,14 +1481,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void broadcast(Packet<?> packet) {
- Iterator iterator = this.seenBy.iterator();
-
- while (iterator.hasNext()) {
- ServerPlayerConnection serverplayerconnection = (ServerPlayerConnection) iterator.next();
-
+ for (ServerPlayerConnection serverplayerconnection : this.seenBy) {
serverplayerconnection.send(packet);
}
-
}
public void broadcastAndSend(Packet<?> packet) {
@@ -1519,14 +1495,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void broadcastRemoved() {
- Iterator iterator = this.seenBy.iterator();
-
- while (iterator.hasNext()) {
- ServerPlayerConnection serverplayerconnection = (ServerPlayerConnection) iterator.next();
-
+ for (ServerPlayerConnection serverplayerconnection : this.seenBy) {
this.serverEntity.removePairing(serverplayerconnection.getPlayer());
}
-
}
public void removePlayer(ServerPlayer player) {
@@ -1534,7 +1505,6 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
}
-
}
public void updatePlayer(ServerPlayer player) {
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
index 50cf4d200bc2892f2140c9929193b4b20ad2bd17..afccc8e6d6a6f8d324a58570c7c2245544516e7b 100644
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
@@ -3,14 +3,12 @@ package net.minecraft.server.level;
import com.google.common.collect.Lists;
import com.mojang.datafixers.util.Pair;
import com.mojang.logging.LogUtils;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+
+import java.util.*;
import java.util.function.Consumer;
import javax.annotation.Nullable;
+
+import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket;
@@ -373,7 +371,7 @@ public class ServerEntity {
}
if (this.entity instanceof LivingEntity) {
- Set<AttributeInstance> set = ((LivingEntity) this.entity).getAttributes().getDirtyAttributes();
+ List<AttributeInstance> set = new ArrayList<>(((LivingEntity) this.entity).getAttributes().getDirtyAttributes());
if (!set.isEmpty()) {
// CraftBukkit start - Send scaled max health
@@ -394,6 +392,5 @@ public class ServerEntity {
if (this.entity instanceof ServerPlayer) {
((ServerPlayer) this.entity).connection.send(packet);
}
-
}
}

View File

@@ -1,104 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 18 Jan 2023 14:39:17 +0800
Subject: [PATCH] Hearse: Some concurrent fixes
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
index 097007c1c25ba55d9916fc820dd1d1149d81f6f4..b15b47041ad891deca9ff9b3bc6d196598f27a68 100644
--- a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
+++ b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
@@ -30,7 +30,7 @@ import org.slf4j.Logger;
public class GossipContainer {
private static final Logger LOGGER = LogUtils.getLogger();
public static final int DISCARD_THRESHOLD = 2;
- public final Map<UUID, GossipContainer.EntityGossips> gossips = Maps.newHashMap();
+ public final Map<UUID, GossipContainer.EntityGossips> gossips = Maps.newConcurrentMap();
@VisibleForDebug
public Map<UUID, Object2IntMap<GossipType>> getGossipEntries() {
diff --git a/src/main/java/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java b/src/main/java/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java
index d59857e9db945d5b659153e55dafa2d91c388458..3231a17627ab0d88d4a83371033dfd228c5169bc 100644
--- a/src/main/java/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java
+++ b/src/main/java/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java
@@ -1,10 +1,12 @@
package net.minecraft.world.entity.ai.memory;
-import com.google.common.collect.Iterables;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMaps;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.sensing.Sensor;
@@ -16,20 +18,14 @@ public class NearestVisibleLivingEntities {
private NearestVisibleLivingEntities() {
this.nearbyEntities = List.of();
- this.lineOfSightTest = (entity) -> {
- return false;
- };
+ this.lineOfSightTest = (entity) -> false;
}
public NearestVisibleLivingEntities(LivingEntity owner, List<LivingEntity> entities) {
this.nearbyEntities = entities;
- Object2BooleanOpenHashMap<LivingEntity> object2BooleanOpenHashMap = new Object2BooleanOpenHashMap<>(entities.size());
- Predicate<LivingEntity> predicate = (entity) -> {
- return Sensor.isEntityTargetable(owner, entity);
- };
- this.lineOfSightTest = (entity) -> {
- return object2BooleanOpenHashMap.computeIfAbsent(entity, predicate);
- };
+ Object2BooleanMap<LivingEntity> object2BooleanOpenHashMap = Object2BooleanMaps.synchronize(new Object2BooleanOpenHashMap<>(entities.size()));
+ Predicate<LivingEntity> predicate = (entity) -> Sensor.isEntityTargetable(owner, entity);
+ this.lineOfSightTest = (entity) -> object2BooleanOpenHashMap.computeIfAbsent(entity, predicate);
}
public static NearestVisibleLivingEntities empty() {
@@ -47,15 +43,11 @@ public class NearestVisibleLivingEntities {
}
public Iterable<LivingEntity> findAll(Predicate<LivingEntity> predicate) {
- return Iterables.filter(this.nearbyEntities, (entity) -> {
- return predicate.test(entity) && this.lineOfSightTest.test(entity);
- });
+ return this.nearbyEntities.stream().filter((entity) -> predicate.test(entity) && this.lineOfSightTest.test(entity)).collect(Collectors.toList());
}
public Stream<LivingEntity> find(Predicate<LivingEntity> predicate) {
- return this.nearbyEntities.stream().filter((entity) -> {
- return predicate.test(entity) && this.lineOfSightTest.test(entity);
- });
+ return this.nearbyEntities.stream().filter((entity) -> predicate.test(entity) && this.lineOfSightTest.test(entity));
}
public boolean contains(LivingEntity entity) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
index 714cc41d6358ea57a23e2e663422fdba28efcf4d..0d0ca5d29f32286cb0609a1570a64731380f270c 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
@@ -8,6 +8,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectLists;
import net.minecraft.server.level.ServerLevel;
@@ -34,7 +35,8 @@ public class NearestLivingEntitySensor<T extends LivingEntity> extends Sensor<T>
this.entitiesCache.sort(Comparator.comparingDouble(compareCache::distanceToSqr));
Brain<?> brain = entity.getBrain();
- final List<LivingEntity> list = this.entitiesCache.stream().map(EntityPositionCache::getCurrentEntity).toList();
+ final List<LivingEntity> list = Lists.newCopyOnWriteArrayList();
+ list.addAll(this.entitiesCache.stream().map(EntityPositionCache::getCurrentEntity).toList());
brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES,list);
brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(entity, list));
}finally {

View File

@@ -1,399 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 18 Jan 2023 16:57:32 +0800
Subject: [PATCH] Hearse: Use old refacted EntityLookup
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
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 09a85daaf8cb40b49fe264e999778b49160fe12a..387d07868301877dd7fca5d8dfd21e1331f4793e 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
@@ -2,31 +2,45 @@ package io.papermc.paper.chunk.system.entity;
import com.destroystokyo.paper.util.maplist.EntityList;
import com.mojang.logging.LogUtils;
-import io.papermc.paper.chunk.system.ChunkSystem;
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.Int2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectArrayMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
-import it.unimi.dsi.fastutil.objects.ObjectArrayList;
-import it.unimi.dsi.fastutil.objects.ObjectLists;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import net.minecraft.core.BlockPos;
+import io.papermc.paper.chunk.system.ChunkSystem;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.AbortableIterationConsumer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
-import net.minecraft.world.level.entity.*;
+import net.minecraft.world.level.entity.EntityInLevelCallback;
+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.Visibility;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
-
-import java.util.*;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.StampedLock;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -40,14 +54,16 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public final ServerLevel world;
- private final ReadWriteLock chunkEntitySlicesLock = new ReentrantReadWriteLock();
+ private final StampedLock entityByLock = new StampedLock();
+ private final Lock regionLoadLock = new ReentrantLock(true);
- protected final Long2ObjectMap<ChunkSlicesRegion> regions = new Long2ObjectArrayMap<>();
+ protected final Long2ObjectMap<ChunkSlicesRegion> regions = Long2ObjectMaps.synchronize(new Long2ObjectArrayMap<>());
private final int minSection; // inclusive
private final int maxSection; // inclusive
private final LevelCallback<Entity> worldCallback;
- private final List<Entity> addedEntities = ObjectLists.synchronize(new ObjectArrayList<>());
+ private final Int2ReferenceMap<Entity> entityById = new Int2ReferenceArrayMap<>();
+ private final Object2ReferenceMap<UUID, Entity> entityByUUID = new Object2ReferenceArrayMap<>();
private final EntityList accessibleEntities = new EntityList();
public EntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
@@ -68,23 +84,52 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
@Nullable
@Override
public Entity get(final int id) {
- for (Entity entity : this.addedEntities){
- if (id == entity.getId()){
- return entity;
+ final long attempt = this.entityByLock.tryOptimisticRead();
+ if (attempt != 0L) {
+ try {
+ final Entity ret = this.entityById.get(id);
+
+ if (this.entityByLock.validate(attempt)) {
+ return maskNonAccessible(ret);
+ }
+ } catch (final Error error) {
+ throw error;
+ } catch (final Throwable thr) {
+ // ignore
}
}
- return null;
+
+ this.entityByLock.readLock();
+ try {
+ return maskNonAccessible(this.entityById.get(id));
+ } finally {
+ this.entityByLock.tryUnlockRead();
+ }
}
@Nullable
@Override
public Entity get(final UUID id) {
- for (Entity entity : this.addedEntities){
- if (entity.getUUID().equals(id)){
- return entity;
+ final long attempt = this.entityByLock.tryOptimisticRead();
+ if (attempt != 0L) {
+ try {
+ final Entity ret = this.entityByUUID.get(id);
+ if (this.entityByLock.validate(attempt)) {
+ return maskNonAccessible(ret);
+ }
+ } catch (final Error error) {
+ throw error;
+ } catch (final Throwable thr) {
+ // ignore
}
}
- return null;
+
+ this.entityByLock.readLock();
+ try {
+ return maskNonAccessible(this.entityByUUID.get(id));
+ } finally {
+ this.entityByLock.tryUnlockRead();
+ }
}
public boolean hasEntity(final UUID uuid) {
@@ -92,14 +137,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
public String getDebugInfo() {
- int reginSize;
- this.chunkEntitySlicesLock.readLock().lock();
- try {
- reginSize = this.regions.size();
- }finally {
- this.chunkEntitySlicesLock.readLock().unlock();
- }
- return "count:" + this.addedEntities.size() +",region_count:" + reginSize;
+ return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + this.regions.size();
}
static final class ArrayIterable<T> implements Iterable<T> {
@@ -136,12 +174,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();
}
@@ -162,7 +200,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
@Override
public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AbortableIterationConsumer<U> action) {
- for (final Entity entity : this.addedEntities) {
+ for (final Entity entity : this.entityById.values()) {
final Visibility visibility = EntityLookup.getEntityStatus(entity);
if (!visibility.isAccessible()) {
continue;
@@ -299,17 +337,26 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
}
- if (this.addedEntities.contains(entity)) {
- LOGGER.warn("Entity already exists: " + entity);
+ this.entityByLock.writeLock();
+ try {
+ if (this.entityById.containsKey(entity.getId())) {
+ LOGGER.warn("Entity id already exists: " + entity.getId() + ", mapped to " + this.entityById.get(entity.getId()) + ", can't add " + entity);
return false;
}
- this.addedEntities.add(entity);
+ if (this.entityByUUID.containsKey(entity.getUUID())) {
+ LOGGER.warn("Entity uuid already exists: " + entity.getUUID() + ", mapped to " + this.entityByUUID.get(entity.getUUID()) + ", can't add " + entity);
+ return false;
+ }
+ this.entityById.put(entity.getId(), entity);
+ this.entityByUUID.put(entity.getUUID(), entity);
+ } finally {
+ this.entityByLock.tryUnlockWrite();
+ }
entity.sectionX = sectionX;
entity.sectionY = sectionY;
entity.sectionZ = sectionZ;
- ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ);
-
+ final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ);
if (!slices.addEntity(entity, sectionY)) {
LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")");
}
@@ -329,7 +376,14 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
if (!entity.isRemoved()) {
throw new IllegalStateException("Only call Entity#setRemoved to remove an entity");
}
- ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ);
+ ChunkEntitySlices slices;
+ this.regionLoadLock.lock();
+ try {
+ slices = this.getChunk(sectionX, sectionZ);
+ }finally {
+ this.regionLoadLock.unlock();
+ }
+
// all entities should be in a chunk
if (slices == null) {
LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")");
@@ -340,9 +394,17 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
entity.sectionX = entity.sectionY = entity.sectionZ = Integer.MIN_VALUE;
- if (!this.addedEntities.remove(entity)) {
- LOGGER.warn("Failed to remove entity " + entity);
+ this.entityByLock.writeLock();
+ try {
+ if (!this.entityById.remove(entity.getId(), entity)) {
+ LOGGER.warn("Failed to remove entity " + entity + " by id, current entity mapped: " + this.entityById.get(entity.getId()));
+ }
+ if (!this.entityByUUID.remove(entity.getUUID(), entity)) {
+ LOGGER.warn("Failed to remove entity " + entity + " by uuid, current entity mapped: " + this.entityByUUID.get(entity.getUUID()));
}
+ } finally {
+ this.entityByLock.tryUnlockWrite();
+ }
}
private ChunkEntitySlices moveEntity(final Entity entity) {
@@ -364,8 +426,16 @@ 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");
- ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ);
- ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
+ ChunkEntitySlices old;
+
+ this.regionLoadLock.lock();
+ try {
+ old = this.getChunk(entity.sectionX, entity.sectionZ);
+ }finally {
+ this.regionLoadLock.unlock();
+ }
+
+ final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
if (!old.removeEntity(entity, entity.sectionY)) {
LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section");
@@ -581,18 +651,20 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public void entitySectionLoad(final int chunkX, final int chunkZ, final ChunkEntitySlices slices) {
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot load in entity section off-main");
- final ChunkEntitySlices curr = this.getChunk(chunkX, chunkZ);
- this.chunkEntitySlicesLock.writeLock().lock();
- try {
- if (curr != null) {
- this.removeChunk(chunkX, chunkZ);
- curr.mergeInto(slices);
- this.addChunk(chunkX, chunkZ, slices);
- } else {
- this.addChunk(chunkX, chunkZ, slices);
+ synchronized (this) {
+ final ChunkEntitySlices curr = this.getChunk(chunkX, chunkZ);
+ this.regionLoadLock.lock();
+ try {
+ if (curr != null) {
+ this.removeChunk(chunkX, chunkZ);
+ curr.mergeInto(slices);
+ this.addChunk(chunkX, chunkZ, slices);
+ } else {
+ this.addChunk(chunkX, chunkZ, slices);
+ }
+ } finally {
+ this.regionLoadLock.unlock();
}
- } finally {
- this.chunkEntitySlicesLock.writeLock().unlock();
}
}
@@ -607,6 +679,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
if (region == null) {
return null;
}
+
return region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT));
}
@@ -623,46 +696,32 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public ChunkSlicesRegion getRegion(final int regionX, final int regionZ) {
final long key = CoordinateUtils.getChunkKey(regionX, regionZ);
- this.chunkEntitySlicesLock.readLock().lock();
- try {
- return this.regions.get(key);
- }finally {
- this.chunkEntitySlicesLock.readLock().unlock();
- }
+ return this.regions.get(key);
}
- private void removeChunk(final int chunkX, final int chunkZ) {
+ private synchronized void removeChunk(final int chunkX, final int chunkZ) {
final long key = CoordinateUtils.getChunkKey(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
final int relIndex = (chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT);
- this.chunkEntitySlicesLock.writeLock().lock();
- try {
- final ChunkSlicesRegion region = this.regions.get(key);
- final int remaining = region.remove(relIndex);
- if (remaining == 0) {
- this.regions.remove(key);
- }
- }finally {
- this.chunkEntitySlicesLock.writeLock().unlock();
+ final ChunkSlicesRegion region = this.regions.get(key);
+ final int remaining = region.remove(relIndex);
+
+ if (remaining == 0) {
+ this.regions.remove(key);
}
}
- public void addChunk(final int chunkX, final int chunkZ, final ChunkEntitySlices slices) {
+ public synchronized void addChunk(final int chunkX, final int chunkZ, final ChunkEntitySlices slices) {
final long key = CoordinateUtils.getChunkKey(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
final int relIndex = (chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT);
- this.chunkEntitySlicesLock.writeLock().lock();
- try {
- ChunkSlicesRegion region = this.regions.get(key);
- if (region != null) {
- region.add(relIndex, slices);
- } else {
- region = new ChunkSlicesRegion();
- region.add(relIndex, slices);
- this.regions.put(key, region);
- }
- } finally {
- this.chunkEntitySlicesLock.writeLock().unlock();
+ ChunkSlicesRegion region = this.regions.get(key);
+ if (region != null) {
+ region.add(relIndex, slices);
+ } else {
+ region = new ChunkSlicesRegion();
+ region.add(relIndex, slices);
+ this.regions.put(key, region);
}
}
@@ -671,11 +730,11 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
protected final ChunkEntitySlices[] slices = new ChunkEntitySlices[REGION_SIZE * REGION_SIZE];
protected int sliceCount;
- public synchronized ChunkEntitySlices get(final int index) {
+ public ChunkEntitySlices get(final int index) {
return this.slices[index];
}
- public synchronized int remove(final int index) {
+ public int remove(final int index) {
final ChunkEntitySlices slices = this.slices[index];
if (slices == null) {
throw new IllegalStateException();
@@ -686,7 +745,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
return --this.sliceCount;
}
- public synchronized void add(final int index, final ChunkEntitySlices slices) {
+ public void add(final int index, final ChunkEntitySlices slices) {
final ChunkEntitySlices curr = this.slices[index];
if (curr != null) {
throw new IllegalStateException();

View File

@@ -1,134 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 18 Jan 2023 19:58:12 +0800
Subject: [PATCH] Hearse: Fix some sort problems in some sensors
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
index 2c4517850a9692f1c2b1332b58e8312fe1166772..bee1aa7eded53b3302f39053bfd9c4af5f3008c3 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
@@ -27,12 +27,11 @@ public class NearestItemSensor extends Sensor<Mob> {
List<ItemEntity> list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0D, 16.0D, 32.0D), (itemEntity) -> {
return itemEntity.closerThan(entity, 9.0D) && entity.wantsToPickUp(itemEntity.getItem()); // Paper - move predicate into getEntities
});
- list.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2))); // better to take the sort perf hit than using line of sight more than we need to.
+ list.sort(Comparator.comparingDouble(entity::distanceToSqr)); // better to take the sort perf hit than using line of sight more than we need to.
// Paper start - remove streams
// Paper start - remove streams in favour of lists
ItemEntity nearest = null;
- for (int i = 0; i < list.size(); i++) {
- ItemEntity entityItem = list.get(i);
+ for (ItemEntity entityItem : list) {
if (entity.hasLineOfSight(entityItem)) {
// Paper end - remove streams
nearest = entityItem;
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
index 75d9c4f011b5a97def215784c92bb57bbb35d06b..af6dcbd8f531705c356780cc79aa1868a10cfaf9 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
@@ -1,11 +1,14 @@
package net.minecraft.world.entity.ai.sensing;
+import co.earthme.hearse.util.EntityPositionCache;
import com.google.common.collect.ImmutableSet;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
+
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.LivingEntity;
@@ -14,6 +17,9 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.player.Player;
public class PlayerSensor extends Sensor<LivingEntity> {
+ private final List<Player> playerList = ObjectLists.synchronize(new ObjectArrayList<>());
+ private final AtomicBoolean calling = new AtomicBoolean();
+
@Override
public Set<MemoryModuleType<?>> requires() {
return ImmutableSet.of(MemoryModuleType.NEAREST_PLAYERS, MemoryModuleType.NEAREST_VISIBLE_PLAYER, MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER);
@@ -21,30 +27,51 @@ public class PlayerSensor extends Sensor<LivingEntity> {
@Override
protected void doTick(ServerLevel world, LivingEntity entity) {
- // Paper start - remove streams
- List<Player> players = (List)world.getNearbyPlayers(entity, entity.getX(), entity.getY(), entity.getZ(), 16.0D, EntitySelector.NO_SPECTATORS);
- players.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2)));
- Brain<?> brain = entity.getBrain();
-
- brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, players);
-
- Player firstTargetable = null;
- Player firstAttackable = null;
- for (int index = 0, len = players.size(); index < len; ++index) {
- Player player = players.get(index);
- if (firstTargetable == null && isEntityTargetable(entity, player)) {
- firstTargetable = player;
- }
- if (firstAttackable == null && isEntityAttackable(entity, player)) {
- firstAttackable = player;
- }
+ if (this.calling.get()){
+ return;
+ }
+
+ this.calling.set(true);
+ try {
+ // Paper start - remove streams
+ List<EntityPositionCache> playersPosCaches = new ArrayList<>(List.of(world
+ .getNearbyPlayers(entity, entity.getX(), entity.getY(), entity.getZ(), 16.0D, EntitySelector.NO_SPECTATORS)
+ .stream()
+ .map(EntityPositionCache::new)
+ .toArray(EntityPositionCache[]::new)));
+
+ final EntityPositionCache entityPositionCache = new EntityPositionCache(entity);
+
+ playersPosCaches.sort(Comparator.comparingDouble(entityPositionCache::distanceToSqr));
+
+ final List<Player> players = playersPosCaches
+ .stream()
+ .map(cache -> ((Player) cache.getCurrentEntity()))
+ .toList();
+
+ Brain<?> brain = entity.getBrain();
+
+ brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, players);
+
+ Player firstTargetable = null;
+ Player firstAttackable = null;
+ for (Player player : players) {
+ if (firstTargetable == null && isEntityTargetable(entity, player)) {
+ firstTargetable = player;
+ }
+ if (firstAttackable == null && isEntityAttackable(entity, player)) {
+ firstAttackable = player;
+ }
- if (firstAttackable != null && firstTargetable != null) {
- break;
+ if (firstAttackable != null && firstTargetable != null) {
+ break;
+ }
}
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, firstTargetable);
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, Optional.ofNullable(firstAttackable));
+ // Paper end - remove streams
+ }finally {
+ this.calling.set(false);
}
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, firstTargetable);
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, Optional.ofNullable(firstAttackable));
- // Paper end - remove streams
}
}

View File

@@ -1,89 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 18 Jan 2023 20:08:55 +0800
Subject: [PATCH] Hearse: Add config for multithreaded tracker
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0f18f7ce182e35ee6f3f385553e1bdb5ee4ad587..cf97584215e4b0fcb3b4b92942eaf1f6c8682f54 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1,5 +1,6 @@
package net.minecraft.server.level;
+import co.earthme.hearse.HearseConfig;
import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
import com.google.common.collect.*;
@@ -61,6 +62,7 @@ import java.io.Writer;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*;
@@ -1197,18 +1199,43 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new DefaultWorkerFactory("tracker-async"));
- private final Executor concurrentTrackWorker = new WorkerThreadPoolExecutor(
- Runtime.getRuntime().availableProcessors(),
- Runtime.getRuntime().availableProcessors(),
- 5L,
- TimeUnit.SECONDS,
- new LinkedBlockingQueue<>(),
- new DefaultWorkerFactory("tracker-concurrent"));
-
- private final AtomicInteger totalRunning = new AtomicInteger();
+ private static Executor concurrentTrackWorker = null;
+ private final AtomicInteger totalRunning = new AtomicInteger(0);
+ private static final AtomicBoolean isInited = new AtomicBoolean(false);
+ private static final AtomicBoolean enabled = new AtomicBoolean();
+
+ public static void tryInitIfNotInited(){
+ if (!isInited.get()){
+ enabled.set(HearseConfig.getBoolean("optimizations.enable-multithreaded-tracker",true));
+ if (enabled.get()){
+ final int threadCount = HearseConfig.getInt("optimizations.multithreaded-tracker-thread-count",Runtime.getRuntime().availableProcessors());
+ concurrentTrackWorker = new WorkerThreadPoolExecutor(
+ threadCount,
+ threadCount,
+ 5L,
+ TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(),
+ new DefaultWorkerFactory("tracker-concurrent"));
+ }
+ isInited.set(true);
+ }
+ }
// Paper start - optimised tracker
private final void processTrackQueue() {
+ tryInitIfNotInited();
+
+ if (!enabled.get()){
+ for (TrackedEntity tracker : this.entityMap.values()) {
+ tracker.updatePlayers(tracker.entity.getPlayersInTrackRange());
+ }
+
+ for (TrackedEntity tracker : this.entityMap.values()) {
+ tracker.serverEntity.sendChanges();
+ }
+ return;
+ }
+
if(this.totalRunning.get() > 0){
return;
}
@@ -1222,7 +1249,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
.stream()
.map(tracker -> CompletableFuture.runAsync(()->{
tracker.updatePlayers(tracker.entity.getPlayersInTrackRange());
- },this.concurrentTrackWorker))
+ },concurrentTrackWorker))
.toArray(CompletableFuture[]::new)).join();
} finally {
//this.level.timings.tracker1.stopTiming(); // Purpur

View File

@@ -1,21 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 18 Jan 2023 20:32:12 +0800
Subject: [PATCH] Hearse: Correct some config key name
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index cf97584215e4b0fcb3b4b92942eaf1f6c8682f54..bae79fd5d9e2bc3ce2386ac8f8e1c14deba94a8f 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1208,7 +1208,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (!isInited.get()){
enabled.set(HearseConfig.getBoolean("optimizations.enable-multithreaded-tracker",true));
if (enabled.get()){
- final int threadCount = HearseConfig.getInt("optimizations.multithreaded-tracker-thread-count",Runtime.getRuntime().availableProcessors());
+ final int threadCount = HearseConfig.getInt("workers.multithreaded-tracker-thread-count",Runtime.getRuntime().availableProcessors());
concurrentTrackWorker = new WorkerThreadPoolExecutor(
threadCount,
threadCount,

View File

@@ -1,37 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 18 Jan 2023 21:17:41 +0800
Subject: [PATCH] Hearse: forkjoinworker can not skip the async check
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/io/papermc/paper/util/TickThread.java b/src/main/java/io/papermc/paper/util/TickThread.java
index fc57850b80303fcade89ca95794f63910404a407..ea277170975f59561775ad9b63467a7c9abdbbe3 100644
--- a/src/main/java/io/papermc/paper/util/TickThread.java
+++ b/src/main/java/io/papermc/paper/util/TickThread.java
@@ -1,5 +1,6 @@
package io.papermc.paper.util;
+import co.earthme.hearse.concurrent.thread.Worker;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
@@ -74,14 +75,14 @@ public class TickThread extends Thread {
}
public static boolean isTickThread() {
- return Thread.currentThread() instanceof TickThread;
+ return Thread.currentThread() instanceof TickThread || Thread.currentThread() instanceof Worker;
}
public static boolean isTickThreadFor(final ServerLevel world, final int chunkX, final int chunkZ) {
- return Thread.currentThread() instanceof TickThread;
+ return Thread.currentThread() instanceof TickThread || Thread.currentThread() instanceof Worker;
}
public static boolean isTickThreadFor(final Entity entity) {
- return Thread.currentThread() instanceof TickThread;
+ return Thread.currentThread() instanceof TickThread || Thread.currentThread() instanceof Worker;
}
}

View File

@@ -1,29 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 18 Jan 2023 21:18:06 +0800
Subject: [PATCH] Hearse: Add NotNull annotation to EntityPositionCache
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/co/earthme/hearse/util/EntityPositionCache.java b/src/main/java/co/earthme/hearse/util/EntityPositionCache.java
index b7ec86d5cb2bb8d62d1a54f5e7b394e992a2b870..6f34233901cf1943694224ab393dea5548cb8e5b 100644
--- a/src/main/java/co/earthme/hearse/util/EntityPositionCache.java
+++ b/src/main/java/co/earthme/hearse/util/EntityPositionCache.java
@@ -3,6 +3,7 @@ package co.earthme.hearse.util;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
+import org.jetbrains.annotations.NotNull;
public class EntityPositionCache {
private final double x;
@@ -10,7 +11,7 @@ public class EntityPositionCache {
private final double z;
private final LivingEntity currentEntity;
- public EntityPositionCache(LivingEntity entity){
+ public EntityPositionCache(@NotNull LivingEntity entity){
this.x = entity.getX();
this.y = entity.getY();
this.z = entity.getZ();

View File

@@ -1,36 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Wed, 18 Jan 2023 21:30:18 +0800
Subject: [PATCH] Hearse: Add tracker thread pool to the pool management list
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index bae79fd5d9e2bc3ce2386ac8f8e1c14deba94a8f..eb0319b961893cc4454196970402b88ced3bce7f 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1,5 +1,6 @@
package net.minecraft.server.level;
+import co.earthme.hearse.Hearse;
import co.earthme.hearse.HearseConfig;
import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
@@ -1199,7 +1200,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new DefaultWorkerFactory("tracker-async"));
- private static Executor concurrentTrackWorker = null;
+ private static WorkerThreadPoolExecutor concurrentTrackWorker = null;
private final AtomicInteger totalRunning = new AtomicInteger(0);
private static final AtomicBoolean isInited = new AtomicBoolean(false);
private static final AtomicBoolean enabled = new AtomicBoolean();
@@ -1216,6 +1217,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new DefaultWorkerFactory("tracker-concurrent"));
+ Hearse.getWorkerManager().addWorker("tracker",concurrentTrackWorker);
}
isInited.set(true);
}

View File

@@ -1,133 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Thu, 19 Jan 2023 10:03:10 +0800
Subject: [PATCH] Hearse: Add locks when calling getChunk in worker thread
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index e311724d2e723115bc9549a61e6206a8aed835d8..b151339cc56418f82f9b75b39b8fc6ab6686993e 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -1,5 +1,6 @@
package net.minecraft.server.level;
+import co.earthme.hearse.concurrent.thread.Worker;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.mojang.datafixers.DataFixer;
@@ -377,57 +378,68 @@ public class ServerChunkCache extends ChunkSource {
}
// Paper end - async chunk io
+ private final Object workerGetChunkLock = new Object();
+
@Nullable
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) {
- final int x1 = x; final int z1 = z; // Paper - conflict on variable change
if (!io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system
return (ChunkAccess) CompletableFuture.supplyAsync(() -> {
return this.getChunk(x, z, leastStatus, create);
}, this.mainThreadProcessor).join();
} else {
- // Paper start - optimise for loaded chunks
- LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z);
- if (ifLoaded != null) {
- return ifLoaded;
+ if (Thread.currentThread() instanceof Worker){
+ synchronized (this.workerGetChunkLock){
+ return this.getChunkUnsafe(x,z,leastStatus,create);
+ }
}
- // Paper end
- //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur
+ return this.getChunkUnsafe(x,z,leastStatus,create);
+ }
+ }
- //gameprofilerfiller.incrementCounter("getChunk"); // Purpur
- long k = ChunkPos.asLong(x, z);
+ private ChunkAccess getChunkUnsafe(int x,int z,ChunkStatus leastStatus,boolean create){
+ final int x1 = x; final int z1 = z; // Paper - conflict on variable change
+ // Paper start - optimise for loaded chunks
+ LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z);
+ if (ifLoaded != null) {
+ return ifLoaded;
+ }
+ // Paper end
+ //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur
- ChunkAccess ichunkaccess;
+ //gameprofilerfiller.incrementCounter("getChunk"); // Purpur
+ long k = ChunkPos.asLong(x, z);
- // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system
+ ChunkAccess ichunkaccess;
- //gameprofilerfiller.incrementCounter("getChunkCacheMiss"); // Purpur
- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper
- ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor;
+ // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system
- Objects.requireNonNull(completablefuture);
- if (!completablefuture.isDone()) { // Paper
- // Paper start - async chunk io/loading
- io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
- // 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);
- io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
- //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur
- } // Paper
- ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
- return ichunkaccess1;
- }, (playerchunk_failure) -> {
- if (create) {
- throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + playerchunk_failure));
- } else {
- return null;
- }
- });
- this.storeInCache(k, ichunkaccess, leastStatus);
- return ichunkaccess;
- }
+ //gameprofilerfiller.incrementCounter("getChunkCacheMiss"); // Purpur
+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper
+ ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor;
+
+ Objects.requireNonNull(completablefuture);
+ if (!completablefuture.isDone()) { // Paper
+ // Paper start - async chunk io/loading
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
+ // 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);
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
+ //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur
+ } // Paper
+ ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
+ return ichunkaccess1;
+ }, (playerchunk_failure) -> {
+ if (create) {
+ throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + playerchunk_failure));
+ } else {
+ return null;
+ }
+ });
+ this.storeInCache(k, ichunkaccess, leastStatus);
+ return ichunkaccess;
}
@Nullable
@@ -440,6 +452,7 @@ public class ServerChunkCache extends ChunkSource {
}
}
+
private void clearCache() {
Arrays.fill(this.lastChunkPos, ChunkPos.INVALID_CHUNK_POS);
Arrays.fill(this.lastChunkStatus, (Object) null);

View File

@@ -1,31 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Thu, 19 Jan 2023 10:04:04 +0800
Subject: [PATCH] Hearse: Change back some collections in
CollectingNeighborUpdater because we already have lock to ensure thread safe
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
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 a1ff442357dfea868c319fd3c10ae28e6fb81956..5bb3ef743fd2c0e0ac69e340355acbf49e4c862b 100644
--- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
+++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
@@ -1,5 +1,6 @@
package net.minecraft.world.level.redstone;
+import com.google.common.collect.Lists;
import com.mojang.logging.LogUtils;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -20,8 +21,8 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
private static final Logger LOGGER = LogUtils.getLogger();
private final Level level;
private final int maxChainedNeighborUpdates;
- private final Deque<NeighborUpdates> stack = new ConcurrentLinkedDeque<>();
- private final List<CollectingNeighborUpdater.NeighborUpdates> addedThisLayer = new CopyOnWriteArrayList<>();
+ private final Deque<NeighborUpdates> stack = new ArrayDeque<>();
+ private final List<CollectingNeighborUpdater.NeighborUpdates> addedThisLayer = Lists.newArrayList();
private int count = 0;
public CollectingNeighborUpdater(Level world, int maxChainDepth) {

View File

@@ -1,156 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 21 Jan 2023 08:46:23 +0800
Subject: [PATCH] Hearse: Port some C2ME's fixes and fix a concurrent problem
in ChunkMap
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index eb0319b961893cc4454196970402b88ced3bce7f..3cc0b79d94784a2c6895336ee1000b70b9278f4d 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1457,7 +1457,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final Entity entity;
private final int range;
SectionPos lastSectionPos;
- public final Set<ServerPlayerConnection> seenBy = ReferenceSets.synchronize(new ReferenceOpenHashSet<>()); // Paper - optimise map impl //Hearse - multithread tracker
+ public final Set<ServerPlayerConnection> seenBy = Sets.newConcurrentHashSet(); // Paper - optimise map impl //Hearse - multithread tracker
public TrackedEntity(Entity entity, int i, int j, boolean flag) {
this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index b151339cc56418f82f9b75b39b8fc6ab6686993e..e73a7685cc78af407131388acff62c3427fd4618 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -1,5 +1,6 @@
package net.minecraft.server.level;
+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
import co.earthme.hearse.concurrent.thread.Worker;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
@@ -378,7 +379,7 @@ public class ServerChunkCache extends ChunkSource {
}
// Paper end - async chunk io
- private final Object workerGetChunkLock = new Object();
+ private final Object schedulingMutex = new Object();
@Nullable
@Override
@@ -388,58 +389,46 @@ public class ServerChunkCache extends ChunkSource {
return this.getChunk(x, z, leastStatus, create);
}, this.mainThreadProcessor).join();
} else {
- if (Thread.currentThread() instanceof Worker){
- synchronized (this.workerGetChunkLock){
- return this.getChunkUnsafe(x,z,leastStatus,create);
- }
+ final int x1 = x; final int z1 = z; // Paper - conflict on variable change
+ //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur
+ LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z);
+ if (ifLoaded != null) {
+ return ifLoaded;
}
- return this.getChunkUnsafe(x,z,leastStatus,create);
- }
- }
-
- private ChunkAccess getChunkUnsafe(int x,int z,ChunkStatus leastStatus,boolean create){
- final int x1 = x; final int z1 = z; // Paper - conflict on variable change
- // Paper start - optimise for loaded chunks
- LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z);
- if (ifLoaded != null) {
- return ifLoaded;
- }
- // Paper end
- //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur
-
- //gameprofilerfiller.incrementCounter("getChunk"); // Purpur
- long k = ChunkPos.asLong(x, z);
+ //gameprofilerfiller.incrementCounter("getChunk"); // Purpur
+ long k = ChunkPos.asLong(x, z);
- ChunkAccess ichunkaccess;
+ ChunkAccess ichunkaccess;
- // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system
+ // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system
- //gameprofilerfiller.incrementCounter("getChunkCacheMiss"); // Purpur
- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper
- ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor;
+ //gameprofilerfiller.incrementCounter("getChunkCacheMiss"); // Purpur
+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper
+ ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor;
- Objects.requireNonNull(completablefuture);
- if (!completablefuture.isDone()) { // Paper
- // Paper start - async chunk io/loading
- io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
- // 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);
- io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
- //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur
- } // Paper
- ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
- return ichunkaccess1;
- }, (playerchunk_failure) -> {
- if (create) {
- throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + playerchunk_failure));
- } else {
- return null;
- }
- });
- this.storeInCache(k, ichunkaccess, leastStatus);
- return ichunkaccess;
+ Objects.requireNonNull(completablefuture);
+ if (!completablefuture.isDone()) { // Paper
+ // Paper start - async chunk io/loading
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
+ // 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);
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
+ //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur
+ } // Paper
+ ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
+ return ichunkaccess1;
+ }, (playerchunk_failure) -> {
+ if (create) {
+ throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + playerchunk_failure));
+ } else {
+ return null;
+ }
+ });
+ this.storeInCache(k, ichunkaccess, leastStatus);
+ return ichunkaccess;
+ }
}
@Nullable
@@ -509,11 +498,14 @@ public class ServerChunkCache extends ChunkSource {
}
};
- this.level.chunkTaskScheduler.scheduleChunkLoad(
- chunkX, chunkZ, leastStatus, true,
- isUrgent ? ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.BLOCKING : ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL,
- complete
- );
+ //C2ME --general issue fixes
+ synchronized (this.schedulingMutex){
+ this.level.chunkTaskScheduler.scheduleChunkLoad(
+ chunkX, chunkZ, leastStatus, true,
+ isUrgent ? PrioritisedExecutor.Priority.BLOCKING : PrioritisedExecutor.Priority.NORMAL,
+ complete
+ );
+ }
return ret;
} else {

View File

@@ -1,25 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 21 Jan 2023 08:49:00 +0800
Subject: [PATCH] Hearse: Fix a careless error in PlayerChunkLoader
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
index 028b23f5c23bbfd83498c3e06a56079ceb0798ad..d9cee42da1b097590e627142d3c5dccbc180b5ae 100644
--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
+++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
@@ -527,11 +527,9 @@ public final class PlayerChunkLoader {
// drain entries from wait queue
while ((data1 = this.chunkSendWaitQueue.pollFirst())!=null) {
if (data1.nextChunkSendTarget > time) {
+ this.chunkSendWaitQueue.add(data1);
break;
}
-
- this.chunkSendWaitQueue.pollFirst();
-
this.chunkSendQueue.add(data1);
}

View File

@@ -1,235 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sat, 21 Jan 2023 09:49:43 +0800
Subject: [PATCH] Hearse: Remove some fixes and await entity task
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
index c0d235e7227db0be6c6f753d8a6e13ad2716f798..891a9167e4072d6ce5b526b1676d8ecb836c8733 100644
--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -5,37 +5,37 @@ 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 co.earthme.hearse.util.ArrayListBlockingQueue;
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;
+import java.util.concurrent.locks.LockSupport;
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;
+ private static boolean asyncEntityEnabled = true;
+ private static boolean awaitEntityTasks = true;
+ private static final AtomicInteger taskCounter = new AtomicInteger(0);
- public static void executeAsyncTask(Runnable task){
- if (!asyncEntityEnabled){
- throw new RejectedExecutionException();
+ //To prevent the chunk system error.This is necessary
+ public static void awaitEntityTasks(){
+ while (taskCounter.get() > 0){
+ LockSupport.parkNanos("Await entities",1000000);
}
- 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){
+ asyncEntityEnabled = HearseConfig.getBoolean("optimizations.enable-parallel-entity",asyncEntityEnabled);
+ awaitEntityTasks = HearseConfig.getBoolean("optimizations.await-parallel-entity-tasks",awaitEntityTasks);
+ if (asyncEntityEnabled){
+ final int workerCount = HearseConfig.getInt("workers.async-entity-worker-count",Runtime.getRuntime().availableProcessors());
worker = new WorkerThreadPoolExecutor(
workerCount,
workerCount,
@@ -46,47 +46,33 @@ public class ServerEntityTickHook {
);
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();
+ taskCounter.getAndIncrement();
Runnable task = ()->{
- entity.activatedPriorityReset = false;
- if (!entity.isRemoved()) {
- entity.checkDespawn();
- Entity entity1 = entity.getVehicle();
- if (entity1 != null) {
- if (!entity1.isRemoved() && entity1.hasPassenger(entity)) {
- return;
+ try {
+ 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();
}
- 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();
}
+ }finally {
+ taskCounter.getAndDecrement();
}
};
if (!asyncEntityEnabled){
diff --git a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
index 759b6dc9c719c6ff63348f9eacc760f8cef3163e..4c85bf8e4705a781a55a048f0b0541f0d32e2f07 100644
--- a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
+++ b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
@@ -20,13 +20,11 @@ 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);
+ boolean enabledParaWorld = HearseConfig.getBoolean("optimizations.enableparallelworldtick", true);
if (enabledParaWorld){
worker = new WorkerThreadPoolExecutor(
MinecraftServer.getServer().levels.size(),
@@ -43,16 +41,11 @@ public class ServerLevelTickHook {
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(()->{
+ Runnable task = () -> {
try {
try {
worldserver.tick(shouldKeepTicking);
@@ -66,7 +59,14 @@ public class ServerLevelTickHook {
}finally {
activeTaskCount.getAndDecrement();
}
- });
+ };
+
+ if (worker == null){
+ task.run();
+ return;
+ }
+
+ worker.execute(task);
}
public static void awaitWorldTicKTasks(){
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 42599340856aa5ffcfae2281684174ca4dee289a..f0f164c6c95c0abde63f1327335af94148910152 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1,7 +1,6 @@
package net.minecraft.server;
import co.earthme.hearse.Hearse;
-import co.earthme.hearse.HearseConfig;
import co.earthme.hearse.server.ServerEntityTickHook;
import co.earthme.hearse.server.ServerLevelTickHook;
import com.google.common.base.Splitter;
@@ -1397,7 +1396,6 @@ 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);
@@ -1535,6 +1533,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
//this.profiler.push("tick"); // Purpur
ServerLevelTickHook.callWorldTick(worldserver,shouldKeepTicking);
}
+ ServerEntityTickHook.awaitEntityTasks();
ServerLevelTickHook.awaitWorldTicKTasks();
this.isIteratingOverLevels = false; // Paper
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index e73a7685cc78af407131388acff62c3427fd4618..becac3c1087c77b289c3b0a721c4146bea046e51 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -498,14 +498,11 @@ public class ServerChunkCache extends ChunkSource {
}
};
- //C2ME --general issue fixes
- synchronized (this.schedulingMutex){
- this.level.chunkTaskScheduler.scheduleChunkLoad(
- chunkX, chunkZ, leastStatus, true,
- isUrgent ? PrioritisedExecutor.Priority.BLOCKING : PrioritisedExecutor.Priority.NORMAL,
- complete
- );
- }
+ this.level.chunkTaskScheduler.scheduleChunkLoad(
+ chunkX, chunkZ, leastStatus, true,
+ isUrgent ? PrioritisedExecutor.Priority.BLOCKING : PrioritisedExecutor.Priority.NORMAL,
+ complete
+ );
return ret;
} else {

View File

@@ -1,83 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 22 Jan 2023 13:32:24 +0800
Subject: [PATCH] Hearse: Fix a threading issue and ingore an error
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
index d9cee42da1b097590e627142d3c5dccbc180b5ae..23c32a06dce8f0c45647c3619c98ba95290cfa7d 100644
--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
+++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
@@ -518,6 +518,7 @@ public final class PlayerChunkLoader {
protected static final AtomicInteger concurrentChunkSends = new AtomicInteger();
protected final Reference2IntOpenHashMap<PlayerLoaderData> sendingChunkCounts = new Reference2IntOpenHashMap<>();
private static long nextChunkSend;
+
private void trySendChunks() {
final long time = System.nanoTime();
if (time < nextChunkSend) {
@@ -560,7 +561,7 @@ public final class PlayerChunkLoader {
}
if (!this.isChunkPlayerLoaded(queuedSend.chunkX, queuedSend.chunkZ)) {
- throw new IllegalStateException();
+ continue;
}
data.nextChunkSendTarget = nextPlayerDeadline;
@@ -722,12 +723,13 @@ public final class PlayerChunkLoader {
}
}
+
public void tickMidTick() {
- // try to send more chunks
- this.trySendChunks();
+ // try to send more chunks
+ this.trySendChunks();
- // try to queue more chunks to load
- this.tryLoadChunks();
+ // try to queue more chunks to load
+ this.tryLoadChunks();
}
static final class ChunkPriorityHolder {
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index becac3c1087c77b289c3b0a721c4146bea046e51..2caa5ab3a7b4bf0c535863f5ff54fcb8c0dc3c59 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -937,6 +937,8 @@ public class ServerChunkCache extends ChunkSource {
this.spawnFriendlies = spawnAnimals;
}
+ private final Object pollTaskLock = new Object();
+
public String getChunkDebugData(ChunkPos pos) {
return this.chunkMap.getChunkDebugData(pos);
}
@@ -963,6 +965,7 @@ public class ServerChunkCache extends ChunkSource {
this.distanceManager.removeTicketsOnClosing();
}
+
public final class MainThreadExecutor extends BlockableEventLoop<Runnable> {
MainThreadExecutor(Level world) {
@@ -998,9 +1001,11 @@ public class ServerChunkCache extends ChunkSource {
@Override
// CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
public boolean pollTask() {
- ServerChunkCache.this.chunkMap.playerChunkManager.tickMidTick();
- if (ServerChunkCache.this.runDistanceManagerUpdates()) {
- return true;
+ synchronized (ServerChunkCache.this.pollTaskLock){
+ ServerChunkCache.this.chunkMap.playerChunkManager.tickMidTick();
+ if (ServerChunkCache.this.runDistanceManagerUpdates()) {
+ return true;
+ }
}
return super.pollTask() | ServerChunkCache.this.level.chunkTaskScheduler.executeMainThreadTask(); // Paper - rewrite chunk system
}

View File

@@ -1,31 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Tue, 24 Jan 2023 09:32:37 +0800
Subject: [PATCH] Hearse-Fix some threading issue in bukkit event system
Original license: MIT
Original project: https://github.com/Era4FunMC/Hearse
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 96cde1f86ca073e7e9e5799bcb12a10adf9230b2..a273d04c9c1645f1b1147e1a8502856a410400e9 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.concurrent.thread.Worker;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
@@ -2220,6 +2221,11 @@ public final class CraftServer implements Server {
return io.papermc.paper.util.TickThread.isTickThread(); // Paper - rewrite chunk system
}
+ @Override
+ public boolean isWorkerThread(){
+ return Worker.isWorker();
+ }
+
// Paper start
@Override
public net.kyori.adventure.text.Component motd() {