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:
@@ -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) {
|
||||
@@ -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
|
||||
@@ -1,8 +1,11 @@
|
||||
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
|
||||
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
|
||||
index 4fd709a550bf8da1e996894a1ca6b91206c31e9e..b43a7c3aea930805ea917176d4a7b5564be084cb 100644
|
||||
791
patches/server/0041-MikuServer-Async-entity-traveling.patch
Normal file
791
patches/server/0041-MikuServer-Async-entity-traveling.patch
Normal 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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
620
patches/server/0042-MikuServer-Async-entity-ai.patch
Normal file
620
patches/server/0042-MikuServer-Async-entity-ai.patch
Normal 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));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: wangxyper <wangxyper@163.com>
|
||||
Date: Sun, 15 Jan 2023 09:52:27 +0800
|
||||
Subject: [PATCH] Hearse: Paper chunk system changes
|
||||
Date: Fri, 27 Jan 2023 19:52:49 +0800
|
||||
Subject: [PATCH] MikuServer: Concurrent problems fixes
|
||||
|
||||
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
|
||||
index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079ceb0798ad 100644
|
||||
index 0b060183429f4c72ec767075538477b4302bbf0d..23c32a06dce8f0c45647c3619c98ba95290cfa7d 100644
|
||||
--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
|
||||
+++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
|
||||
@@ -7,9 +7,9 @@ import io.papermc.paper.util.CoordinateUtils;
|
||||
@@ -81,7 +81,13 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@@ -93,17 +99,18 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
|
||||
- if (data.nextChunkSendTarget > time) {
|
||||
+ while ((data1 = this.chunkSendWaitQueue.pollFirst())!=null) {
|
||||
+ if (data1.nextChunkSendTarget > time) {
|
||||
+ this.chunkSendWaitQueue.add(data1);
|
||||
break;
|
||||
}
|
||||
|
||||
this.chunkSendWaitQueue.pollFirst();
|
||||
|
||||
-
|
||||
- this.chunkSendWaitQueue.pollFirst();
|
||||
-
|
||||
- this.chunkSendQueue.add(data);
|
||||
+ this.chunkSendQueue.add(data1);
|
||||
}
|
||||
|
||||
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 long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time;
|
||||
@@ -117,7 +124,7 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
|
||||
final int currSends = concurrentChunkSends.get();
|
||||
if (currSends >= maxSends) {
|
||||
break;
|
||||
@@ -554,19 +552,12 @@ public final class PlayerChunkLoader {
|
||||
@@ -554,24 +551,17 @@ public final class PlayerChunkLoader {
|
||||
if (!concurrentChunkSends.compareAndSet(currSends, currSends + 1)) {
|
||||
continue;
|
||||
}
|
||||
@@ -138,7 +145,13 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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 long time = System.nanoTime();
|
||||
boolean updatedCounters = false;
|
||||
@@ -179,7 +192,7 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -673,7 +661,7 @@ public final class PlayerChunkLoader {
|
||||
@@ -673,7 +660,7 @@ public final class PlayerChunkLoader {
|
||||
|
||||
final int currentChunkLoads = this.concurrentChunkLoads;
|
||||
if (currentChunkLoads >= maxLoads || (GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate > 0 && (TICKET_ADDITION_COUNTER_SHORT.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate || TICKET_ADDITION_COUNTER_LONG.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate))
|
||||
@@ -188,6 +201,24 @@ index 0b060183429f4c72ec767075538477b4302bbf0d..028b23f5c23bbfd83498c3e06a56079c
|
||||
// don't poll, we didn't load it
|
||||
this.chunkLoadQueue.add(data);
|
||||
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 {
|
||||
|
||||
// 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?
|
||||
&& this.usingLookingPriority == useLookPriority
|
||||
diff --git a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
|
||||
index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc926053dff 100644
|
||||
index ec90ff5c2581706180498b74dbbf960d52d47209..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
|
||||
@@ -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.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.Int2ReferenceArrayMap;
|
||||
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.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.Object2ReferenceArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
|
||||
-import it.unimi.dsi.fastutil.objects.Object2ReferenceMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import io.papermc.paper.chunk.system.ChunkSystem;
|
||||
@@ -45,16 +51,16 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
@@ -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;
|
||||
|
||||
- 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 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 maxSection; // inclusive
|
||||
private final LevelCallback<Entity> worldCallback;
|
||||
-
|
||||
- private final StampedLock entityByLock = new StampedLock();
|
||||
- private final Int2ReferenceOpenHashMap<Entity> entityById = new Int2ReferenceOpenHashMap<>();
|
||||
- private final Object2ReferenceOpenHashMap<UUID, Entity> entityByUUID = new Object2ReferenceOpenHashMap<>();
|
||||
+ private final Int2ReferenceMap<Entity> entityById = new Int2ReferenceOpenHashMap<>();
|
||||
+ private final Object2ReferenceMap<UUID, Entity> entityByUUID = new Object2ReferenceOpenHashMap<>();
|
||||
- private final Map<Integer,Entity> entityById = Int2ReferenceMaps.synchronize(new Int2ReferenceOpenHashMap<>());
|
||||
- private final Object2ReferenceMap<UUID, Entity> entityByUUID = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenHashMap<>());
|
||||
+ 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) {
|
||||
@@ -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) {
|
||||
try {
|
||||
final Entity ret = this.entityByUUID.get(id);
|
||||
@@ -275,7 +325,7 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
|
||||
if (this.entityByLock.validate(attempt)) {
|
||||
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
|
||||
@@ -290,52 +340,21 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
|
||||
if (this.off >= this.length) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
@@ -179,7 +184,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
}
|
||||
|
||||
@Override
|
||||
- public void remove() {
|
||||
+ public synchronized void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -208,8 +213,8 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
public void get(final AABB box, final Consumer<Entity> action) {
|
||||
List<Entity> entities = new ArrayList<>();
|
||||
this.getEntitiesWithoutDragonParts(null, box, entities, null);
|
||||
- for (int i = 0, len = entities.size(); i < len; ++i) {
|
||||
- action.accept(entities.get(i));
|
||||
+ for (Entity entity : entities) {
|
||||
+ action.accept(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,8 +222,8 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AABB box, final AbortableIterationConsumer<U> action) {
|
||||
List<Entity> entities = new ArrayList<>();
|
||||
this.getEntitiesWithoutDragonParts(null, box, entities, null);
|
||||
- for (int i = 0, len = entities.size(); i < len; ++i) {
|
||||
- final U casted = filter.tryCast(entities.get(i));
|
||||
+ for (Entity entity : entities) {
|
||||
+ final U casted = filter.tryCast(entity);
|
||||
if (casted != null && action.accept(casted).shouldAbort()) {
|
||||
break;
|
||||
}
|
||||
@@ -228,75 +233,50 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
@@ -231,75 +236,50 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
public void entityStatusChange(final Entity entity, final ChunkEntitySlices slices, final Visibility oldVisibility, final Visibility newVisibility, final boolean moved,
|
||||
final boolean created, final boolean destroyed) {
|
||||
TickThread.ensureTickThread(entity, "Entity status change must only happen on the main thread");
|
||||
-
|
||||
- if (entity.updatingSectionStatus) {
|
||||
- // recursive status update
|
||||
- LOGGER.error("Cannot recursively update entity chunk status for entity " + entity, new Throwable());
|
||||
- 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;
|
||||
- }
|
||||
-
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
- 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;
|
||||
- }
|
||||
-
|
||||
if (fromDisk) {
|
||||
ChunkSystem.onEntityPreAdd(this.world, entity);
|
||||
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()) {
|
||||
throw new IllegalStateException("Only call Entity#setRemoved to remove an entity");
|
||||
}
|
||||
- final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ);
|
||||
+ ChunkEntitySlices slices;
|
||||
+ synchronized (this.chunkEntitySlicesLock){
|
||||
+ 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 + ")");
|
||||
@@ -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
|
||||
TickThread.ensureTickThread(this.world, entity.sectionX, entity.sectionZ, "Cannot move entity off-main");
|
||||
|
||||
- final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ);
|
||||
+ ChunkEntitySlices old;
|
||||
+ synchronized (this.chunkEntitySlicesLock){
|
||||
+
|
||||
+ 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)) {
|
||||
@@ -609,7 +590,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
@@ -612,7 +602,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
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");
|
||||
synchronized (this) {
|
||||
final ChunkEntitySlices curr = this.getChunk(chunkX, chunkZ);
|
||||
@@ -489,7 +517,8 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
|
||||
- this.addChunk(chunkX, chunkZ, slices);
|
||||
- } else {
|
||||
- this.addChunk(chunkX, chunkZ, slices);
|
||||
+ synchronized (this.chunkEntitySlicesLock){
|
||||
+ this.regionLoadLock.lock();
|
||||
+ try {
|
||||
+ if (curr != null) {
|
||||
+ this.removeChunk(chunkX, chunkZ);
|
||||
+ curr.mergeInto(slices);
|
||||
@@ -497,6 +526,8 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9
|
||||
+ } else {
|
||||
+ 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) {
|
||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot unload entity section off-main");
|
||||
- this.removeChunk(chunkX, chunkZ);
|
||||
+ synchronized (this.chunkEntitySlicesLock){
|
||||
+ this.removeChunk(chunkX, chunkZ);
|
||||
+ }
|
||||
}
|
||||
|
||||
public ChunkEntitySlices getChunk(final int chunkX, final int chunkZ) {
|
||||
@@ -699,27 +683,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
this.removeChunk(chunkX, chunkZ);
|
||||
@@ -702,27 +696,7 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
|
||||
public ChunkSlicesRegion getRegion(final int regionX, final int 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) {
|
||||
@@ -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);
|
||||
|
||||
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 {
|
||||
region = new ChunkSlicesRegion();
|
||||
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();
|
||||
|
||||
@Override
|
||||
@@ -686,3 +711,581 @@ index 830d863cd9665d58875bfa5ca2bcd22f89ab2d49..15eeea40bb7a44470f6f3f0e2473cb45
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
306
patches/server/0044-MikuServer-Code-error-fix.patch
Normal file
306
patches/server/0044-MikuServer-Code-error-fix.patch
Normal 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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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());
|
||||
@@ -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");
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
@@ -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) {
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
+ }
|
||||
+}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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;
|
||||
+ }
|
||||
+}
|
||||
@@ -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() {
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
@@ -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() {
|
||||
@@ -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);
|
||||
}
|
||||
-
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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();
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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,
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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) {
|
||||
@@ -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 {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
}
|
||||
@@ -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() {
|
||||
Reference in New Issue
Block a user