diff --git a/patches/api/0011-Hearse-Fix-some-threading-issue-in-bukkit-event-syst.patch b/patches/api/0011-Hearse-Fix-some-threading-issue-in-bukkit-event-syst.patch deleted file mode 100644 index ebd1274a..00000000 --- a/patches/api/0011-Hearse-Fix-some-threading-issue-in-bukkit-event-syst.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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) { diff --git a/patches/api/0011-MikuServer-Fix-threading-issue-in-event-system.patch b/patches/api/0011-MikuServer-Fix-threading-issue-in-event-system.patch new file mode 100644 index 00000000..a0e459d4 --- /dev/null +++ b/patches/api/0011-MikuServer-Fix-threading-issue-in-event-system.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +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 diff --git a/patches/server/0040-Akarin-Async-userlist-saving.patch b/patches/server/0040-Akarin-Save-Json-list-asynchronously.patch similarity index 93% rename from patches/server/0040-Akarin-Async-userlist-saving.patch rename to patches/server/0040-Akarin-Save-Json-list-asynchronously.patch index 4041ceec..32c115b5 100644 --- a/patches/server/0040-Akarin-Async-userlist-saving.patch +++ b/patches/server/0040-Akarin-Save-Json-list-asynchronously.patch @@ -1,8 +1,11 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper +From: =?UTF-8?q?=E3=84=97=E3=84=A0=CB=8B=20=E3=84=91=E3=84=A7=CB=8A?= + 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 diff --git a/patches/server/0041-MikuServer-Async-entity-traveling.patch b/patches/server/0041-MikuServer-Async-entity-traveling.patch new file mode 100644 index 00000000..823d2dbe --- /dev/null +++ b/patches/server/0041-MikuServer-Async-entity-traveling.patch @@ -0,0 +1,791 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +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 submittedTasks = new ConcurrentLinkedDeque<>(); ++ ++ public CallbackExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue) { ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); ++ } ++ ++ public CallbackExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull ThreadFactory threadFactory) { ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); ++ } ++ ++ public CallbackExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull RejectedExecutionHandler handler) { ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); ++ } ++ ++ public CallbackExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue 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 { + +- 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 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 iterator() { +- return new Iterator() { +- +- 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 { + public final ServerLevel world; + + private final StampedLock stateLock = new StampedLock(); +- protected final Long2ObjectOpenHashMap regions = new Long2ObjectOpenHashMap<>(128, 0.5f); ++ protected final Long2ObjectMap regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f)); + + private final int minSection; // inclusive + private final int maxSection; // inclusive + private final LevelCallback worldCallback; + + private final StampedLock entityByLock = new StampedLock(); +- private final Int2ReferenceOpenHashMap entityById = new Int2ReferenceOpenHashMap<>(); +- private final Object2ReferenceOpenHashMap entityByUUID = new Object2ReferenceOpenHashMap<>(); ++ private final Map entityById = Int2ReferenceMaps.synchronize(new Int2ReferenceOpenHashMap<>()); ++ private final Object2ReferenceMap entityByUUID = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenHashMap<>()); + private final EntityList accessibleEntities = new EntityList(); + + public EntityLookup(final ServerLevel world, final LevelCallback worldCallback) { +@@ -208,8 +211,8 @@ public final class EntityLookup implements LevelEntityGetter { + public void get(final AABB box, final Consumer action) { + List 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 { + public void get(final EntityTypeTest filter, final AABB box, final AbortableIterationConsumer action) { + List 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 { + + 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 { + } + + 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 TEMP_COLLISION_LIST = new UnsafeList<>(1024); +- static boolean tempCollisionListInUse; +- +- public static UnsafeList getTempCollisionList() { +- if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) { +- return new UnsafeList<>(16); +- } +- tempCollisionListInUse = true; +- return TEMP_COLLISION_LIST; +- } +- +- public static void returnTempCollisionList(List list) { +- if (list != TEMP_COLLISION_LIST) { +- return; +- } +- ((UnsafeList)list).setSize(0); +- tempCollisionListInUse = false; ++ public static List getTempCollisionList() { ++ return Lists.newCopyOnWriteArrayList(); + } + +- static final UnsafeList TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024); +- static boolean tempGetEntitiesListInUse; ++ public static void returnTempCollisionList(List list) {} + +- public static UnsafeList getTempGetEntitiesList() { +- if (!Bukkit.isPrimaryThread() || tempGetEntitiesListInUse) { +- return new UnsafeList<>(16); +- } +- tempGetEntitiesListInUse = true; +- return TEMP_GET_ENTITIES_LIST; ++ public static List getTempGetEntitiesList() { ++ return Lists.newCopyOnWriteArrayList(); + } + +- public static void returnTempGetEntitiesList(List list) { +- if (list != TEMP_GET_ENTITIES_LIST) { +- return; +- } +- ((UnsafeList)list).setSize(0); +- tempGetEntitiesListInUse = false; +- } ++ public static void returnTempGetEntitiesList(List 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(), ++ 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= 5000000000L) { + this.lastServerStatus = i; +@@ -1522,11 +1536,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 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> 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 records = new Short2ObjectOpenHashMap<>(); +- private final Map, Set> byType = Maps.newHashMap(); public final Map, Set> getData() { return this.byType; } // Paper - public accessor ++ private final Short2ObjectMap records = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>()); ++ private final Map, Set> byType = Maps.newConcurrentMap(); ++ public final Map, Set> getData() { return this.byType; } // Paper - public accessor + private final Runnable setDirty; + private boolean isValid; + public final Optional 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(); + } + diff --git a/patches/server/0042-Hearse-Paper-world-code-changes.patch b/patches/server/0042-Hearse-Paper-world-code-changes.patch deleted file mode 100644 index 28ff6777..00000000 --- a/patches/server/0042-Hearse-Paper-world-code-changes.patch +++ /dev/null @@ -1,763 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 queuedCoordinates = new ConcurrentLinkedDeque<>(); -+ public final Deque 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, EntityCollectionBySection> entitiesByClass; -+ protected final Reference2ObjectMap, 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 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 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 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, EntityCollectionBySection>> iterator = -- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { -- final Reference2ObjectMap.Entry, 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, 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, EntityCollectionBySection>> iterator = -- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { -- final Reference2ObjectMap.Entry, 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, 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 into, final Predicate 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 into, final Predicate 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 into, final Predicate 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 void getEntities(final EntityType type, final AABB box, final List into, - final Predicate 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 clazz) { -@@ -287,12 +411,28 @@ public final class ChunkEntitySlices { - - public void getEntities(final Class clazz, final Entity except, final AABB box, final List into, - final Predicate 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); - } - } - } diff --git a/patches/server/0042-MikuServer-Async-entity-ai.patch b/patches/server/0042-MikuServer-Async-entity-ai.patch new file mode 100644 index 00000000..f73e3fac --- /dev/null +++ b/patches/server/0042-MikuServer-Async-entity-ai.patch @@ -0,0 +1,620 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +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 { + 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 { + } + } + +- 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 { + 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 { + } + } + +- 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 { + 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 { + //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 iterator() { ++ public synchronized IteratorSafeOrderedReferenceSet.Iterator iterator() { + return this.iterator(0); + } + +- public IteratorSafeOrderedReferenceSet.Iterator iterator(final int flags) { ++ public synchronized IteratorSafeOrderedReferenceSet.Iterator 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 unsafeIterator() { ++ public synchronized java.util.Iterator unsafeIterator() { + return this.unsafeIterator(0); + } +- public java.util.Iterator unsafeIterator(final int flags) { ++ public synchronized java.util.Iterator 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 { + } + + @Override +- public boolean hasNext() { ++ public synchronized boolean hasNext() { + if (this.finished) { + return false; + } +@@ -297,7 +297,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + + @Override +- public E next() { ++ public synchronized E next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } +@@ -310,7 +310,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + + @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 { + } + + @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, EntityCollectionBySection> entitiesByClass; ++ protected final Reference2ObjectMap, 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, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { ++ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { + final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); + + if (entry.getKey().isInstance(entity)) { +@@ -234,7 +235,7 @@ public final class ChunkEntitySlices { + } + + for (final Iterator, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { ++ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { + final Reference2ObjectMap.Entry, 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 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 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 into, final Predicate predicate) { ++ public synchronized void getEntities(final Entity except, final AABB box, final List into, final Predicate 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 into, ++ public synchronized void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List into, + final Predicate 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 into, ++ public synchronized void getEntitiesWithEnderDragonParts(final Entity except, final Class clazz, final AABB box, final List into, + final Predicate predicate) { + if (this.count == 0) { + return; +@@ -559,7 +560,7 @@ public final class ChunkEntitySlices { + } + } + +- public void getEntities(final EntityType type, final AABB box, final List into, ++ public synchronized void getEntities(final EntityType type, final AABB box, final List into, + final Predicate 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 unloadQueue; + int viewDistance; + public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper +- public final ReferenceOpenHashSet needsChangeBroadcasting = new ReferenceOpenHashSet<>(); ++ public final ReferenceSet 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 copy = this.chunkMap.needsChangeBroadcasting.clone(); ++ List 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 extends AbstractCollection { +- private final Map, List> byClass = Maps.newHashMap(); ++ private final Map, List> byClass = Maps.newConcurrentMap(); + private final Class baseClass; +- private final List allInstances = Lists.newArrayList(); ++ private final List allInstances = Lists.newCopyOnWriteArrayList(); + + public ClassInstanceMultiMap(Class elementType) { + this.baseClass = elementType; +@@ -23,7 +23,7 @@ public class ClassInstanceMultiMap extends AbstractCollection { + } + + @Override +- public boolean add(T object) { ++ public synchronized boolean add(T object) { + boolean bl = false; + + for(Map.Entry, List> entry : this.byClass.entrySet()) { +@@ -36,7 +36,7 @@ public class ClassInstanceMultiMap extends AbstractCollection { + } + + @Override +- public boolean remove(Object object) { ++ public synchronized boolean remove(Object object) { + boolean bl = false; + + for(Map.Entry, List> entry : this.byClass.entrySet()) { +@@ -50,32 +50,32 @@ public class ClassInstanceMultiMap extends AbstractCollection { + } + + @Override +- public boolean contains(Object object) { ++ public synchronized boolean contains(Object object) { + return this.find(object.getClass()).contains(object); + } + +- public Collection find(Class type) { ++ public synchronized Collection find(Class type) { + if (!this.baseClass.isAssignableFrom(type)) { + throw new IllegalArgumentException("Don't know how to search for " + type); + } else { + List list = this.byClass.computeIfAbsent(type, (typeClass) -> { + return this.allInstances.stream().filter(typeClass::isInstance).collect(Collectors.toList()); + }); +- return Collections.unmodifiableCollection(list); ++ return (Collection) Collections.unmodifiableCollection(list); + } + } + + @Override +- public Iterator iterator() { ++ public synchronized Iterator iterator() { + return (Iterator)(this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator())); + } + +- public List getAllInstances() { ++ public synchronized List 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 stack = new ArrayDeque<>(); +- private final List addedThisLayer = new ArrayList<>(); ++ private final Deque stack = new ConcurrentLinkedDeque<>(); ++ private final List 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)); + } + diff --git a/patches/server/0043-Hearse-Paper-util-code-changes.patch b/patches/server/0043-Hearse-Paper-util-code-changes.patch deleted file mode 100644 index 996ed81a..00000000 --- a/patches/server/0043-Hearse-Paper-util-code-changes.patch +++ /dev/null @@ -1,475 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 { -- -- 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 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 iterator() { -- return new Iterator() { -- -- 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 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 -- */ - public final class OptimizedSmallEnumSet> { - - private final Class enumClass; -@@ -20,7 +17,7 @@ public final class OptimizedSmallEnumSet> { - 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> { - 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> { - 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 enums) { -+ public synchronized void addAllUnchecked(final Collection enums) { - for (final E element : enums) { - if (element == null) { - throw new NullPointerException("Null element"); -@@ -57,15 +54,15 @@ public final class OptimizedSmallEnumSet> { - } - } - -- public long getBackingSet() { -+ public synchronized long getBackingSet() { - return this.backingSet; - } - -- public boolean hasCommonElements(final OptimizedSmallEnumSet other) { -+ public synchronized boolean hasCommonElements(final OptimizedSmallEnumSet 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 TEMP_COLLISION_LIST = new UnsafeList<>(1024); -- static boolean tempCollisionListInUse; -- -- public static UnsafeList getTempCollisionList() { -- if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) { -- return new UnsafeList<>(16); -- } -- tempCollisionListInUse = true; -- return TEMP_COLLISION_LIST; -- } -- -- public static void returnTempCollisionList(List list) { -- if (list != TEMP_COLLISION_LIST) { -- return; -- } -- ((UnsafeList)list).setSize(0); -- tempCollisionListInUse = false; -+ public static List getTempCollisionList() { -+ return Lists.newCopyOnWriteArrayList(); - } - -- static final UnsafeList TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024); -- static boolean tempGetEntitiesListInUse; -+ public static void returnTempCollisionList(List list) {} - -- public static UnsafeList getTempGetEntitiesList() { -- if (!Bukkit.isPrimaryThread() || tempGetEntitiesListInUse) { -- return new UnsafeList<>(16); -- } -- tempGetEntitiesListInUse = true; -- return TEMP_GET_ENTITIES_LIST; -+ public static List getTempGetEntitiesList() { -+ return Lists.newCopyOnWriteArrayList(); - } - -- public static void returnTempGetEntitiesList(List list) { -- if (list != TEMP_GET_ENTITIES_LIST) { -- return; -- } -- ((UnsafeList)list).setSize(0); -- tempGetEntitiesListInUse = false; -- } -- // Paper end - optimise collisions -+ public static void returnTempGetEntitiesList(List 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 { - 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 { - } - } - -- 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 { - 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 { - } - } - -- 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 { - 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 { - //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 iterator() { -+ public synchronized IteratorSafeOrderedReferenceSet.Iterator iterator() { - return this.iterator(0); - } - -- public IteratorSafeOrderedReferenceSet.Iterator iterator(final int flags) { -+ public synchronized IteratorSafeOrderedReferenceSet.Iterator 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 unsafeIterator() { -+ public synchronized java.util.Iterator unsafeIterator() { - return this.unsafeIterator(0); - } -- public java.util.Iterator unsafeIterator(final int flags) { -+ public synchronized java.util.Iterator 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 { - } - - @Override -- public boolean hasNext() { -+ public synchronized boolean hasNext() { - if (this.finished) { - return false; - } -@@ -297,7 +297,7 @@ public final class IteratorSafeOrderedReferenceSet { - } - - @Override -- public E next() { -+ public synchronized E next() { - if (!this.hasNext()) { - throw new NoSuchElementException(); - } -@@ -310,7 +310,7 @@ public final class IteratorSafeOrderedReferenceSet { - } - - @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 { - } - - @Override -- public void finishedIterating() { -+ public synchronized void finishedIterating() { - if (this.finished || !this.canFinish) { - throw new IllegalStateException(); - } diff --git a/patches/server/0041-Hearse-Paper-chunk-system-changes.patch b/patches/server/0043-MikuServer-Concurrent-problems-fixes.patch similarity index 50% rename from patches/server/0041-Hearse-Paper-chunk-system-changes.patch rename to patches/server/0043-MikuServer-Concurrent-problems-fixes.patch index c7899984..5ead6253 100644 --- a/patches/server/0041-Hearse-Paper-chunk-system-changes.patch +++ b/patches/server/0043-MikuServer-Concurrent-problems-fixes.patch @@ -1,13 +1,13 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: wangxyper -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 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 { +@@ -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 { public final ServerLevel world; - private final StampedLock stateLock = new StampedLock(); -- protected final Long2ObjectOpenHashMap regions = new Long2ObjectOpenHashMap<>(128, 0.5f); +- protected final Long2ObjectMap 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 regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f)); ++ protected final Long2ObjectMap regions = Long2ObjectMaps.synchronize(new Long2ObjectArrayMap<>()); private final int minSection; // inclusive private final int maxSection; // inclusive private final LevelCallback worldCallback; - - private final StampedLock entityByLock = new StampedLock(); -- private final Int2ReferenceOpenHashMap entityById = new Int2ReferenceOpenHashMap<>(); -- private final Object2ReferenceOpenHashMap entityByUUID = new Object2ReferenceOpenHashMap<>(); -+ private final Int2ReferenceMap entityById = new Int2ReferenceOpenHashMap<>(); -+ private final Object2ReferenceMap entityByUUID = new Object2ReferenceOpenHashMap<>(); +- private final Map entityById = Int2ReferenceMaps.synchronize(new Int2ReferenceOpenHashMap<>()); +- private final Object2ReferenceMap entityByUUID = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenHashMap<>()); ++ private final Int2ReferenceMap entityById = new Int2ReferenceArrayMap<>(); ++ private final Object2ReferenceMap entityByUUID = new Object2ReferenceArrayMap<>(); private final EntityList accessibleEntities = new EntityList(); public EntityLookup(final ServerLevel world, final LevelCallback worldCallback) { -@@ -105,7 +111,6 @@ public final class EntityLookup implements LevelEntityGetter { +@@ -108,7 +114,6 @@ public final class EntityLookup implements LevelEntityGetter { 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 { +@@ -169,12 +174,12 @@ public final class EntityLookup implements LevelEntityGetter { } @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 { - } - - @Override -- public void remove() { -+ public synchronized void remove() { - throw new UnsupportedOperationException(); - } - } -@@ -208,8 +213,8 @@ public final class EntityLookup implements LevelEntityGetter { - public void get(final AABB box, final Consumer action) { - List 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 { - public void get(final EntityTypeTest filter, final AABB box, final AbortableIterationConsumer action) { - List 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 { +@@ -231,75 +236,50 @@ public final class EntityLookup implements LevelEntityGetter { 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 { +@@ -349,11 +329,6 @@ public final class EntityLookup implements LevelEntityGetter { 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 { +@@ -401,7 +376,14 @@ public final class EntityLookup implements LevelEntityGetter { 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 { +@@ -444,7 +426,15 @@ public final class EntityLookup implements LevelEntityGetter { // 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 { +@@ -612,7 +602,7 @@ public final class EntityLookup implements LevelEntityGetter { continue; } @@ -477,7 +505,7 @@ index 61c170555c8854b102c640b0b6a615f9f732edbf..ec8982b89cde24eefd3d392eed3e6cc9 } } } -@@ -660,21 +641,24 @@ public final class EntityLookup implements LevelEntityGetter { +@@ -663,18 +653,22 @@ public final class EntityLookup implements LevelEntityGetter { 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 { + this.removeChunk(chunkX, chunkZ); +@@ -702,27 +696,7 @@ public final class EntityLookup implements LevelEntityGetter { 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 { +@@ -733,12 +707,7 @@ public final class EntityLookup implements LevelEntityGetter { 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 { +@@ -752,12 +721,7 @@ public final class EntityLookup implements LevelEntityGetter { } 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 { +@@ -834,9 +798,11 @@ public final class EntityLookup implements LevelEntityGetter { 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, 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 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 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 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, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { +- final Reference2ObjectMap.Entry, 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, 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, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { +- final Reference2ObjectMap.Entry, 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, 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 into, final Predicate 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 into, final Predicate 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 into, final Predicate 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 void getEntities(final EntityType type, final AABB box, final List into, + final Predicate 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 clazz) { +@@ -288,12 +411,28 @@ public final class ChunkEntitySlices { + + public void getEntities(final Class clazz, final Entity except, final AABB box, final List into, + final Predicate 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 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 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 into, final Predicate predicate) { ++ public void getEntities(final Entity except, final AABB box, final List into, final Predicate 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 into, ++ public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List into, + final Predicate 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 into, ++ public void getEntitiesWithEnderDragonParts(final Entity except, final Class clazz, final AABB box, final List into, + final Predicate 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 void getEntities(final EntityType type, final AABB box, final List into, ++ public void getEntities(final EntityType type, final AABB box, final List into, + final Predicate 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); + } + } + } diff --git a/patches/server/0044-Hearse-Base-codes.patch b/patches/server/0044-Hearse-Base-codes.patch deleted file mode 100644 index 8a3b52b3..00000000 --- a/patches/server/0044-Hearse-Base-codes.patch +++ /dev/null @@ -1,685 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 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 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 "); -+ } -+ -+ @Override -+ public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException { -+ final List ret = new ArrayList<>(); -+ if (args.length == 1){ -+ ret.add("status"); -+ ret.add("setThreadCount"); -+ ret.add("forceStop"); -+ } -+ if (args.length == 2){ -+ for (Map.Entry 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 taskEntries = new ConcurrentLinkedQueue<>(); -+ -+ public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue 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 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 createdThreads = ObjectLists.synchronize(new ObjectArrayList<>()); -+ -+ public DefaultWorkerFactory(String bound){ -+ poolId.getAndIncrement(); -+ this.bound = bound; -+ } -+ -+ public List 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 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 getManagedWorkers() { -+ return Maps.newHashMap(this.managedWorkers); -+ } -+ -+ @Deprecated -+ public WorkerThreadPoolExecutor getTargetWorker(String bound){ -+ return this.managedWorkers.get(bound); -+ } -+ -+ public Map> shutdownAllNow(){ -+ final Map> ret = Maps.newHashMap(); -+ for (Map.Entry entry : this.managedWorkers.entrySet()){ -+ final String workerName = entry.getKey(); -+ final WorkerThreadPoolExecutor worker = entry.getValue(); -+ if (!worker.isShutdown()){ -+ try { -+ final List 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 implements FeatureElement, EntityTypeT - return (EntityType) Registry.register(BuiltInRegistries.ENTITY_TYPE, id, (EntityType) 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 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)); diff --git a/patches/server/0044-MikuServer-Code-error-fix.patch b/patches/server/0044-MikuServer-Code-error-fix.patch new file mode 100644 index 00000000..e00d59b3 --- /dev/null +++ b/patches/server/0044-MikuServer-Code-error-fix.patch @@ -0,0 +1,306 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +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 queuedCoordinates = new ConcurrentLinkedDeque<>(); ++ public final Deque 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; diff --git a/patches/server/0045-Hearse-Add-mcmt-collections.patch b/patches/server/0045-Hearse-Add-mcmt-collections.patch deleted file mode 100644 index 33144433..00000000 --- a/patches/server/0045-Hearse-Add-mcmt-collections.patch +++ /dev/null @@ -1,4502 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -Date: Sun, 15 Jan 2023 09:56:52 +0800 -Subject: [PATCH] Hearse: Add mcmt collections - -Original license: MIT -Original project: https://github.com/Era4FunMC/Hearse - -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3c29129dc02ddcfaad026d1f81e5da879a0d64cb ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java -@@ -0,0 +1,4 @@ -+package net.himeki.mcmtfabric.parallelised; -+ -+public class ConcurrentArrayDeque { -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java -new file mode 100644 -index 0000000000000000000000000000000000000000..67dd5fe624fe4428d8907000cb23a33485fd6bd9 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java -@@ -0,0 +1,41 @@ -+package net.himeki.mcmtfabric.parallelised; -+ -+import org.apache.logging.log4j.LogManager; -+import org.apache.logging.log4j.Logger; -+ -+import java.util.*; -+import java.util.concurrent.ConcurrentHashMap; -+import java.util.concurrent.ConcurrentLinkedDeque; -+import java.util.concurrent.CopyOnWriteArrayList; -+import java.util.stream.Collector; -+import java.util.stream.Collectors; -+ -+public class ConcurrentCollections { -+ -+ private static final Logger LOGGER = LogManager.getLogger(); -+ -+ public static Set newHashSet() { -+ //LOGGER.info("Concurrent hash set created"); -+ return Collections.newSetFromMap(new ConcurrentHashMap()); -+ } -+ -+ public static Map newHashMap() { -+ //LOGGER.info("Concurrent hash map created"); -+ return new ConcurrentHashMap(); -+ } -+ -+ public static List newLinkedList() { -+ LOGGER.info("Concurrent \"linked\" list created"); -+ return new CopyOnWriteArrayList(); -+ } -+ -+ public static Collector> toList() { -+ return Collectors.toCollection(CopyOnWriteArrayList::new); -+ } -+ -+ public static Queue newArrayDeque() { -+ LOGGER.info("Concurrent \"array\" deque created"); -+ return new ConcurrentLinkedDeque(); -+ } -+ -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fec1f280c72c5b519173017877812ec3f7149ec5 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java -@@ -0,0 +1,937 @@ -+package net.himeki.mcmtfabric.parallelised; -+ -+/* -+ * From: http://www.java2s.com/Code/Java/Collections-Data-Structure/ConcurrentDoublyLinkedList.htm -+ * -+ * Written by Doug Lea with assistance from members of JCP JSR-166 -+ * Expert Group and released to the public domain, as explained at -+ * http://creativecommons.org/licenses/publicdomain -+ * -+ * Modified to actually implement List -+ */ -+ -+import org.apache.commons.lang3.NotImplementedException; -+ -+import java.util.*; -+import java.util.concurrent.atomic.AtomicReference; -+ -+/** -+ * A concurrent linked-list implementation of a {@link Deque} (double-ended -+ * queue). Concurrent insertion, removal, and access operations execute safely -+ * across multiple threads. Iterators are weakly consistent, returning -+ * elements reflecting the state of the deque at some point at or since the -+ * creation of the iterator. They do not throw -+ * {@link ConcurrentModificationException}, and may proceed concurrently with -+ * other operations. -+ * -+ *

-+ * This class and its iterators implement all of the optional methods -+ * of the {@link Collection} and {@link Iterator} interfaces. Like most other -+ * concurrent collection implementations, this class does not permit the use of -+ * null elements. because some null arguments and return values cannot -+ * be reliably distinguished from the absence of elements. Arbitrarily, the -+ * {@link Collection#remove} method is mapped to removeFirstOccurrence, -+ * and {@link Collection#add} is mapped to addLast. -+ * -+ *

-+ * Beware that, unlike in most collections, the size method is -+ * NOT a constant-time operation. Because of the asynchronous nature of -+ * these deques, determining the current number of elements requires a traversal -+ * of the elements. -+ * -+ *

-+ * This class is Serializable, but relies on default serialization -+ * mechanisms. Usually, it is a better idea for any serializable class using a -+ * ConcurrentLinkedDeque to instead serialize a snapshot of the -+ * elements obtained by method toArray. -+ * -+ * @author Doug Lea -+ * @param the type of elements held in this collection -+ */ -+ -+public class ConcurrentDoublyLinkedList extends AbstractCollection implements List, java.io.Serializable { -+ -+ /* -+ * This is an adaptation of an algorithm described in Paul Martin's "A Practical -+ * Lock-Free Doubly-Linked List". Sun Labs Tech report. The basic idea is to -+ * primarily rely on next-pointers to ensure consistency. Prev-pointers are in -+ * part optimistic, reconstructed using forward pointers as needed. The main -+ * forward list uses a variant of HM-list algorithm similar to the one used in -+ * ConcurrentSkipListMap class, but a little simpler. It is also basically -+ * similar to the approach in Edya Ladan-Mozes and Nir Shavit "An Optimistic -+ * Approach to Lock-Free FIFO Queues" in DISC04. -+ * -+ * Quoting a summary in Paul Martin's tech report: -+ * -+ * All cleanups work to maintain these invariants: (1) forward pointers are the -+ * ground truth. (2) forward pointers to dead nodes can be improved by swinging -+ * them further forward around the dead node. (2.1) forward pointers are still -+ * correct when pointing to dead nodes, and forward pointers from dead nodes are -+ * left as they were when the node was deleted. (2.2) multiple dead nodes may -+ * point forward to the same node. (3) backward pointers were correct when they -+ * were installed (3.1) backward pointers are correct when pointing to any node -+ * which points forward to them, but since more than one forward pointer may -+ * point to them, the live one is best. (4) backward pointers that are out of -+ * date due to deletion point to a deleted node, and need to point further back -+ * until they point to the live node that points to their source. (5) backward -+ * pointers that are out of date due to insertion point too far backwards, so -+ * shortening their scope (by searching forward) fixes them. (6) backward -+ * pointers from a dead node cannot be "improved" since there may be no live -+ * node pointing forward to their origin. (However, it does no harm to try to -+ * improve them while racing with a deletion.) -+ * -+ * -+ * Notation guide for local variables n, b, f : a node, its predecessor, and -+ * successor s : some other successor -+ */ -+ -+ // Minor convenience utilities -+ -+ /** -+ * Returns true if given reference is non-null and isn't a header, trailer, or -+ * marker. -+ * -+ * @param n (possibly null) node -+ * @return true if n exists as a user node -+ */ -+ private static boolean usable(Node n) { -+ return n != null && !n.isSpecial(); -+ } -+ -+ /** -+ * Throws NullPointerException if argument is null -+ * -+ * @param v the element -+ */ -+ private static void checkNullArg(Object v) { -+ if (v == null) -+ throw new NullPointerException(); -+ } -+ -+ /** -+ * Returns element unless it is null, in which case throws -+ * NoSuchElementException. -+ * -+ * @param v the element -+ * @return the element -+ */ -+ private E screenNullResult(E v) { -+ if (v == null) -+ throw new NoSuchElementException(); -+ return v; -+ } -+ -+ /** -+ * Creates an array list and fills it with elements of this list. Used by -+ * toArray. -+ * -+ * @return the arrayList -+ */ -+ private ArrayList toArrayList() { -+ ArrayList c = new ArrayList(); -+ for (Node n = header.forward(); n != null; n = n.forward()) -+ c.add(n.element); -+ return c; -+ } -+ -+ // Fields and constructors -+ -+ private static final long serialVersionUID = 876323262645176354L; -+ -+ /** -+ * List header. First usable node is at header.forward(). -+ */ -+ private final Node header; -+ -+ /** -+ * List trailer. Last usable node is at trailer.back(). -+ */ -+ private final Node trailer; -+ -+ /** -+ * Constructs an empty deque. -+ */ -+ public ConcurrentDoublyLinkedList() { -+ Node h = new Node(null, null, null); -+ Node t = new Node(null, null, h); -+ h.setNext(t); -+ header = h; -+ trailer = t; -+ } -+ -+ /** -+ * Constructs a deque containing the elements of the specified collection, in -+ * the order they are returned by the collection's iterator. -+ * -+ * @param c the collection whose elements are to be placed into this deque. -+ * @throws NullPointerException if c or any element within it is -+ * null -+ */ -+ public ConcurrentDoublyLinkedList(Collection c) { -+ this(); -+ addAll(c); -+ } -+ -+ /** -+ * Prepends the given element at the beginning of this deque. -+ * -+ * @param o the element to be inserted at the beginning of this deque. -+ * @throws NullPointerException if the specified element is null -+ */ -+ public void addFirst(E o) { -+ checkNullArg(o); -+ while (header.append(o) == null) -+ ; -+ } -+ -+ /** -+ * Appends the given element to the end of this deque. This is identical in -+ * function to the add method. -+ * -+ * @param o the element to be inserted at the end of this deque. -+ * @throws NullPointerException if the specified element is null -+ */ -+ public void addLast(E o) { -+ checkNullArg(o); -+ while (trailer.prepend(o) == null) -+ ; -+ } -+ -+ /** -+ * Prepends the given element at the beginning of this deque. -+ * -+ * @param o the element to be inserted at the beginning of this deque. -+ * @return true always -+ * @throws NullPointerException if the specified element is null -+ */ -+ public boolean offerFirst(E o) { -+ addFirst(o); -+ return true; -+ } -+ -+ /** -+ * Appends the given element to the end of this deque. (Identical in function to -+ * the add method; included only for consistency.) -+ * -+ * @param o the element to be inserted at the end of this deque. -+ * @return true always -+ * @throws NullPointerException if the specified element is null -+ */ -+ public boolean offerLast(E o) { -+ addLast(o); -+ return true; -+ } -+ -+ /** -+ * Retrieves, but does not remove, the first element of this deque, or returns -+ * null if this deque is empty. -+ * -+ * @return the first element of this queue, or null if empty. -+ */ -+ public E peekFirst() { -+ Node n = header.successor(); -+ return (n == null) ? null : n.element; -+ } -+ -+ /** -+ * Retrieves, but does not remove, the last element of this deque, or returns -+ * null if this deque is empty. -+ * -+ * @return the last element of this deque, or null if empty. -+ */ -+ public E peekLast() { -+ Node n = trailer.predecessor(); -+ return (n == null) ? null : n.element; -+ } -+ -+ /** -+ * Returns the first element in this deque. -+ * -+ * @return the first element in this deque. -+ * @throws NoSuchElementException if this deque is empty. -+ */ -+ public E getFirst() { -+ return screenNullResult(peekFirst()); -+ } -+ -+ /** -+ * Returns the last element in this deque. -+ * -+ * @return the last element in this deque. -+ * @throws NoSuchElementException if this deque is empty. -+ */ -+ public E getLast() { -+ return screenNullResult(peekLast()); -+ } -+ -+ /** -+ * Retrieves and removes the first element of this deque, or returns null if -+ * this deque is empty. -+ * -+ * @return the first element of this deque, or null if empty. -+ */ -+ public E pollFirst() { -+ for (;;) { -+ Node n = header.successor(); -+ if (!usable(n)) -+ return null; -+ if (n.delete()) -+ return n.element; -+ } -+ } -+ -+ /** -+ * Retrieves and removes the last element of this deque, or returns null if this -+ * deque is empty. -+ * -+ * @return the last element of this deque, or null if empty. -+ */ -+ public E pollLast() { -+ for (;;) { -+ Node n = trailer.predecessor(); -+ if (!usable(n)) -+ return null; -+ if (n.delete()) -+ return n.element; -+ } -+ } -+ -+ /** -+ * Removes and returns the first element from this deque. -+ * -+ * @return the first element from this deque. -+ * @throws NoSuchElementException if this deque is empty. -+ */ -+ public E removeFirst() { -+ return screenNullResult(pollFirst()); -+ } -+ -+ /** -+ * Removes and returns the last element from this deque. -+ * -+ * @return the last element from this deque. -+ * @throws NoSuchElementException if this deque is empty. -+ */ -+ public E removeLast() { -+ return screenNullResult(pollLast()); -+ } -+ -+ // *** Queue and stack methods *** -+ public boolean offer(E e) { -+ return offerLast(e); -+ } -+ -+ public boolean add(E e) { -+ return offerLast(e); -+ } -+ -+ public E poll() { -+ return pollFirst(); -+ } -+ -+ public E remove() { -+ return removeFirst(); -+ } -+ -+ public E peek() { -+ return peekFirst(); -+ } -+ -+ public E element() { -+ return getFirst(); -+ } -+ -+ public void push(E e) { -+ addFirst(e); -+ } -+ -+ public E pop() { -+ return removeFirst(); -+ } -+ -+ /** -+ * Removes the first element e such that o.equals(e), if such -+ * an element exists in this deque. If the deque does not contain the element, -+ * it is unchanged. -+ * -+ * @param o element to be removed from this deque, if present. -+ * @return true if the deque contained the specified element. -+ * @throws NullPointerException if the specified element is null -+ */ -+ public boolean removeFirstOccurrence(Object o) { -+ checkNullArg(o); -+ for (;;) { -+ Node n = header.forward(); -+ for (;;) { -+ if (n == null) -+ return false; -+ if (o.equals(n.element)) { -+ if (n.delete()) -+ return true; -+ else -+ break; // restart if interference -+ } -+ n = n.forward(); -+ } -+ } -+ } -+ -+ /** -+ * Removes the last element e such that o.equals(e), if such -+ * an element exists in this deque. If the deque does not contain the element, -+ * it is unchanged. -+ * -+ * @param o element to be removed from this deque, if present. -+ * @return true if the deque contained the specified element. -+ * @throws NullPointerException if the specified element is null -+ */ -+ public boolean removeLastOccurrence(Object o) { -+ checkNullArg(o); -+ for (;;) { -+ Node s = trailer; -+ for (;;) { -+ Node n = s.back(); -+ if (s.isDeleted() || (n != null && n.successor() != s)) -+ break; // restart if pred link is suspect. -+ if (n == null) -+ return false; -+ if (o.equals(n.element)) { -+ if (n.delete()) -+ return true; -+ else -+ break; // restart if interference -+ } -+ s = n; -+ } -+ } -+ } -+ -+ /** -+ * Returns true if this deque contains at least one element e -+ * such that o.equals(e). -+ * -+ * @param o element whose presence in this deque is to be tested. -+ * @return true if this deque contains the specified element. -+ */ -+ public boolean contains(Object o) { -+ if (o == null) -+ return false; -+ for (Node n = header.forward(); n != null; n = n.forward()) -+ if (o.equals(n.element)) -+ return true; -+ return false; -+ } -+ -+ /** -+ * Returns true if this collection contains no elements. -+ *

-+ * -+ * @return true if this collection contains no elements. -+ */ -+ public boolean isEmpty() { -+ return !usable(header.successor()); -+ } -+ -+ /** -+ * Returns the number of elements in this deque. If this deque contains more -+ * than Integer.MAX_VALUE elements, it returns -+ * Integer.MAX_VALUE. -+ * -+ *

-+ * Beware that, unlike in most collections, this method is NOT a -+ * constant-time operation. Because of the asynchronous nature of these deques, -+ * determining the current number of elements requires traversing them all to -+ * count them. Additionally, it is possible for the size to change during -+ * execution of this method, in which case the returned result will be -+ * inaccurate. Thus, this method is typically not very useful in concurrent -+ * applications. -+ * -+ * @return the number of elements in this deque. -+ */ -+ public int size() { -+ long count = 0; -+ for (Node n = header.forward(); n != null; n = n.forward()) -+ ++count; -+ return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count; -+ } -+ -+ /** -+ * Removes the first element e such that o.equals(e), if such -+ * an element exists in this deque. If the deque does not contain the element, -+ * it is unchanged. -+ * -+ * @param o element to be removed from this deque, if present. -+ * @return true if the deque contained the specified element. -+ * @throws NullPointerException if the specified element is null -+ */ -+ public boolean remove(Object o) { -+ return removeFirstOccurrence(o); -+ } -+ -+ /** -+ * Appends all of the elements in the specified collection to the end of this -+ * deque, in the order that they are returned by the specified collection's -+ * iterator. The behavior of this operation is undefined if the specified -+ * collection is modified while the operation is in progress. (This implies that -+ * the behavior of this call is undefined if the specified Collection is this -+ * deque, and this deque is nonempty.) -+ * -+ * @param c the elements to be inserted into this deque. -+ * @return true if this deque changed as a result of the call. -+ * @throws NullPointerException if c or any element within it is -+ * null -+ */ -+ public boolean addAll(Collection c) { -+ Iterator it = c.iterator(); -+ if (!it.hasNext()) -+ return false; -+ do { -+ addLast(it.next()); -+ } while (it.hasNext()); -+ return true; -+ } -+ -+ /** -+ * Removes all of the elements from this deque. -+ */ -+ public void clear() { -+ while (pollFirst() != null) -+ ; -+ } -+ -+ /** -+ * Returns an array containing all of the elements in this deque in the correct -+ * order. -+ * -+ * @return an array containing all of the elements in this deque in the correct -+ * order. -+ */ -+ public Object[] toArray() { -+ return toArrayList().toArray(); -+ } -+ -+ /** -+ * Returns an array containing all of the elements in this deque in the correct -+ * order; the runtime type of the returned array is that of the specified array. -+ * If the deque fits in the specified array, it is returned therein. Otherwise, -+ * a new array is allocated with the runtime type of the specified array and the -+ * size of this deque. -+ *

-+ * -+ * If the deque fits in the specified array with room to spare (i.e., the array -+ * has more elements than the deque), the element in the array immediately -+ * following the end of the collection is set to null. This is useful in -+ * determining the length of the deque only if the caller knows that the -+ * deque does not contain any null elements. -+ * -+ * @param a the array into which the elements of the deque are to be stored, if -+ * it is big enough; otherwise, a new array of the same runtime type is -+ * allocated for this purpose. -+ * @return an array containing the elements of the deque. -+ * @throws ArrayStoreException if the runtime type of a is not a supertype of -+ * the runtime type of every element in this deque. -+ * @throws NullPointerException if the specified array is null. -+ */ -+ public T[] toArray(T[] a) { -+ return toArrayList().toArray(a); -+ } -+ -+ /** -+ * Returns a weakly consistent iterator over the elements in this deque, in -+ * first-to-last order. The next method returns elements reflecting the -+ * state of the deque at some point at or since the creation of the iterator. -+ * The method does not throw {@link ConcurrentModificationException}, -+ * and may proceed concurrently with other operations. -+ * -+ * @return an iterator over the elements in this deque -+ */ -+ public Iterator iterator() { -+ return new CLDIterator(); -+ } -+ -+ final class CLDIterator implements Iterator { -+ Node last; -+ -+ Node next = header.forward(); -+ -+ public boolean hasNext() { -+ return next != null; -+ } -+ -+ public E next() { -+ Node l = last = next; -+ if (l == null) -+ throw new NoSuchElementException(); -+ next = next.forward(); -+ return l.element; -+ } -+ -+ public void remove() { -+ Node l = last; -+ if (l == null) -+ throw new IllegalStateException(); -+ while (!l.delete() && !l.isDeleted()) -+ ; -+ } -+ } -+ -+ @Override -+ public boolean addAll(int index, Collection c) { -+ throw new NotImplementedException("TODO"); -+ } -+ -+ @Override -+ public E get(int index) { -+ Node current = header.successor(); -+ if (current == null) { -+ throw new IndexOutOfBoundsException(); -+ } -+ for (; index > 0; index --) { -+ current = current.successor(); -+ if (current == null) { -+ throw new IndexOutOfBoundsException(); -+ } -+ } -+ return current.element; -+ } -+ -+ @Override -+ public E set(int index, E element) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public void add(int index, E element) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public E remove(int index) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public int indexOf(Object o) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public int lastIndexOf(Object o) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public ListIterator listIterator() { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public ListIterator listIterator(int index) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public List subList(int fromIndex, int toIndex) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+} -+ -+/** -+ * Linked Nodes. As a minor efficiency hack, this class opportunistically -+ * inherits from AtomicReference, with the atomic ref used as the "next" link. -+ * -+ * Nodes are in doubly-linked lists. There are three kinds of special nodes, -+ * distinguished by: * The list header has a null prev link * The list trailer -+ * has a null next link * A deletion marker has a prev link pointing to itself. -+ * All three kinds of special nodes have null element fields. -+ * -+ * Regular nodes have non-null element, next, and prev fields. To avoid visible -+ * inconsistencies when deletions overlap element replacement, replacements are -+ * done by replacing the node, not just setting the element. -+ * -+ * Nodes can be traversed by read-only ConcurrentLinkedDeque class operations -+ * just by following raw next pointers, so long as they ignore any special nodes -+ * seen along the way. (This is automated in method forward.) However, traversal -+ * using prev pointers is not guaranteed to see all live nodes since a prev -+ * pointer of a deleted node can become unrecoverably stale. -+ */ -+ -+class Node extends AtomicReference> { -+ -+ private static final long serialVersionUID = 6640557564507962862L; -+ -+ private volatile Node prev; -+ -+ final E element; -+ -+ /** Creates a node with given contents */ -+ Node(E element, Node next, Node prev) { -+ super(next); -+ this.prev = prev; -+ this.element = element; -+ } -+ -+ /** Creates a marker node with given successor */ -+ Node(Node next) { -+ super(next); -+ this.prev = this; -+ this.element = null; -+ } -+ -+ /** -+ * Gets next link (which is actually the value held as atomic reference). -+ */ -+ private Node getNext() { -+ return get(); -+ } -+ -+ /** -+ * Sets next link -+ * -+ * @param n the next node -+ */ -+ void setNext(Node n) { -+ set(n); -+ } -+ -+ /** -+ * compareAndSet next link -+ */ -+ private boolean casNext(Node cmp, Node val) { -+ return compareAndSet(cmp, val); -+ } -+ -+ /** -+ * Gets prev link -+ */ -+ private Node getPrev() { -+ return prev; -+ } -+ -+ /** -+ * Sets prev link -+ * -+ * @param b the previous node -+ */ -+ void setPrev(Node b) { -+ prev = b; -+ } -+ -+ /** -+ * Returns true if this is a header, trailer, or marker node -+ */ -+ boolean isSpecial() { -+ return element == null; -+ } -+ -+ /** -+ * Returns true if this is a trailer node -+ */ -+ boolean isTrailer() { -+ return getNext() == null; -+ } -+ -+ /** -+ * Returns true if this is a header node -+ */ -+ boolean isHeader() { -+ return getPrev() == null; -+ } -+ -+ /** -+ * Returns true if this is a marker node -+ */ -+ boolean isMarker() { -+ return getPrev() == this; -+ } -+ -+ /** -+ * Returns true if this node is followed by a marker, meaning that it is -+ * deleted. -+ * -+ * @return true if this node is deleted -+ */ -+ boolean isDeleted() { -+ Node f = getNext(); -+ return f != null && f.isMarker(); -+ } -+ -+ /** -+ * Returns next node, ignoring deletion marker -+ */ -+ private Node nextNonmarker() { -+ Node f = getNext(); -+ return (f == null || !f.isMarker()) ? f : f.getNext(); -+ } -+ -+ /** -+ * Returns the next non-deleted node, swinging next pointer around any -+ * encountered deleted nodes, and also patching up successor''s prev link to -+ * point back to this. Returns null if this node is trailer so has no successor. -+ * -+ * @return successor, or null if no such -+ */ -+ Node successor() { -+ Node f = nextNonmarker(); -+ for (;;) { -+ if (f == null) -+ return null; -+ if (!f.isDeleted()) { -+ if (f.getPrev() != this && !isDeleted()) -+ f.setPrev(this); // relink f's prev -+ return f; -+ } -+ Node s = f.nextNonmarker(); -+ if (f == getNext()) -+ casNext(f, s); // unlink f -+ f = s; -+ } -+ } -+ -+ /** -+ * Returns the apparent predecessor of target by searching forward for it -+ * starting at this node, patching up pointers while traversing. Used by -+ * predecessor(). -+ * -+ * @return target's predecessor, or null if not found -+ */ -+ private Node findPredecessorOf(Node target) { -+ Node n = this; -+ for (;;) { -+ Node f = n.successor(); -+ if (f == target) -+ return n; -+ if (f == null) -+ return null; -+ n = f; -+ } -+ } -+ -+ /** -+ * Returns the previous non-deleted node, patching up pointers as needed. -+ * Returns null if this node is header so has no successor. May also return null -+ * if this node is deleted, so doesn't have a distinct predecessor. -+ * -+ * @return predecessor or null if not found -+ */ -+ Node predecessor() { -+ Node n = this; -+ for (;;) { -+ Node b = n.getPrev(); -+ if (b == null) -+ return n.findPredecessorOf(this); -+ Node s = b.getNext(); -+ if (s == this) -+ return b; -+ if (s == null || !s.isMarker()) { -+ Node p = b.findPredecessorOf(this); -+ if (p != null) -+ return p; -+ } -+ n = b; -+ } -+ } -+ -+ /** -+ * Returns the next node containing a nondeleted user element. Use for forward -+ * list traversal. -+ * -+ * @return successor, or null if no such -+ */ -+ Node forward() { -+ Node f = successor(); -+ return (f == null || f.isSpecial()) ? null : f; -+ } -+ -+ /** -+ * Returns previous node containing a nondeleted user element, if possible. Use -+ * for backward list traversal, but beware that if this method is called from a -+ * deleted node, it might not be able to determine a usable predecessor. -+ * -+ * @return predecessor, or null if no such could be found -+ */ -+ Node back() { -+ Node f = predecessor(); -+ return (f == null || f.isSpecial()) ? null : f; -+ } -+ -+ /** -+ * Tries to insert a node holding element as successor, failing if this node is -+ * deleted. -+ * -+ * @param element the element -+ * @return the new node, or null on failure. -+ */ -+ Node append(E element) { -+ for (;;) { -+ Node f = getNext(); -+ if (f == null || f.isMarker()) -+ return null; -+ Node x = new Node(element, f, this); -+ if (casNext(f, x)) { -+ f.setPrev(x); // optimistically link -+ return x; -+ } -+ } -+ } -+ -+ /** -+ * Tries to insert a node holding element as predecessor, failing if no live -+ * predecessor can be found to link to. -+ * -+ * @param element the element -+ * @return the new node, or null on failure. -+ */ -+ Node prepend(E element) { -+ for (;;) { -+ Node b = predecessor(); -+ if (b == null) -+ return null; -+ Node x = new Node(element, this, b); -+ if (b.casNext(this, x)) { -+ setPrev(x); // optimistically link -+ return x; -+ } -+ } -+ } -+ -+ /** -+ * Tries to mark this node as deleted, failing if already deleted or if this -+ * node is header or trailer -+ * -+ * @return true if successful -+ */ -+ boolean delete() { -+ Node b = getPrev(); -+ Node f = getNext(); -+ if (b != null && f != null && !f.isMarker() && casNext(f, new Node(f))) { -+ if (b.casNext(this, f)) -+ f.setPrev(b); -+ return true; -+ } -+ return false; -+ } -+ -+ /** -+ * Tries to insert a node holding element to replace this node. failing if -+ * already deleted. -+ * -+ * @param newElement the new element -+ * @return the new node, or null on failure. -+ */ -+ Node replace(E newElement) { -+ for (;;) { -+ Node b = getPrev(); -+ Node f = getNext(); -+ if (b == null || f == null || f.isMarker()) -+ return null; -+ Node x = new Node(newElement, f, b); -+ if (casNext(f, new Node(x))) { -+ b.successor(); // to relink b -+ x.successor(); // to relink f -+ return x; -+ } -+ } -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java -new file mode 100644 -index 0000000000000000000000000000000000000000..586b3c87dcf010e2401933c5ec2338f59b9edad3 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java -@@ -0,0 +1,230 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.longs.*; -+ -+import java.util.Collection; -+import java.util.Iterator; -+import java.util.concurrent.ConcurrentSkipListSet; -+ -+public class ConcurrentLongLinkedOpenHashSet extends LongLinkedOpenHashSet { -+ -+ private static final long serialVersionUID = -5532128240738069111L; -+ -+ private final ConcurrentSkipListSet backing; -+ -+ public ConcurrentLongLinkedOpenHashSet() { -+ //backing = new ConcurrentLinkedDeque(); -+ backing = new ConcurrentSkipListSet(); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final int initial) { -+ //backing = new ConcurrentLinkedDeque(); -+ backing = new ConcurrentSkipListSet(); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final int initial, final float dnc) { -+ this(initial); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final LongCollection c) { -+ this(c.size()); -+ addAll(c); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final LongCollection c, final float f) { -+ this(c.size(), f); -+ addAll(c); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final LongIterator i, final float f) { -+ this(16, f); -+ while (i.hasNext()) -+ add(i.nextLong()); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final LongIterator i) { -+ this(i, -1); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final Iterator i, final float f) { -+ this(LongIterators.asLongIterator(i), f); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final Iterator i) { -+ this(LongIterators.asLongIterator(i)); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length, final float f) { -+ this(length < 0 ? 0 : length, f); -+ LongArrays.ensureOffsetLength(a, offset, length); -+ for (int i = 0; i < length; i++) -+ add(a[offset + i]); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length) { -+ this(a, offset, length, DEFAULT_LOAD_FACTOR); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final long[] a, final float f) { -+ this(a, 0, a.length, f); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final long[] a) { -+ this(a, -1); -+ } -+ -+ @Override -+ public boolean add(final long k) { -+ boolean out = backing.add(k); -+ /* -+ if (!firstDef) { -+ first = k; -+ firstDef = true; -+ } -+ last = k; -+ */ -+ return out; -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return addAll((Collection) c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean addAndMoveToFirst(final long k) { -+ boolean out = backing.add(k); -+ //first = k; -+ return out; -+ } -+ -+ @Override -+ public boolean addAndMoveToLast(final long k) { -+ boolean out = backing.add(k); -+ //last = k; -+ return out; -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public LongLinkedOpenHashSet clone() { -+ return new ConcurrentLongLinkedOpenHashSet(backing.iterator()); -+ } -+ -+ @Override -+ public LongComparator comparator() { -+ return null; -+ } -+ -+ @Override -+ public boolean contains(final long k) { -+ return backing.contains(k); -+ } -+ -+ @Override -+ public long firstLong() { -+ /* -+ if (backing.size() == 0) throw new NoSuchElementException(); -+ return first; -+ */ -+ return backing.first(); -+ } -+ -+ @Override -+ public int hashCode() { -+ return backing.hashCode(); -+ } -+ -+ @Override -+ public LongSortedSet headSet(long to) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public LongListIterator iterator() { -+ return FastUtilHackUtil.wrap(backing.iterator()); -+ } -+ -+ @Override -+ public LongListIterator iterator(long from) { -+ throw new IllegalStateException(); -+ //return FastUtilHackUtil.wrap(backing.iterator()); -+ } -+ -+ @Override -+ public long lastLong() { -+ /* -+ if (backing.size() == 0) throw new NoSuchElementException(); -+ return last; -+ */ -+ return backing.last(); -+ } -+ -+ @Override -+ public boolean remove(final long k) { -+ /* -+ if (k == first) { -+ first = backing.iterator().next(); -+ } -+ if (k == last) { -+ last = backing.iterator().next(); -+ } -+ */ -+ return backing.remove(k); -+ } -+ -+ @Override -+ public long removeFirstLong() { -+ long fl = this.firstLong(); -+ this.remove(fl); -+ //first = backing.iterator().next(); -+ return fl; -+ } -+ -+ @Override -+ public long removeLastLong() { -+ long fl = this.lastLong(); -+ this.remove(fl); -+ //last = backing.iterator().next(); -+ return fl; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public LongSortedSet subSet(long from, long to) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongSortedSet tailSet(long from) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public boolean trim() { -+ return true; -+ } -+ -+ @Override -+ public boolean trim(final int n) { -+ return true; -+ } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java -new file mode 100644 -index 0000000000000000000000000000000000000000..93bd066ec2013e42a85fcf21344fe41f3ad69598 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java -@@ -0,0 +1,144 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.longs.*; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.Collection; -+import java.util.concurrent.ConcurrentSkipListSet; -+ -+public class ConcurrentLongSortedSet implements LongSortedSet { -+ -+ ConcurrentSkipListSet back = new ConcurrentSkipListSet<>(); -+ -+ @Override -+ public LongBidirectionalIterator iterator(long fromElement) { -+ return null; -+ } -+ -+ @Override -+ public int size() { -+ return back.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return back.isEmpty(); -+ } -+ -+ @Override -+ public LongBidirectionalIterator iterator() { -+ return null; -+ } -+ -+ @NotNull -+ @Override -+ public Object[] toArray() { -+ return back.toArray(); -+ } -+ -+ @NotNull -+ @Override -+ public T[] toArray(@NotNull T[] ts) { -+ return null; -+ } -+ -+ @Override -+ public boolean containsAll(@NotNull Collection collection) { -+ return back.containsAll(collection); -+ } -+ -+ @Override -+ public boolean addAll(@NotNull Collection collection) { -+ return back.addAll(collection); -+ } -+ -+ @Override -+ public boolean removeAll(@NotNull Collection collection) { -+ return back.removeAll(collection); -+ } -+ -+ @Override -+ public boolean retainAll(@NotNull Collection collection) { -+ return back.retainAll(collection); -+ } -+ -+ @Override -+ public void clear() { -+ back.clear(); -+ } -+ -+ @Override -+ public boolean add(long key) { -+ return back.add(key); -+ } -+ -+ @Override -+ public boolean contains(long key) { -+ return back.contains(key); -+ } -+ -+ @Override -+ public long[] toLongArray() { -+ return new long[0]; -+ } -+ -+ @Override -+ public long[] toArray(long[] a) { -+ return new long[0]; -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return back.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(LongCollection c) { -+ return back.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(LongCollection c) { -+ return back.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(LongCollection c) { -+ return back.retainAll(c); -+ } -+ -+ @Override -+ public boolean remove(long k) { -+ return back.remove(k); -+ } -+ -+ @Override -+ public LongSortedSet subSet(long fromElement, long toElement) { -+ return new LongAVLTreeSet(back.subSet(fromElement,toElement)); -+ } -+ -+ @Override -+ public LongSortedSet headSet(long toElement) { -+ return new LongAVLTreeSet(back.headSet(toElement)); -+ } -+ -+ @Override -+ public LongSortedSet tailSet(long fromElement) { -+ return new LongAVLTreeSet(back.tailSet(fromElement)); -+ } -+ -+ @Override -+ public LongComparator comparator() { -+ return null; -+ } -+ -+ @Override -+ public long firstLong() { -+ return back.first(); -+ } -+ -+ @Override -+ public long lastLong() { -+ return back.last(); -+ } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ff1a4f87356459d3bc990a77c3081932046da5b1 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java -@@ -0,0 +1,112 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.shorts.ShortCollection; -+import it.unimi.dsi.fastutil.shorts.ShortIterator; -+import it.unimi.dsi.fastutil.shorts.ShortSet; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.Collection; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class ConcurrentShortHashSet implements ShortSet { -+ -+ ConcurrentHashMap.KeySetView backing = ConcurrentHashMap.newKeySet(); -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public ShortIterator iterator() { -+ return new FastUtilHackUtil.WrappingShortIterator(backing.iterator()); -+ } -+ -+ @NotNull -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @NotNull -+ @Override -+ public T[] toArray(@NotNull T[] ts) { -+ return (T[]) backing.toArray(); -+ } -+ -+ @Override -+ public boolean containsAll(@NotNull Collection collection) { -+ return backing.containsAll(collection); -+ } -+ -+ @Override -+ public boolean addAll(@NotNull Collection collection) { -+ return backing.addAll(collection); -+ } -+ -+ @Override -+ public boolean removeAll(@NotNull Collection collection) { -+ return backing.removeAll(collection); -+ } -+ -+ @Override -+ public boolean retainAll(@NotNull Collection collection) { -+ return backing.retainAll(collection); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ -+ } -+ -+ @Override -+ public boolean add(short key) { -+ return backing.add(key); -+ } -+ -+ @Override -+ public boolean contains(short key) { -+ return backing.contains(key); -+ } -+ -+ @Override -+ public short[] toShortArray() { -+ return new short[0]; -+ } -+ -+ @Override -+ public short[] toArray(short[] a) { -+ return new short[0]; -+ } -+ -+ @Override -+ public boolean addAll(ShortCollection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(ShortCollection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(ShortCollection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(ShortCollection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public boolean remove(short k) { -+ return backing.remove(k); -+ } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ca1fd9ca6f5c5d5d4a0f374440ccaf80347fb781 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java -@@ -0,0 +1,1678 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.bytes.ByteCollection; -+import it.unimi.dsi.fastutil.bytes.ByteIterator; -+import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; -+import it.unimi.dsi.fastutil.ints.IntCollection; -+import it.unimi.dsi.fastutil.ints.IntIterator; -+import it.unimi.dsi.fastutil.ints.IntSet; -+import it.unimi.dsi.fastutil.longs.*; -+import it.unimi.dsi.fastutil.objects.ObjectCollection; -+import it.unimi.dsi.fastutil.objects.ObjectIterator; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+import it.unimi.dsi.fastutil.shorts.ShortIterator; -+import org.apache.commons.lang3.ArrayUtils; -+ -+import java.util.*; -+import java.util.function.Function; -+import java.util.stream.Collectors; -+ -+public class FastUtilHackUtil { -+ -+ public static class ConvertingObjectSet implements ObjectSet { -+ -+ Set backing; -+ Function forward; -+ Function back; -+ -+ public ConvertingObjectSet(Set backing, Function forward, Function back) { -+ this.backing = backing; -+ this.forward = forward; -+ this.back = back; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean contains(Object o) { -+ try { -+ return backing.contains(back.apply((T) o)); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.stream().map(forward).toArray(); -+ } -+ -+ @Override -+ public R[] toArray(R[] a) { -+ return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); -+ } -+ -+ @Override -+ public boolean add(T e) { -+ return backing.add(back.apply(e)); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean remove(Object o) { -+ try { -+ return backing.remove(back.apply((T) o)); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean containsAll(Collection c) { -+ try { -+ return backing.containsAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c.stream().map(i -> back.apply(i)).collect(Collectors.toSet())); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean removeAll(Collection c) { -+ try { -+ return backing.removeAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean retainAll(Collection c) { -+ try { -+ return backing.retainAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ -+ } -+ -+ @Override -+ public ObjectIterator iterator() { -+ final Iterator backg = backing.iterator(); -+ return new ObjectIterator() { -+ -+ @Override -+ public boolean hasNext() { -+ return backg.hasNext(); -+ } -+ -+ @Override -+ public T next() { -+ return forward.apply(backg.next()); -+ } -+ -+ @Override -+ public void remove() { -+ backg.remove(); -+ } -+ }; -+ } -+ -+ -+ } -+ -+ public static class ConvertingObjectSetFast implements Long2ObjectMap.FastEntrySet { -+ -+ Set backing; -+ Function> forward; -+ Function, E> back; -+ -+ public ConvertingObjectSetFast(Set backing, -+ Function> forward, -+ Function, E> back) { -+ this.backing = backing; -+ this.forward = forward; -+ this.back = back; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean contains(Object o) { -+ try { -+ return backing.contains(back.apply((Long2ObjectMap.Entry)o)); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.stream().map(forward).toArray(); -+ } -+ -+ @Override -+ public R[] toArray(R[] a) { -+ return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean remove(Object o) { -+ try { -+ return backing.remove(back.apply((Long2ObjectMap.Entry)o)); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean containsAll(Collection c) { -+ try { -+ return backing.containsAll(c.stream() -+ .map(i -> back.apply((Long2ObjectMap.Entry) i)) -+ .collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean removeAll(Collection c) { -+ try { -+ return backing.removeAll(c.stream().map(i -> back -+ .apply((Long2ObjectMap.Entry) i)) -+ .collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean retainAll(Collection c) { -+ try { -+ return backing.retainAll(c.stream() -+ .map(i -> back.apply((Long2ObjectMap.Entry) i)) -+ .collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ -+ } -+ -+ @Override -+ public ObjectIterator> iterator() { -+ final Iterator backg = backing.iterator(); -+ return new ObjectIterator>() { -+ -+ @Override -+ public boolean hasNext() { -+ return backg.hasNext(); -+ } -+ -+ @Override -+ public Long2ObjectMap.Entry next() { -+ return forward.apply(backg.next()); -+ } -+ -+ @Override -+ public void remove() { -+ backg.remove(); -+ } -+ }; -+ } -+ -+ @Override -+ public boolean add(Long2ObjectMap.Entry e) { -+ return backing.add(back.apply(e)); -+ } -+ -+ @Override -+ public boolean addAll(Collection> c) { -+ return backing.addAll(c.stream().map(back).collect(Collectors.toList())); -+ } -+ -+ @Override -+ public ObjectIterator> fastIterator() { -+ return iterator(); -+ } -+ -+ -+ } -+ -+ private static Entry intEntryForwards(Map.Entry entry) { -+ return new Entry() { -+ -+ @Override -+ public T getValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public T setValue(T value) { -+ return entry.setValue(value); -+ } -+ -+ @Override -+ public int getIntKey() { -+ return entry.getKey(); -+ } -+ -+ @Override -+ public boolean equals(Object obj) { -+ if (obj == entry) { -+ return true; -+ } -+ return super.equals(obj); -+ } -+ -+ @Override -+ public int hashCode() { -+ return entry.hashCode(); -+ } -+ }; -+ } -+ -+ private static Map.Entry intEntryBackwards(Entry entry) { -+ return entry; -+ } -+ -+ private static Long2ObjectMap.Entry longEntryForwards(Map.Entry entry) { -+ return new Long2ObjectMap.Entry() { -+ -+ @Override -+ public T getValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public T setValue(T value) { -+ return entry.setValue(value); -+ } -+ -+ @Override -+ public long getLongKey() { -+ return entry.getKey(); -+ } -+ -+ @Override -+ public boolean equals(Object obj) { -+ if (obj == entry) { -+ return true; -+ } -+ return super.equals(obj); -+ } -+ -+ @Override -+ public int hashCode() { -+ return entry.hashCode(); -+ } -+ }; -+ } -+ -+ private static Map.Entry longEntryBackwards(Long2ObjectMap.Entry entry) { -+ return entry; -+ } -+ -+ private static Long2ByteMap.Entry longByteEntryForwards(Map.Entry entry) { -+ return new Long2ByteMap.Entry() { -+ -+ @Override -+ public Byte getValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public byte setValue(byte value) { -+ return entry.setValue(value); -+ } -+ -+ @Override -+ public byte getByteValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public long getLongKey() { -+ return entry.getKey(); -+ } -+ -+ @Override -+ public boolean equals(Object obj) { -+ if (obj == entry) { -+ return true; -+ } -+ return super.equals(obj); -+ } -+ -+ @Override -+ public int hashCode() { -+ return entry.hashCode(); -+ } -+ -+ }; -+ } -+ -+ private static Map.Entry longByteEntryBackwards(Long2ByteMap.Entry entry) { -+ return entry; -+ } -+ -+ private static Long2LongMap.Entry longLongEntryForwards(Map.Entry entry) { -+ return new Long2LongMap.Entry() { -+ -+ @Override -+ public Long getValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public long setValue(long value) { -+ return entry.setValue(value); -+ } -+ -+ @Override -+ public long getLongValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public long getLongKey() { -+ return entry.getKey(); -+ } -+ -+ @Override -+ public boolean equals(Object obj) { -+ if (obj == entry) { -+ return true; -+ } -+ return super.equals(obj); -+ } -+ -+ @Override -+ public int hashCode() { -+ return entry.hashCode(); -+ } -+ -+ }; -+ } -+ -+ private static Map.Entry longLongEntryBackwards(Long2LongMap.Entry entry) { -+ return entry; -+ } -+ -+ public static ObjectSet> entrySetIntWrap(Map map) { -+ return new ConvertingObjectSet, Entry>(map.entrySet(), FastUtilHackUtil::intEntryForwards, FastUtilHackUtil::intEntryBackwards); -+ } -+ -+ public static ObjectSet> entrySetLongWrap(Map map) { -+ return new ConvertingObjectSet, Long2ObjectMap.Entry>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); -+ } -+ -+ public static Long2ObjectMap.FastEntrySet entrySetLongWrapFast(Map map) { -+ return new ConvertingObjectSetFast, T>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); -+ } -+ -+ public static ObjectSet entrySetLongByteWrap(Map map) { -+ return new ConvertingObjectSet, Long2ByteMap.Entry>(map.entrySet(), FastUtilHackUtil::longByteEntryForwards, FastUtilHackUtil::longByteEntryBackwards); -+ } -+ -+ public static ObjectSet entrySetLongLongWrap(Map map) { -+ return new ConvertingObjectSet, Long2LongMap.Entry>(map.entrySet(), FastUtilHackUtil::longLongEntryForwards, FastUtilHackUtil::longLongEntryBackwards); -+ } -+ -+ -+ static class WrappingIntIterator implements IntIterator { -+ -+ Iterator backing; -+ -+ public WrappingIntIterator(Iterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public int nextInt() { -+ return backing.next(); -+ } -+ -+ @Override -+ public Integer next() { -+ return backing.next(); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ } -+ -+ static class WrappingLongIterator implements LongIterator { -+ -+ Iterator backing; -+ -+ public WrappingLongIterator(Iterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public long nextLong() { -+ return backing.next(); -+ } -+ -+ @Override -+ public Long next() { -+ return backing.next(); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ } -+ -+ static class WrappingShortIterator implements ShortIterator { -+ -+ Iterator backing; -+ -+ public WrappingShortIterator(Iterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public short nextShort() { -+ return backing.next(); -+ } -+ -+ @Override -+ public Short next() { -+ return backing.next(); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ } -+ -+ public static class WrappingIntSet implements IntSet { -+ -+ Set backing; -+ -+ public WrappingIntSet(Set backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean add(int key) { -+ return backing.add(key); -+ } -+ -+ @Override -+ public boolean contains(int key) { -+ return backing.contains(key); -+ } -+ -+ @Override -+ public int[] toIntArray() { -+ return backing.stream().mapToInt(i -> i).toArray(); -+ } -+ -+ @Override -+ public int[] toIntArray(int[] a) { -+ if (a.length >= size()) { -+ return null; -+ } else { -+ return toIntArray(); -+ } -+ } -+ -+ @Override -+ public int[] toArray(int[] a) { -+ return toIntArray(a); -+ } -+ -+ @Override -+ public boolean addAll(IntCollection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(IntCollection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(IntCollection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(IntCollection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public IntIterator iterator() { -+ return new WrappingIntIterator(backing.iterator()); -+ } -+ -+ @Override -+ public boolean remove(int k) { -+ return backing.remove(k); -+ } -+ -+ } -+ -+ public static LongSet wrapLongSet(Set longset) { -+ return new WrappingLongSet(longset); -+ } -+ -+ public static class WrappingLongSet implements LongSet { -+ -+ Set backing; -+ -+ public WrappingLongSet(Set backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean add(long key) { -+ return backing.add(key); -+ } -+ -+ @Override -+ public boolean contains(long key) { -+ return backing.contains(key); -+ } -+ -+ @Override -+ public long[] toLongArray() { -+ return backing.stream().mapToLong(i -> i).toArray(); -+ } -+ -+ @Override -+ public long[] toLongArray(long[] a) { -+ if (a.length >= size()) { -+ return null; -+ } else { -+ return toLongArray(); -+ } -+ } -+ -+ @Override -+ public long[] toArray(long[] a) { -+ return toLongArray(a); -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(LongCollection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(LongCollection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(LongCollection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public LongIterator iterator() { -+ return new WrappingLongIterator(backing.iterator()); -+ } -+ -+ @Override -+ public boolean remove(long k) { -+ return backing.remove(k); -+ } -+ -+ } -+ -+ public static LongSortedSet wrapLongSortedSet(Set longset) { -+ return new WrappingLongSortedSet(longset); -+ } -+ -+ public static class WrappingLongSortedSet implements LongSortedSet { -+ -+ Set backing; -+ -+ public WrappingLongSortedSet(Set backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean add(long key) { -+ return backing.add(key); -+ } -+ -+ @Override -+ public boolean contains(long key) { -+ return backing.contains(key); -+ } -+ -+ @Override -+ public long[] toLongArray() { -+ return backing.stream().mapToLong(i -> i).toArray(); -+ } -+ -+ @Override -+ public long[] toLongArray(long[] a) { -+ if (a.length >= size()) { -+ return null; -+ } else { -+ return toLongArray(); -+ } -+ } -+ -+ @Override -+ public long[] toArray(long[] a) { -+ return toLongArray(a); -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(LongCollection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(LongCollection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(LongCollection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public boolean remove(long k) { -+ return backing.remove(k); -+ } -+ -+ @Override -+ public LongBidirectionalIterator iterator(long fromElement) { -+ throw new UnsupportedOperationException(); -+ //return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); -+ } -+ -+ @Override -+ public LongBidirectionalIterator iterator() { -+ return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); -+ } -+ -+ @Override -+ public LongSortedSet subSet(long fromElement, long toElement) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongSortedSet headSet(long toElement) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongSortedSet tailSet(long fromElement) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongComparator comparator() { -+ return null; -+ } -+ -+ @Override -+ public long firstLong() { -+ return backing.stream().findAny().get(); -+ } -+ -+ @Override -+ public long lastLong() { -+ return backing.stream().findAny().get(); -+ } -+ -+ } -+ -+ public static IntSet wrapIntSet(Set intset) { -+ return new WrappingIntSet(intset); -+ } -+ -+ public static class WrappingObjectCollection implements ObjectCollection { -+ -+ Collection backing; -+ -+ public WrappingObjectCollection(Collection backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(Object o) { -+ return backing.contains(o); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean add(V e) { -+ return backing.add(e); -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ return backing.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public ObjectIterator iterator() { -+ return FastUtilHackUtil.itrWrap(backing); -+ } -+ -+ } -+ -+ public static ObjectCollection wrap(Collection c) { -+ return new WrappingObjectCollection(c); -+ } -+ -+ public static class WrappingByteCollection implements ByteCollection { -+ -+ Collection backing; -+ -+ public WrappingByteCollection(Collection backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(byte o) { -+ return backing.contains(o); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean add(byte e) { -+ return backing.add(e); -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ return backing.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public ByteIterator iterator() { -+ return FastUtilHackUtil.itrByteWrap(backing); -+ } -+ -+ @Override -+ public boolean rem(byte key) { -+ return this.remove(key); -+ } -+ -+ @Override -+ public byte[] toByteArray() { -+ return null; -+ } -+ -+ @Override -+ public byte[] toByteArray(byte[] a) { -+ return toArray(a); -+ } -+ -+ @Override -+ public byte[] toArray(byte[] a) { -+ return ArrayUtils.toPrimitive(backing.toArray(new Byte[0])); -+ } -+ -+ @Override -+ public boolean addAll(ByteCollection c) { -+ return addAll((Collection) c); -+ } -+ -+ @Override -+ public boolean containsAll(ByteCollection c) { -+ return containsAll((Collection) c); -+ } -+ -+ @Override -+ public boolean removeAll(ByteCollection c) { -+ return removeAll((Collection) c); -+ } -+ -+ @Override -+ public boolean retainAll(ByteCollection c) { -+ return retainAll((Collection) c); -+ } -+ -+ } -+ -+ public static ByteCollection wrapBytes(Collection c) { -+ return new WrappingByteCollection(c); -+ } -+ -+ public static class WrappingIntCollection implements IntCollection { -+ -+ Collection backing; -+ -+ public WrappingIntCollection(Collection backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(int o) { -+ return backing.contains(o); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean add(int e) { -+ return backing.add(e); -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ return backing.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public IntIterator iterator() { -+ return FastUtilHackUtil.itrIntWrap(backing); -+ } -+ -+ @Override -+ public boolean rem(int key) { -+ return this.remove(key); -+ } -+ -+ @Override -+ public int[] toIntArray() { -+ return null; -+ } -+ -+ @Override -+ public int[] toIntArray(int[] a) { -+ return toArray(a); -+ } -+ -+ @Override -+ public int[] toArray(int[] a) { -+ return ArrayUtils.toPrimitive(backing.toArray(new Integer[0])); -+ } -+ -+ @Override -+ public boolean addAll(IntCollection c) { -+ return addAll((Collection) c); -+ } -+ -+ @Override -+ public boolean containsAll(IntCollection c) { -+ return containsAll((Collection) c); -+ } -+ -+ @Override -+ public boolean removeAll(IntCollection c) { -+ return removeAll((Collection) c); -+ } -+ -+ @Override -+ public boolean retainAll(IntCollection c) { -+ return retainAll((Collection) c); -+ } -+ -+ } -+ -+ public static IntCollection wrapInts(Collection c) { -+ return new WrappingIntCollection(c); -+ } -+ -+ public static class WrappingLongCollection implements LongCollection { -+ -+ Collection backing; -+ -+ public WrappingLongCollection(Collection backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(long o) { -+ return backing.contains(o); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean add(long e) { -+ return backing.add(e); -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ return backing.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public LongIterator iterator() { -+ return FastUtilHackUtil.itrLongWrap(backing); -+ } -+ -+ @Override -+ public boolean rem(long key) { -+ return this.remove(key); -+ } -+ -+ @Override -+ public long[] toLongArray() { -+ return null; -+ } -+ -+ @Override -+ public long[] toLongArray(long[] a) { -+ return toArray(a); -+ } -+ -+ @Override -+ public long[] toArray(long[] a) { -+ return ArrayUtils.toPrimitive(backing.toArray(new Long[0])); -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return addAll((Collection) c); -+ } -+ -+ @Override -+ public boolean containsAll(LongCollection c) { -+ return containsAll((Collection) c); -+ } -+ -+ @Override -+ public boolean removeAll(LongCollection c) { -+ return removeAll((Collection) c); -+ } -+ -+ @Override -+ public boolean retainAll(LongCollection c) { -+ return retainAll((Collection) c); -+ } -+ -+ } -+ -+ public static LongCollection wrapLongs(Collection c) { -+ return new WrappingLongCollection(c); -+ } -+ -+ -+ public static class WrappingLongListIterator implements LongListIterator { -+ -+ ListIterator backing; -+ -+ public WrappingLongListIterator(ListIterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public long previousLong() { -+ return backing.previous(); -+ } -+ -+ @Override -+ public long nextLong() { -+ return backing.next(); -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public boolean hasPrevious() { -+ return backing.hasPrevious(); -+ } -+ -+ @Override -+ public int nextIndex() { -+ return backing.nextIndex(); -+ } -+ -+ @Override -+ public int previousIndex() { -+ return backing.previousIndex(); -+ } -+ -+ @Override -+ public void add(long k) { -+ backing.add(k); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ @Override -+ public void set(long k) { -+ backing.set(k); -+ } -+ } -+ -+ public static class SlimWrappingLongListIterator implements LongListIterator { -+ -+ Iterator backing; -+ -+ public SlimWrappingLongListIterator(Iterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public long previousLong() { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public long nextLong() { -+ return backing.next(); -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public boolean hasPrevious() { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public int nextIndex() { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public int previousIndex() { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public void add(long k) { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ @Override -+ public void set(long k) { -+ throw new IllegalStateException(); -+ } -+ } -+ -+ public static LongListIterator wrap(ListIterator c) { -+ return new WrappingLongListIterator(c); -+ } -+ -+ public static LongListIterator wrap(Iterator c) { -+ return new SlimWrappingLongListIterator(c); -+ } -+ -+ public static class WrappingByteIterator implements ByteIterator { -+ -+ Iterator parent; -+ -+ public WrappingByteIterator(Iterator parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return parent.hasNext(); -+ } -+ -+ @Override -+ public Byte next() { -+ return parent.next(); -+ } -+ -+ @Override -+ public void remove() { -+ parent.remove(); -+ } -+ -+ @Override -+ public byte nextByte() { -+ return next(); -+ } -+ -+ } -+ -+ public static ByteIterator itrByteWrap(Iterator backing) { -+ return new WrappingByteIterator(backing); -+ } -+ -+ public static ByteIterator itrByteWrap(Iterable backing) { -+ return new WrappingByteIterator(backing.iterator()); -+ } -+ -+ public static IntIterator itrIntWrap(Iterator backing) { -+ return new WrappingIntIterator(backing); -+ } -+ -+ public static IntIterator itrIntWrap(Iterable backing) { -+ return new WrappingIntIterator(backing.iterator()); -+ } -+ -+ public static LongIterator itrLongWrap(Iterator backing) { -+ return new WrappingLongIterator(backing); -+ } -+ -+ public static LongIterator itrLongWrap(Iterable backing) { -+ return new WrappingLongIterator(backing.iterator()); -+ } -+ -+ public static ShortIterator itrShortWrap(Iterator backing) { -+ return new WrappingShortIterator(backing); -+ } -+ -+ public static ShortIterator itrShortWrap(Iterable backing) { -+ return new WrappingShortIterator(backing.iterator()); -+ } -+ -+ public static class WrapperObjectIterator implements ObjectIterator { -+ -+ Iterator parent; -+ -+ public WrapperObjectIterator(Iterator parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return parent.hasNext(); -+ } -+ -+ @Override -+ public T next() { -+ return parent.next(); -+ } -+ -+ @Override -+ public void remove() { -+ parent.remove(); -+ } -+ -+ } -+ -+ public static class IntWrapperEntry implements Entry { -+ -+ Map.Entry parent; -+ -+ public IntWrapperEntry(Map.Entry parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public T getValue() { -+ return parent.getValue(); -+ } -+ -+ @Override -+ public T setValue(T value) { -+ return parent.setValue(value); -+ } -+ -+ @Override -+ public int getIntKey() { -+ return parent.getKey(); -+ } -+ -+ @Override -+ public Integer getKey() { -+ return parent.getKey(); -+ } -+ -+ } -+ -+ public static class Long2IntWrapperEntry implements Long2IntMap.Entry { -+ -+ Map.Entry parent; -+ -+ public Long2IntWrapperEntry(Map.Entry parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public long getLongKey() { -+ return parent.getKey(); -+ } -+ -+ @Override -+ public int getIntValue() { -+ return parent.getValue(); -+ } -+ -+ @Override -+ public int setValue(int value) { -+ return parent.setValue(value); -+ } -+ -+ -+ } -+ -+ public static class WrapperIntEntryObjectIterator implements ObjectIterator> { -+ -+ Iterator> parent; -+ -+ public WrapperIntEntryObjectIterator(Iterator> parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return parent.hasNext(); -+ } -+ -+ @Override -+ public Entry next() { -+ Map.Entry val = parent.next(); -+ if (val == null) return null; -+ return new IntWrapperEntry(val); -+ } -+ -+ @Override -+ public void remove() { -+ parent.remove(); -+ } -+ -+ } -+ -+ public static ObjectIterator> intMapItrFake(Map in) { -+ return new WrapperIntEntryObjectIterator(in.entrySet().iterator()); -+ } -+ -+ public static ObjectIterator itrWrap(Iterator in) { -+ return new WrapperObjectIterator(in); -+ } -+ -+ public static ObjectIterator itrWrap(Iterable in) { -+ return new WrapperObjectIterator(in.iterator()); -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..bb6c592fd34423fdd910feae83a058d288da537a ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java -@@ -0,0 +1,93 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -+import it.unimi.dsi.fastutil.ints.IntSet; -+import it.unimi.dsi.fastutil.objects.ObjectCollection; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+import org.apache.commons.lang3.NotImplementedException; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class Int2ObjectConcurrentHashMap implements Int2ObjectMap { -+ -+ Map backing; -+ -+ public Int2ObjectConcurrentHashMap() { -+ backing = new ConcurrentHashMap(); -+ } -+ -+ @Override -+ public V get(int key) { -+ return backing.get(key); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean containsValue(Object value) { -+ return backing.containsValue(value); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(V rv) { -+ throw new NotImplementedException("MCMT - Not implemented"); -+ } -+ -+ @Override -+ public V defaultReturnValue() { -+ return null; -+ } -+ -+ @Override -+ public ObjectSet> int2ObjectEntrySet() { -+ return FastUtilHackUtil.entrySetIntWrap(backing); -+ } -+ -+ -+ @Override -+ public IntSet keySet() { -+ return FastUtilHackUtil.wrapIntSet(backing.keySet()); -+ } -+ -+ @Override -+ public ObjectCollection values() { -+ return FastUtilHackUtil.wrap(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(int key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public V put(int key, V value) { -+ return backing.put(key, value); -+ } -+ -+ @Override -+ public V put(Integer key, V value) { -+ return backing.put(key, value); -+ } -+ -+ @Override -+ public V remove(int key) { -+ return backing.remove(key); -+ } -+ -+ @Override -+ public void clear() { backing.clear(); } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e0fab16860e1be817fd10dbec684f295f2e291dd ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java -@@ -0,0 +1,99 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.bytes.ByteCollection; -+import it.unimi.dsi.fastutil.longs.Long2ByteMap; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class Long2ByteConcurrentHashMap implements Long2ByteMap { -+ -+ Map backing; -+ byte defaultReturn = 0; -+ byte nullKey = 0; -+ -+ public Long2ByteConcurrentHashMap() { -+ backing = new ConcurrentHashMap<>(); -+ } -+ -+ public Long2ByteConcurrentHashMap(int initialCapacity, float loadFactor) { -+ backing = new ConcurrentHashMap<>(initialCapacity, loadFactor); -+ } -+ -+ @Override -+ public byte get(long key) { -+ Byte out = backing.get(key); -+ return out == null ? defaultReturn : out; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean containsValue(byte value) { -+ return backing.containsValue(value); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(byte rv) { -+ defaultReturn = rv; -+ } -+ -+ @Override -+ public byte defaultReturnValue() { -+ return defaultReturn; -+ } -+ -+ @Override -+ public ObjectSet long2ByteEntrySet() { -+ return FastUtilHackUtil.entrySetLongByteWrap(backing); -+ } -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public ByteCollection values() { -+ return FastUtilHackUtil.wrapBytes(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public byte put(long key, byte value) { -+ return put((Long) key, (Byte) value); -+ } -+ -+ @Override -+ public Byte put(Long key, Byte value) { -+ Byte out = backing.put(key, value); -+ return out == null ? Byte.valueOf(defaultReturn) : out; -+ } -+ -+ @Override -+ public byte remove(long key) { -+ Byte out = backing.remove(key); -+ return out == null ? defaultReturn : out; -+ } -+ -+ -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..261f06a88021a95b6a0500444665547aeb4ae2c1 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java -@@ -0,0 +1,74 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.ints.IntCollection; -+import it.unimi.dsi.fastutil.longs.Long2IntMap; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class Long2IntConcurrentHashMap implements Long2IntMap { -+ -+ public Map backing = new ConcurrentHashMap(); -+ int defaultRV = 0; -+ -+ @Override -+ public int get(long key) { -+ if (backing.containsKey(key)) { -+ return backing.get(key); -+ } else return defaultRV; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(int rv) { -+ defaultRV = rv; -+ } -+ -+ @Override -+ public int defaultReturnValue() { -+ return defaultRV; -+ } -+ -+ @Override -+ public ObjectSet long2IntEntrySet() { -+ return null; -+ } -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public IntCollection values() { -+ return FastUtilHackUtil.wrapInts(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public boolean containsValue(int value) { -+ return backing.containsValue(value); -+ } -+ -+ -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..dc2342486318721d399c7c60a0a859befb4d1c9f ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java -@@ -0,0 +1,375 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.Hash; -+import it.unimi.dsi.fastutil.ints.IntCollection; -+import it.unimi.dsi.fastutil.longs.*; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+@SuppressWarnings("deprecation") -+public class Long2IntConcurrentNonLinkedOpenMap extends Long2IntLinkedOpenHashMap { -+ -+ /** -+ * -+ */ -+ private static final long serialVersionUID = -2082212127278131631L; -+ -+ public Map backing = new ConcurrentHashMap(); -+ -+ public Long2IntConcurrentNonLinkedOpenMap(final int expected, final float f) { -+ -+ } -+ -+ /** -+ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. -+ * -+ * @param expected the expected number of elements in the hash map. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final int expected) { -+ } -+ -+ /** -+ * Creates a new hash map with initial expected -+ * {@link Hash#DEFAULT_INITIAL_SIZE} entries and -+ * {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap() { -+ } -+ -+ /** -+ * Creates a new hash map copying a given one. -+ * -+ * @param m a {@link Map} to be copied into the new hash map. -+ * @param f the load factor. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final Map m, final float f) { -+ putAll(m); -+ } -+ -+ /** -+ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor -+ * copying a given one. -+ * -+ * @param m a {@link Map} to be copied into the new hash map. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final Map m) { -+ this(m, DEFAULT_LOAD_FACTOR); -+ } -+ -+ /** -+ * Creates a new hash map copying a given type-specific one. -+ * -+ * @param m a type-specific map to be copied into the new hash map. -+ * @param f the load factor. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m, final float f) { -+ this(m.size(), f); -+ putAll(m); -+ } -+ -+ /** -+ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor -+ * copying a given type-specific one. -+ * -+ * @param m a type-specific map to be copied into the new hash map. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m) { -+ this(m, DEFAULT_LOAD_FACTOR); -+ } -+ -+ /** -+ * Creates a new hash map using the elements of two parallel arrays. -+ * -+ * @param k the array of keys of the new hash map. -+ * @param v the array of corresponding values in the new hash map. -+ * @param f the load factor. -+ * @throws IllegalArgumentException if {@code k} and {@code v} have different -+ * lengths. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v, final float f) { -+ if (k.length != v.length) -+ throw new IllegalArgumentException( -+ "The key array and the value array have different lengths (" + k.length + " and " + v.length + ")"); -+ for (int i = 0; i < k.length; i++) -+ this.put(k[i], v[i]); -+ } -+ -+ /** -+ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor -+ * using the elements of two parallel arrays. -+ * -+ * @param k the array of keys of the new hash map. -+ * @param v the array of corresponding values in the new hash map. -+ * @throws IllegalArgumentException if {@code k} and {@code v} have different -+ * lengths. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v) { -+ this(k, v, DEFAULT_LOAD_FACTOR); -+ } -+ -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ public int put(final long k, final int v) { -+ Integer out = backing.put(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int addTo(final long k, final int incr) { -+ Integer out = backing.put(k, this.get(k)+incr); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int remove(final long k) { -+ Integer out = backing.remove(k); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int removeFirstInt() { -+ Integer out = this.remove(backing.keySet().stream().findAny().get()); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int removeLastInt() { -+ Integer out = this.remove(backing.keySet().stream().findAny().get()); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public int getAndMoveToFirst(final long k) { -+ Integer out = backing.get(k); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int getAndMoveToLast(final long k) { -+ Integer out = backing.get(k); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int putAndMoveToFirst(final long k, final int v) { -+ Integer out = backing.put(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int putAndMoveToLast(final long k, final int v) { -+ Integer out = backing.put(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int get(final long k) { -+ Integer out = backing.get(k); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public boolean containsKey(final long k) { -+ return backing.containsKey(k); -+ } -+ -+ public boolean containsValue(final int v) { -+ return backing.containsValue(v); -+ } -+ -+ public int getOrDefault(final long k, final int defaultValue) { -+ Integer out = backing.getOrDefault(k, defaultValue); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int putIfAbsent(final long k, final int v) { -+ Integer out = backing.putIfAbsent(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public boolean remove(final long k, final int v) { -+ return backing.remove(k, v); -+ } -+ -+ -+ public boolean replace(final long k, final int oldValue, final int v) { -+ return backing.replace(k, oldValue, v); -+ } -+ -+ -+ public int replace(final long k, final int v) { -+ Integer out = backing.replace(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public int computeIfAbsent(final long k, final java.util.function.LongToIntFunction mappingFunction) { -+ Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.applyAsInt(l)); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public int computeIfAbsentNullable(final long k, -+ final java.util.function.LongFunction mappingFunction) { -+ Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.apply(l)); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public int computeIfPresent(final long k, -+ final java.util.function.BiFunction remappingFunction) { -+ if (this.containsKey(k)) { -+ Integer out = backing.put(k, remappingFunction.apply(k, backing.get(k))); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ return defaultReturnValue(); -+ -+ } -+ -+ @Override -+ public int compute(final long k, -+ final java.util.function.BiFunction remappingFunction) { -+ Integer out = backing.compute(k, remappingFunction); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ @Override -+ public int merge(final long k, final int v, -+ final java.util.function.BiFunction remappingFunction) { -+ Integer out = backing.merge(k, v, remappingFunction); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public long firstLongKey() { -+ return backing.keySet().stream().findAny().get(); -+ } -+ -+ @Override -+ public long lastLongKey() { -+ return backing.keySet().stream().findAny().get(); -+ } -+ -+ @Override -+ public Long2IntSortedMap tailMap(long from) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public Long2IntSortedMap headMap(long to) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public Long2IntSortedMap subMap(long from, long to) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongComparator comparator() { -+ return null; -+ } -+ -+ -+ @Override -+ public FastSortedEntrySet long2IntEntrySet() { -+ //TODO implement -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongSortedSet keySet() { -+ return FastUtilHackUtil.wrapLongSortedSet(backing.keySet()); -+ } -+ -+ -+ @Override -+ public IntCollection values() { -+ return FastUtilHackUtil.wrapInts(backing.values()); -+ } -+ -+ public boolean trim() { -+ return true; -+ } -+ -+ public boolean trim(final int n) { -+ return true; -+ } -+ -+ -+ @Override -+ public Long2IntConcurrentNonLinkedOpenMap clone() { -+ return new Long2IntConcurrentNonLinkedOpenMap(backing); -+ } -+ -+ @Override -+ public int hashCode() { -+ return backing.hashCode(); -+ } -+ -+ -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3205d30a03f99caf7dfa05237b2bc31182b2db20 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java -@@ -0,0 +1,97 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.longs.Long2LongMap; -+import it.unimi.dsi.fastutil.longs.LongCollection; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+ -+public class Long2LongConcurrentHashMap implements Long2LongMap { -+ -+ public Map backing = new ConcurrentHashMap(); -+ long defaultRV = 0; -+ -+ public Long2LongConcurrentHashMap(long defaultRV) { -+ this.defaultRV = defaultRV; -+ } -+ -+ @Override -+ public long get(long key) { -+ if (backing.containsKey(key)) { -+ return backing.get(key); -+ } else return defaultRV; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public long put(final long key, final long val) { -+ backing.put(key,val); -+ return val; -+ } -+ -+ @Override -+ public Long put(final Long key, final Long val) { -+ backing.put(key,val); -+ return val; -+ } -+ -+ @Override -+ public long remove(final long key) { -+ return backing.remove(key); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(long rv) { -+ defaultRV = rv; -+ } -+ -+ @Override -+ public long defaultReturnValue() { -+ return defaultRV; -+ } -+ -+ @Override -+ public ObjectSet long2LongEntrySet() { -+ return FastUtilHackUtil.entrySetLongLongWrap(backing); -+ } -+ -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public LongCollection values() { -+ return FastUtilHackUtil.wrapLongs(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public boolean containsValue(long value) { -+ return backing.containsValue(value); -+ } -+ -+ -+} -\ No newline at end of file -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a5ed71564d4c9a986f77cbc0397130aa38f97a91 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java -@@ -0,0 +1,93 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectCollection; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class Long2ObjectConcurrentHashMap implements Long2ObjectMap { -+ -+ Map backing; -+ V defaultReturn = null; -+ -+ public Long2ObjectConcurrentHashMap() { -+ backing = new ConcurrentHashMap(); -+ } -+ -+ @Override -+ public V get(long key) { -+ V out = backing.get(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean containsValue(Object value) { -+ return backing.containsValue(value); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(V rv) { -+ defaultReturn = rv; -+ } -+ -+ @Override -+ public V defaultReturnValue() { -+ return defaultReturn; -+ } -+ -+ @Override -+ public ObjectSet> long2ObjectEntrySet() { -+ return FastUtilHackUtil.entrySetLongWrap(backing); -+ } -+ -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public ObjectCollection values() { -+ return FastUtilHackUtil.wrap(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public V put(long key, V value) { -+ return put((Long)key, value); -+ } -+ -+ @Override -+ public V put(Long key, V value) { -+ V out = backing.put(key, value); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); -+ } -+ -+ @Override -+ public V remove(long key) { -+ V out = backing.remove(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a7d6be048ab3b8bd38231fce16eca0ac78e24690 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java -@@ -0,0 +1,233 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.longs.Long2ObjectFunction; -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectCollection; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+import java.util.function.Function; -+ -+public class Long2ObjectOpenConcurrentHashMap extends Long2ObjectOpenHashMap { -+ -+ /** -+ * -+ */ -+ private static final long serialVersionUID = -121514116954680057L; -+ -+ Map backing; -+ V defaultReturn = null; -+ -+ public Long2ObjectOpenConcurrentHashMap() { -+ backing = new ConcurrentHashMap(); -+ } -+ -+ @Override -+ public V get(long key) { -+ V out = backing.get(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+ -+ @Override -+ public V get(Object key) { -+ V out = backing.get(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean containsValue(Object value) { -+ return backing.containsValue(value); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(V rv) { -+ defaultReturn = rv; -+ } -+ -+ @Override -+ public V defaultReturnValue() { -+ return defaultReturn; -+ } -+ -+ @Override -+ public FastEntrySet long2ObjectEntrySet() { -+ return FastUtilHackUtil.entrySetLongWrapFast(backing); -+ } -+ -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public ObjectCollection values() { -+ return FastUtilHackUtil.wrap(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public V put(long key, V value) { -+ return put((Long)key, value); -+ } -+ -+ @Override -+ public V put(Long key, V value) { -+ V out = backing.put(key, value); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); -+ } -+ -+ @Override -+ public V remove(long key) { -+ V out = backing.remove(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+ -+ @Override -+ public boolean trim() { return true; } -+ -+ @Override -+ public boolean trim(final int n) { return true; } -+ -+ @Override -+ public boolean replace(final long k, final V oldValue, final V v) { -+ return backing.replace(k, oldValue, v); -+ } -+ -+ @Override -+ public V replace(final long k, final V v) { -+ return backing.replace(k, v); -+ } -+ -+ @Override -+ public boolean replace(final Long k, final V oldValue, final V v) { -+ return backing.replace(k, oldValue, v); -+ } -+ -+ @Override -+ public V replace(final Long k, final V v) { -+ return backing.replace(k, v); -+ } -+ -+ @Override -+ public boolean remove(final long k, final Object v) { -+ return backing.remove(k, v); -+ } -+ -+ @Override -+ public V putIfAbsent(final long k, final V v) { -+ return backing.putIfAbsent(k, v); -+ } -+ -+ @Override -+ public V putIfAbsent(final Long k, final V v) { -+ return backing.putIfAbsent(k, v); -+ } -+ -+ @Override -+ public V merge(final long k, final V v, final java.util.function.BiFunction remappingFunction) { -+ return backing.merge(k, v, remappingFunction); -+ } -+ -+ @Override -+ public V merge(Long k, final V v, final java.util.function.BiFunction remappingFunction) { -+ return backing.merge(k, v, remappingFunction); -+ } -+ -+ @Override -+ public int hashCode() { -+ return backing.hashCode(); -+ } -+ -+ @Override -+ public V getOrDefault(final long k, final V defaultValue) { -+ return backing.getOrDefault(k, defaultValue); -+ } -+ -+ @Override -+ public V getOrDefault(Object k, final V defaultValue) { -+ return backing.getOrDefault(k, defaultValue); -+ } -+ -+ @Override -+ public V computeIfPresent(final long k, final java.util.function.BiFunction remappingFunction) { -+ return backing.computeIfPresent(k, remappingFunction); -+ } -+ -+ @Override -+ public V computeIfPresent(final Long k, final java.util.function.BiFunction remappingFunction) { -+ return backing.computeIfPresent(k, remappingFunction); -+ } -+ -+ @Override -+ public V computeIfAbsent(final long k, final java.util.function.LongFunction mappingFunction) { -+ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); -+ } -+ -+ public V computeIfAbsent(final Long k, final java.util.function.LongFunction mappingFunction) { -+ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); -+ } -+ -+ @Override -+ public V computeIfAbsentPartial(final long key, final Long2ObjectFunction mappingFunction) { -+ if (!mappingFunction.containsKey(key)) -+ return defaultReturn; -+ return backing.computeIfAbsent(key, (llong) -> mappingFunction.apply(llong)); -+ } -+ -+ @Override -+ public V compute(final long k, final java.util.function.BiFunction remappingFunction) { -+ return backing.compute(k, remappingFunction); -+ } -+ -+ @Override -+ public V compute(final Long k, final java.util.function.BiFunction remappingFunction) { -+ return backing.compute(k, remappingFunction); -+ } -+ -+ @Override -+ public Long2ObjectOpenHashMap clone() { -+ throw new IllegalArgumentException(); -+ } -+ -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public ObjectSet> entrySet() { -+ return new FastUtilHackUtil.ConvertingObjectSet, Map.Entry>(backing.entrySet(), Function.identity(), Function.identity()); -+ } -+ -+ @Override -+ public V remove(Object key) { -+ return backing.remove(key); -+ } -+ -+ @Override -+ public boolean remove(Object key, Object value) { -+ return backing.remove(key, value); -+ } -+ -+} -\ No newline at end of file -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1d65e9575389a170e8b8465b1221a6e319749eb1 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java -@@ -0,0 +1,190 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil.sync; -+ -+import it.unimi.dsi.fastutil.longs.*; -+ -+import java.util.Collection; -+import java.util.Iterator; -+ -+public class SyncLongLinkedOpenHashSet extends LongLinkedOpenHashSet { -+ -+ private static final long serialVersionUID = -5532128240738069111L; -+ -+ public SyncLongLinkedOpenHashSet() { -+ super(); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final int initial) { -+ super(initial); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final int initial, final float dnc) { -+ this(initial); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final LongCollection c) { -+ this(c.size()); -+ addAll(c); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final LongCollection c, final float f) { -+ this(c.size(), f); -+ addAll(c); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final LongIterator i, final float f) { -+ this(16, f); -+ while (i.hasNext()) -+ add(i.nextLong()); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final LongIterator i) { -+ this(i, -1); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final Iterator i, final float f) { -+ this(LongIterators.asLongIterator(i), f); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final Iterator i) { -+ this(LongIterators.asLongIterator(i)); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final long[] a, final int offset, final int length, final float f) { -+ this(length < 0 ? 0 : length, f); -+ LongArrays.ensureOffsetLength(a, offset, length); -+ for (int i = 0; i < length; i++) -+ add(a[offset + i]); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final long[] a, final int offset, final int length) { -+ this(a, offset, length, DEFAULT_LOAD_FACTOR); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final long[] a, final float f) { -+ this(a, 0, a.length, f); -+ } -+ -+ public SyncLongLinkedOpenHashSet(final long[] a) { -+ this(a, -1); -+ } -+ -+ @Override -+ public synchronized boolean add(final long k) { -+ return super.add(k); -+ } -+ -+ @Override -+ public synchronized boolean addAll(LongCollection c) { -+ return super.addAll(c); -+ } -+ -+ @Override -+ public synchronized boolean addAll(Collection c) { -+ return super.addAll(c); -+ } -+ -+ @Override -+ public synchronized boolean addAndMoveToFirst(final long k) { -+ return super.addAndMoveToFirst(k); -+ } -+ -+ @Override -+ public synchronized boolean addAndMoveToLast(final long k) { -+ return super.addAndMoveToFirst(k); -+ } -+ -+ @Override -+ public synchronized void clear() { -+ super.clear(); -+ } -+ -+ @Override -+ public synchronized LongLinkedOpenHashSet clone() { -+ return new SyncLongLinkedOpenHashSet(this); -+ } -+ -+ @Override -+ public synchronized LongComparator comparator() { -+ return super.comparator(); -+ } -+ -+ @Override -+ public synchronized boolean contains(final long k) { -+ return super.contains(k); -+ } -+ -+ @Override -+ public synchronized long firstLong() { -+ return super.firstLong(); -+ } -+ -+ @Override -+ public synchronized int hashCode() { -+ return super.hashCode(); -+ } -+ -+ @Override -+ public synchronized LongSortedSet headSet(long to) { -+ return super.headSet(to); -+ } -+ -+ @Override -+ public synchronized boolean isEmpty() { -+ return super.isEmpty(); -+ } -+ -+ @Override -+ public synchronized LongListIterator iterator() { -+ return super.iterator(); -+ } -+ -+ @Override -+ public synchronized LongListIterator iterator(long from) { -+ return super.iterator(from); -+ } -+ -+ @Override -+ public synchronized long lastLong() { -+ return super.lastLong(); -+ } -+ -+ @Override -+ public synchronized boolean remove(final long k) { -+ return super.remove(k); -+ } -+ -+ @Override -+ public synchronized long removeFirstLong() { -+ return super.removeFirstLong(); -+ } -+ -+ @Override -+ public synchronized long removeLastLong() { -+ return super.removeLastLong(); -+ } -+ -+ @Override -+ public synchronized int size() { -+ return super.size(); -+ } -+ -+ @Override -+ public synchronized LongSortedSet subSet(long from, long to) { -+ return super.subSet(from, to); -+ } -+ -+ @Override -+ public synchronized LongSortedSet tailSet(long from) { -+ return super.tailSet(from); -+ } -+ -+ @Override -+ public synchronized boolean trim() { -+ return super.trim(); -+ } -+ -+ @Override -+ public synchronized boolean trim(final int n) { -+ return super.trim(n); -+ } -+} diff --git a/patches/server/0045-MikuServer-Bug-fixes-in-CPS-and-Pathfinding.patch b/patches/server/0045-MikuServer-Bug-fixes-in-CPS-and-Pathfinding.patch new file mode 100644 index 00000000..d87b7279 --- /dev/null +++ b/patches/server/0045-MikuServer-Bug-fixes-in-CPS-and-Pathfinding.patch @@ -0,0 +1,254 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +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 objectiterator = +- level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); ++ Iterator objectiterator = ++ level.entityTickList.entities.iterator(); + gg.pufferfish.pufferfish.util.IterableWrapper 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 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 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 iterator = this.entities.iterator(); +- try { +- while (iterator.hasNext()) { +- action.accept(iterator.next()); +- } +- } finally { +- iterator.finishedIterating(); ++ Iterator 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 positions, float followRange, int distance, float rangeMultiplier) { ++ public synchronized Path findPath(PathNavigationRegion world, Mob mob, Set positions, float followRange, int distance, float rangeMultiplier) { + this.openSet.clear(); + this.nodeEvaluator.prepare(world, mob); + Node node = this.nodeEvaluator.getStart(); diff --git a/patches/server/0046-Hearse-MC-Code-changes.patch b/patches/server/0046-Hearse-MC-Code-changes.patch deleted file mode 100644 index dc3c38da..00000000 --- a/patches/server/0046-Hearse-MC-Code-changes.patch +++ /dev/null @@ -1,2843 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -Date: Sun, 15 Jan 2023 09:57:50 +0800 -Subject: [PATCH] Hearse: MC Code changes - -Original license: MIT -Original project: https://github.com/Era4FunMC/Hearse - -diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java -index 24c677e80af652952263253409c050641e72e3b5..c6f9fb3efb92de0879eab6389fabd531bb4cfcb2 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java -@@ -1,7 +1,8 @@ - package net.minecraft.network.protocol.game; - --import it.unimi.dsi.fastutil.shorts.ShortIterator; - import it.unimi.dsi.fastutil.shorts.ShortSet; -+import java.util.ArrayList; -+import java.util.List; - import java.util.function.BiConsumer; - import net.minecraft.core.BlockPos; - import net.minecraft.core.SectionPos; -@@ -22,14 +23,16 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet copy = new ArrayList<>(positions); -+ this.positions = new short[copy.size()]; -+ this.states = new BlockState[copy.size()]; -+ for (int i = 0; i < copy.size(); i++) { -+ this.positions[i] = copy.get(i); -+ } - -- this.positions = new short[i]; -- this.states = new BlockState[i]; -- int j = 0; - -- for (ShortIterator shortiterator = positions.iterator(); shortiterator.hasNext(); ++j) { -- short short0 = (Short) shortiterator.next(); -+ for (int j = 0;j < this.positions.length;j++) { -+ short short0 = this.positions[j]; - - this.positions[j] = short0; - this.states[j] = (section != null) ? section.getBlockState(SectionPos.sectionRelativeX(short0), SectionPos.sectionRelativeY(short0), SectionPos.sectionRelativeZ(short0)) : net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(); // CraftBukkit - SPIGOT-6076, Mojang bug when empty chunk section notified -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 3b7e4b724e86518ea57f5ed5ef0b8b3741d10f6f..42599340856aa5ffcfae2281684174ca4dee289a 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1,10 +1,11 @@ - 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; - 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; -@@ -85,7 +86,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; -@@ -109,17 +109,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; -@@ -183,12 +180,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 - -@@ -233,7 +224,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop registries; -- private Map, ServerLevel> levels; -+ public Map, ServerLevel> levels; - private PlayerList playerList; - private volatile boolean running; - private volatile boolean isRestarting = false; // Paper - flag to signify we're attempting to restart -@@ -922,10 +913,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 5000000000L) { - this.lastServerStatus = i; - this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount())); -@@ -1522,11 +1516,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 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 -@@ -1541,35 +1533,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296 - if (this.changedBlocksPerSection[i] == null) { - this.hasChangedSections = true; this.addToBroadcastMap(); // Paper - optimise chunk tick iteration -- this.changedBlocksPerSection[i] = new ShortOpenHashSet(); -+ this.changedBlocksPerSection[i] = new ConcurrentShortHashSet(); - } - - this.changedBlocksPerSection[i].add(SectionPos.sectionRelativePos(pos)); -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index beb7c22cb63021f26c06f91050361e1b25fcc72d..dfb747eba6bf7088af0ff400da169de00a076365 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1,57 +1,22 @@ - package net.minecraft.server.level; - --import co.aikar.timings.Timing; // Paper --import com.google.common.collect.ImmutableList; -+import com.google.common.collect.*; - import com.google.common.collect.ImmutableList.Builder; --import com.google.common.collect.Iterables; --import com.google.common.collect.ComparisonChain; // Paper --import com.google.common.collect.Lists; --import com.google.common.collect.Queues; --import com.google.common.collect.Sets; - import com.google.gson.JsonElement; - import com.mojang.datafixers.DataFixer; - import com.mojang.datafixers.util.Either; - import com.mojang.logging.LogUtils; - import com.mojang.serialization.DataResult; - import com.mojang.serialization.JsonOps; -+import io.papermc.paper.util.MCUtil; - import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; - import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; --import it.unimi.dsi.fastutil.longs.Long2ByteMap; --import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; --import it.unimi.dsi.fastutil.longs.Long2LongMap; --import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; --import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; --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.longs.*; - import it.unimi.dsi.fastutil.objects.ObjectIterator; --import java.io.IOException; --import java.io.Writer; --import java.nio.file.Path; --import java.util.ArrayList; --import java.util.BitSet; --import java.util.Iterator; --import java.util.List; --import java.util.Locale; --import java.util.Objects; --import java.util.Optional; --import java.util.Queue; --import java.util.Set; --import java.util.concurrent.CancellationException; --import java.util.concurrent.CompletableFuture; --import java.util.concurrent.CompletionException; --import java.util.concurrent.CompletionStage; --import java.util.concurrent.Executor; --import java.util.concurrent.atomic.AtomicInteger; --import java.util.function.BooleanSupplier; --import java.util.function.Consumer; --import java.util.function.IntFunction; --import java.util.function.IntSupplier; --import java.util.function.Supplier; --import java.util.stream.Collectors; --import javax.annotation.Nullable; -+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; -+import it.unimi.dsi.fastutil.objects.ReferenceSet; -+import it.unimi.dsi.fastutil.objects.ReferenceSets; - import net.minecraft.CrashReport; - import net.minecraft.CrashReportCategory; - import net.minecraft.ReportedException; -@@ -63,36 +28,19 @@ import net.minecraft.core.registries.Registries; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.network.protocol.Packet; - import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; --import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; --import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket; --import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket; - import net.minecraft.network.protocol.game.DebugPackets; --import io.papermc.paper.util.MCUtil; - import net.minecraft.server.MinecraftServer; - import net.minecraft.server.level.progress.ChunkProgressListener; - import net.minecraft.server.network.ServerPlayerConnection; --import net.minecraft.util.CsvOutput; - import net.minecraft.util.Mth; --import net.minecraft.util.profiling.ProfilerFiller; - import net.minecraft.util.thread.BlockableEventLoop; --import net.minecraft.util.thread.ProcessorHandle; --import net.minecraft.util.thread.ProcessorMailbox; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.EntityType; --import net.minecraft.world.entity.Mob; - import net.minecraft.world.entity.ai.village.poi.PoiManager; - import net.minecraft.world.entity.boss.EnderDragonPart; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.GameRules; --import net.minecraft.world.level.chunk.ChunkAccess; --import net.minecraft.world.level.chunk.ChunkGenerator; --import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; --import net.minecraft.world.level.chunk.ChunkStatus; --import net.minecraft.world.level.chunk.ImposterProtoChunk; --import net.minecraft.world.level.chunk.LevelChunk; --import net.minecraft.world.level.chunk.LightChunkGetter; --import net.minecraft.world.level.chunk.ProtoChunk; --import net.minecraft.world.level.chunk.UpgradeData; -+import net.minecraft.world.level.chunk.*; - import net.minecraft.world.level.chunk.storage.ChunkSerializer; - import net.minecraft.world.level.chunk.storage.ChunkStorage; - import net.minecraft.world.level.entity.ChunkStatusUpdateListener; -@@ -101,19 +49,24 @@ import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; - import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; - import net.minecraft.world.level.levelgen.RandomState; - import net.minecraft.world.level.levelgen.blending.BlendingData; --import net.minecraft.world.level.levelgen.structure.StructureStart; - import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelStorageSource; --import net.minecraft.world.phys.Vec3; --import org.apache.commons.lang3.mutable.MutableBoolean; - import org.apache.commons.lang3.mutable.MutableObject; --import org.slf4j.Logger; - import org.bukkit.craftbukkit.generator.CustomChunkGenerator; --import org.bukkit.entity.Player; --// CraftBukkit end -+import org.slf4j.Logger; - --import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper -+import javax.annotation.Nullable; -+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.atomic.AtomicInteger; -+import java.util.function.*; - - public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider { - -@@ -147,13 +100,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - public final StructureTemplateManager structureTemplateManager; // Paper - rewrite chunk system - private final String storageName; - private final PlayerMap playerMap; -- public final Int2ObjectMap entityMap; -+ public final Map entityMap; - private final Long2ByteMap chunkTypeCache; - private final Long2LongMap chunkSaveCooldowns; - private final Queue unloadQueue; - int viewDistance; - public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper -- public final ReferenceOpenHashSet needsChangeBroadcasting = new ReferenceOpenHashSet<>(); -+ public final ReferenceSet needsChangeBroadcasting = ReferenceSets.synchronize(new ReferenceOpenHashSet<>()); - - // Paper - rewrite chunk system - // Paper start - optimise checkDespawn -@@ -295,9 +248,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper - rewrite chunk system - this.tickingGenerated = new AtomicInteger(); - this.playerMap = new PlayerMap(); -- this.entityMap = new Int2ObjectOpenHashMap(); -- this.chunkTypeCache = new Long2ByteOpenHashMap(); -- this.chunkSaveCooldowns = new Long2LongOpenHashMap(); -+ this.entityMap = Maps.newConcurrentMap(); -+ this.chunkTypeCache = Long2ByteMaps.synchronize(new Long2ByteOpenHashMap()); -+ this.chunkSaveCooldowns = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); - this.unloadQueue = Queues.newConcurrentLinkedQueue(); - this.structureTemplateManager = structureTemplateManager; - Path path = session.getDimensionPath(world.dimension()); -@@ -1183,8 +1136,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot - // Paper start - ignore and warn about illegal addEntity calls instead of crashing server - if (!entity.valid || entity.level != this.level || this.entityMap.containsKey(entity.getId())) { -- LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName() -- + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable()); -+ LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName() + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : "")); - return; - } - if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Delay adding to tracker until after list packets -@@ -1209,11 +1161,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - ServerPlayer entityplayer = (ServerPlayer) entity; - - this.updatePlayerStatus(entityplayer, true); -- ObjectIterator objectiterator = this.entityMap.values().iterator(); -- -- while (objectiterator.hasNext()) { -- ChunkMap.TrackedEntity playerchunkmap_entitytracker1 = (ChunkMap.TrackedEntity) objectiterator.next(); - -+ for (TrackedEntity playerchunkmap_entitytracker1 : this.entityMap.values()) { - if (playerchunkmap_entitytracker1.entity != entityplayer) { - playerchunkmap_entitytracker1.updatePlayer(entityplayer); - } -@@ -1231,11 +1180,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - ServerPlayer entityplayer = (ServerPlayer) entity; - - this.updatePlayerStatus(entityplayer, false); -- ObjectIterator objectiterator = this.entityMap.values().iterator(); -- -- while (objectiterator.hasNext()) { -- ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next(); - -+ for (TrackedEntity playerchunkmap_entitytracker : this.entityMap.values()) { - playerchunkmap_entitytracker.removePlayer(entityplayer); - } - } -@@ -1281,7 +1227,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper end - optimized tracker - List list = Lists.newArrayList(); - List list1 = this.level.players(); -- ObjectIterator objectiterator = this.entityMap.values().iterator(); -+ Iterator objectiterator = this.entityMap.values().iterator(); - //level.timings.tracker1.startTiming(); // Paper // Purpur - - ChunkMap.TrackedEntity playerchunkmap_entitytracker; -diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java -index 52cba8f68d274cce106304aef1249a95474d3238..a9ba8adc5f290f6e2820632bdae8e50165595706 100644 ---- a/src/main/java/net/minecraft/server/level/DistanceManager.java -+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java -@@ -1,42 +1,20 @@ - package net.minecraft.server.level; - --import com.google.common.annotations.VisibleForTesting; --import com.google.common.collect.ImmutableList; --import com.google.common.collect.ImmutableSet; --import com.google.common.collect.Sets; --import com.mojang.datafixers.util.Either; - import com.mojang.logging.LogUtils; --import it.unimi.dsi.fastutil.longs.Long2ByteMap; --import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; --import it.unimi.dsi.fastutil.longs.Long2IntMap; --import it.unimi.dsi.fastutil.longs.Long2IntMaps; --import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; --import it.unimi.dsi.fastutil.longs.Long2ObjectMap; --import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; --import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; --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.longs.*; - import it.unimi.dsi.fastutil.objects.ObjectIterator; --import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; - import it.unimi.dsi.fastutil.objects.ObjectSet; --import java.io.File; --import java.io.FileOutputStream; --import java.io.IOException; --import java.nio.charset.StandardCharsets; --import java.util.Iterator; --import java.util.Objects; --import java.util.Set; --import java.util.concurrent.CompletableFuture; --import java.util.concurrent.Executor; --import javax.annotation.Nullable; - import net.minecraft.core.SectionPos; - import net.minecraft.util.SortedArraySet; --import net.minecraft.util.thread.ProcessorHandle; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.chunk.ChunkStatus; --import net.minecraft.world.level.chunk.LevelChunk; - import org.slf4j.Logger; -+import javax.annotation.Nullable; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.IOException; -+import java.nio.charset.StandardCharsets; -+import java.util.concurrent.Executor; - - public abstract class DistanceManager { - -@@ -52,7 +30,7 @@ public abstract class DistanceManager { - private static final int INITIAL_TICKET_LIST_CAPACITY = 4; - private static final int ENTITY_TICKING_LEVEL_THRESHOLD = 32; - private static final int BLOCK_TICKING_LEVEL_THRESHOLD = 33; -- final Long2ObjectMap> playersPerChunk = new Long2ObjectOpenHashMap(); -+ final Long2ObjectMap> playersPerChunk = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); - // Paper - rewrite chunk system - public static final int MOB_SPAWN_RANGE = 8; // private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - no longer used - //private final TickingTracker tickingTicketsTracker = new TickingTracker(); // Paper - no longer used -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 0ae45cf5a084fd412305e8b2f5dabe608b4eb1c1..e311724d2e723115bc9549a61e6206a8aed835d8 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -4,43 +4,22 @@ import com.google.common.annotations.VisibleForTesting; - import com.google.common.collect.Lists; - 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.concurrent.CompletableFuture; --import java.util.concurrent.Executor; --import java.util.function.BooleanSupplier; --import java.util.function.Consumer; --import java.util.function.Supplier; --import javax.annotation.Nullable; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -+import it.unimi.dsi.fastutil.objects.ObjectArraySet; -+import it.unimi.dsi.fastutil.objects.ObjectSet; - import net.minecraft.Util; - import net.minecraft.core.BlockPos; - import net.minecraft.core.SectionPos; - import net.minecraft.network.protocol.Packet; - import net.minecraft.server.level.progress.ChunkProgressListener; - import net.minecraft.util.VisibleForDebug; --import net.minecraft.util.profiling.ProfilerFiller; - import net.minecraft.util.thread.BlockableEventLoop; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.ai.village.poi.PoiManager; --import net.minecraft.world.level.BlockGetter; --import net.minecraft.world.level.ChunkPos; --import net.minecraft.world.level.GameRules; --import net.minecraft.world.level.Level; --import net.minecraft.world.level.LightLayer; --import net.minecraft.world.level.LocalMobCapCalculator; --import net.minecraft.world.level.NaturalSpawner; --import net.minecraft.world.level.chunk.ChunkAccess; --import net.minecraft.world.level.chunk.ChunkGenerator; --import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; --import net.minecraft.world.level.chunk.ChunkSource; --import net.minecraft.world.level.chunk.ChunkStatus; --import net.minecraft.world.level.chunk.LevelChunk; -+import net.minecraft.world.level.*; -+import net.minecraft.world.level.chunk.*; - import net.minecraft.world.level.chunk.storage.ChunkScanAccess; - import net.minecraft.world.level.entity.ChunkStatusUpdateListener; - import net.minecraft.world.level.levelgen.RandomState; -@@ -48,7 +27,16 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelData; - import net.minecraft.world.level.storage.LevelStorageSource; --import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper -+ -+import javax.annotation.Nullable; -+import java.io.File; -+import java.io.IOException; -+import java.util.*; -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.Executor; -+import java.util.function.BooleanSupplier; -+import java.util.function.Consumer; -+import java.util.function.Supplier; - - public class ServerChunkCache extends ChunkSource { - -@@ -72,8 +60,7 @@ public class ServerChunkCache extends ChunkSource { - @VisibleForDebug - private NaturalSpawner.SpawnState lastSpawnState; - // Paper start -- final com.destroystokyo.paper.util.concurrent.WeakSeqLock loadedChunkMapSeqLock = new com.destroystokyo.paper.util.concurrent.WeakSeqLock(); -- final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap loadedChunkMap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(8192, 0.5f); -+ private final Long2ObjectMap loadedChunkMap = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(8192, 0.5f)); - - private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4]; - -@@ -85,12 +72,7 @@ public class ServerChunkCache extends ChunkSource { - } - - public void addLoadedChunk(LevelChunk chunk) { -- this.loadedChunkMapSeqLock.acquireWrite(); -- try { -- this.loadedChunkMap.put(chunk.coordinateKey, chunk); -- } finally { -- this.loadedChunkMapSeqLock.releaseWrite(); -- } -+ this.loadedChunkMap.put(chunk.coordinateKey, chunk); - - // rewrite cache if we have to - // we do this since we also cache null chunks -@@ -100,13 +82,7 @@ public class ServerChunkCache extends ChunkSource { - } - - public void removeLoadedChunk(LevelChunk chunk) { -- this.loadedChunkMapSeqLock.acquireWrite(); -- try { -- this.loadedChunkMap.remove(chunk.coordinateKey); -- } finally { -- this.loadedChunkMapSeqLock.releaseWrite(); -- } -- -+ this.loadedChunkMap.remove(chunk.coordinateKey); - // rewrite cache if we have to - // we do this since we also cache null chunks - int cacheKey = getChunkCacheKey(chunk.locX, chunk.locZ); -@@ -375,22 +351,7 @@ public class ServerChunkCache extends ChunkSource { - return this.getChunkAtIfLoadedMainThread(x, z); - } - -- LevelChunk ret = null; -- long readlock; -- do { -- readlock = this.loadedChunkMapSeqLock.acquireRead(); -- try { -- ret = this.loadedChunkMap.get(k); -- } catch (Throwable thr) { -- if (thr instanceof ThreadDeath) { -- throw (ThreadDeath)thr; -- } -- // re-try, this means a CME occurred... -- continue; -- } -- } while (!this.loadedChunkMapSeqLock.tryReleaseRead(readlock)); -- -- return ret; -+ return this.loadedChunkMap.get(k); - } - // Paper end - // Paper start - async chunk io -@@ -451,7 +412,7 @@ public class ServerChunkCache extends ChunkSource { - // Paper end - com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info - //this.level.timings.syncChunkLoad.startTiming(); // Paper // Purpur -- chunkproviderserver_b.managedBlock(completablefuture::isDone); -+ chunkproviderserver_b.managedBlock(completablefuture::isDone); - io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system - //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur - } // Paper -@@ -510,6 +471,7 @@ public class ServerChunkCache extends ChunkSource { - // Paper start - add isUrgent - old sig left in place for dirty nms plugins - return getChunkFutureMainThread(chunkX, chunkZ, leastStatus, create, false); - } -+ - private CompletableFuture> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create, boolean isUrgent) { - // Paper start - rewrite chunk system - io.papermc.paper.util.TickThread.ensureTickThread(this.level, chunkX, chunkZ, "Scheduling chunk load off-main"); -@@ -803,7 +765,7 @@ public class ServerChunkCache extends ChunkSource { - //gameprofilerfiller.popPush("broadcast"); // Purpur - //this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur - if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { -- ReferenceOpenHashSet copy = this.chunkMap.needsChangeBroadcasting.clone(); -+ List copy = new ArrayList<>(this.chunkMap.needsChangeBroadcasting); - this.chunkMap.needsChangeBroadcasting.clear(); - for (ChunkHolder holder : copy) { - holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded -@@ -848,12 +810,10 @@ public class ServerChunkCache extends ChunkSource { - if (chunkMap.playerMobDistanceMap != null && _pufferfish_spawnCountsReady.getAndSet(false)) { - net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> { - int mapped = distanceManager.getNaturalSpawnChunkCount(); -- io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator objectiterator = -- level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); -+ Iterator objectiterator = level.entityTickList.entities.iterator(); - gg.pufferfish.pufferfish.util.IterableWrapper wrappedIterator = - new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator); - lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true); -- objectiterator.finishedIterating(); - _pufferfish_spawnCountsReady.set(true); - }); - } -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 03b8ef3409fd5f7a4d4b06e13cf8eb22b3bbf8a1..39c3aaf91514bd8a2f9f04496e25a6253442939f 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1,69 +1,35 @@ - package net.minecraft.server.level; - -+import co.aikar.timings.TimingHistory; -+import co.earthme.hearse.concurrent.WorkerThread; -+import co.earthme.hearse.server.ServerEntityTickHook; - import com.google.common.annotations.VisibleForTesting; --import co.aikar.timings.TimingHistory; // Paper - import com.google.common.collect.Lists; -+import com.google.common.collect.Sets; - import com.mojang.datafixers.DataFixer; - import com.mojang.datafixers.util.Pair; - import com.mojang.logging.LogUtils; -+import io.papermc.paper.util.MCUtil; - import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; - import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -+import it.unimi.dsi.fastutil.ints.IntArrayList; - import it.unimi.dsi.fastutil.longs.LongSet; - import it.unimi.dsi.fastutil.longs.LongSets; - import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry; - import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; - import it.unimi.dsi.fastutil.objects.ObjectArrayList; - import it.unimi.dsi.fastutil.objects.ObjectIterator; --import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; --import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; --import java.io.BufferedWriter; --import java.io.IOException; --import java.io.Writer; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.ArrayList; --import java.util.Comparator; --import java.util.Iterator; --import java.util.List; --import java.util.Locale; --import java.util.Objects; --import java.util.Optional; --import java.util.Set; --import java.util.UUID; --import java.util.concurrent.Executor; --import java.util.function.BooleanSupplier; --import java.util.function.Function; --import java.util.function.Predicate; --import java.util.stream.Collectors; --import java.util.stream.Stream; --import javax.annotation.Nonnull; --import javax.annotation.Nullable; - import net.minecraft.CrashReport; - import net.minecraft.Util; --import net.minecraft.core.BlockPos; --import net.minecraft.core.Direction; --import net.minecraft.core.Holder; --import net.minecraft.core.HolderSet; --import net.minecraft.core.RegistryAccess; --import net.minecraft.core.SectionPos; -+import net.minecraft.core.*; - import net.minecraft.core.particles.ParticleOptions; - import net.minecraft.core.registries.BuiltInRegistries; - import net.minecraft.core.registries.Registries; - import net.minecraft.network.chat.Component; --import net.minecraft.network.chat.MutableComponent; - import net.minecraft.network.protocol.Packet; --import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; --import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; --import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; --import net.minecraft.network.protocol.game.ClientboundExplodePacket; --import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; --import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; --import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket; --import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; --import net.minecraft.network.protocol.game.ClientboundSoundPacket; --import net.minecraft.network.protocol.game.DebugPackets; -+import net.minecraft.network.protocol.game.*; - import net.minecraft.resources.ResourceKey; --import io.papermc.paper.util.MCUtil; - import net.minecraft.server.MinecraftServer; - import net.minecraft.server.ServerScoreboard; - import net.minecraft.server.level.progress.ChunkProgressListener; -@@ -71,21 +37,10 @@ import net.minecraft.server.players.SleepStatus; - import net.minecraft.sounds.SoundEvent; - import net.minecraft.sounds.SoundSource; - import net.minecraft.tags.TagKey; --import net.minecraft.util.AbortableIterationConsumer; --import net.minecraft.util.CsvOutput; --import net.minecraft.util.Mth; --import net.minecraft.util.ProgressListener; --import net.minecraft.util.Unit; --import net.minecraft.util.profiling.ProfilerFiller; -+import net.minecraft.util.*; - import net.minecraft.world.DifficultyInstance; - import net.minecraft.world.damagesource.DamageSource; --import net.minecraft.world.entity.Entity; --import net.minecraft.world.entity.EntityType; --import net.minecraft.world.entity.LightningBolt; --import net.minecraft.world.entity.LivingEntity; --import net.minecraft.world.entity.Mob; --import net.minecraft.world.entity.MobCategory; --import net.minecraft.world.entity.ReputationEventHandler; -+import net.minecraft.world.entity.*; - import net.minecraft.world.entity.ai.navigation.PathNavigation; - import net.minecraft.world.entity.ai.village.ReputationEventType; - import net.minecraft.world.entity.ai.village.poi.PoiManager; -@@ -102,17 +57,7 @@ import net.minecraft.world.entity.raid.Raid; - import net.minecraft.world.entity.raid.Raids; - import net.minecraft.world.flag.FeatureFlagSet; - import net.minecraft.world.item.crafting.RecipeManager; --import net.minecraft.world.level.BlockEventData; --import net.minecraft.world.level.ChunkPos; --import net.minecraft.world.level.CustomSpawner; --import net.minecraft.world.level.Explosion; --import net.minecraft.world.level.ExplosionDamageCalculator; --import net.minecraft.world.level.ForcedChunksSavedData; --import net.minecraft.world.level.GameRules; --import net.minecraft.world.level.Level; --import net.minecraft.world.level.NaturalSpawner; --import net.minecraft.world.level.StructureManager; --import net.minecraft.world.level.WorldGenLevel; -+import net.minecraft.world.level.*; - import net.minecraft.world.level.biome.Biome; - import net.minecraft.world.level.biome.BiomeSource; - import net.minecraft.world.level.block.Block; -@@ -128,12 +73,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; -@@ -158,21 +101,32 @@ import net.minecraft.world.phys.shapes.BooleanOp; - import net.minecraft.world.phys.shapes.Shapes; - import net.minecraft.world.phys.shapes.VoxelShape; - import net.minecraft.world.ticks.LevelTicks; --import org.slf4j.Logger; - import org.bukkit.Bukkit; --import org.bukkit.Location; - import org.bukkit.WeatherType; - import org.bukkit.craftbukkit.event.CraftEventFactory; - import org.bukkit.craftbukkit.generator.CustomWorldChunkManager; --import org.bukkit.craftbukkit.util.CraftNamespacedKey; - import org.bukkit.craftbukkit.util.WorldUUID; - import org.bukkit.event.entity.CreatureSpawnEvent; - import org.bukkit.event.server.MapInitializeEvent; - import org.bukkit.event.weather.LightningStrikeEvent; --import org.bukkit.event.world.GenericGameEvent; - import org.bukkit.event.world.TimeSkipEvent; --// CraftBukkit end --import it.unimi.dsi.fastutil.ints.IntArrayList; // Paper -+import org.slf4j.Logger; -+ -+import javax.annotation.Nonnull; -+import javax.annotation.Nullable; -+import java.io.BufferedWriter; -+import java.io.IOException; -+import java.io.Writer; -+import java.nio.file.Files; -+import java.nio.file.Path; -+import java.util.*; -+import java.util.concurrent.ConcurrentLinkedDeque; -+import java.util.concurrent.Executor; -+import java.util.function.BooleanSupplier; -+import java.util.function.Function; -+import java.util.function.Predicate; -+import java.util.stream.Collectors; -+import java.util.stream.Stream; - - public class ServerLevel extends Level implements WorldGenLevel { - -@@ -192,7 +146,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - public final ServerChunkCache chunkSource; - private final MinecraftServer server; - public final PrimaryLevelData serverLevelData; // CraftBukkit - type -- final EntityTickList entityTickList; -+ public final EntityTickList entityTickList; - //public final PersistentEntitySectionManager entityManager; // Paper - rewrite chunk system - private final GameEventDispatcher gameEventDispatcher; - public boolean noSave; -@@ -204,7 +158,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - final Set navigatingMobs; - volatile boolean isUpdatingNavigations; - protected final Raids raids; -- private final ObjectLinkedOpenHashSet blockEvents; -+ private final Deque blockEvents; - private final List blockEventsToReschedule; - private boolean handlingTick; - private final List customSpawners; -@@ -269,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> onLoad) { -- if (Thread.currentThread() != this.thread) { -+ if (Thread.currentThread() != this.thread && !WorkerThread.isWorker()) { - this.getChunkSource().mainThreadProcessor.execute(() -> { - this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad); - }); -@@ -536,10 +490,10 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.entityTickList = new EntityTickList(); - this.blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier()); - this.fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier()); -- this.navigatingMobs = new ObjectOpenHashSet(); -- this.blockEvents = new ObjectLinkedOpenHashSet(); -- this.blockEventsToReschedule = new ArrayList(64); -- this.dragonParts = new Int2ObjectOpenHashMap(); -+ this.navigatingMobs = Sets.newConcurrentHashSet(); -+ this.blockEvents = new ConcurrentLinkedDeque<>(); -+ this.blockEventsToReschedule = Collections.synchronizedList(new ArrayList(64)); -+ this.dragonParts = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap()); - this.tickTime = flag1; - this.server = minecraftserver; - this.customSpawners = list; -@@ -691,71 +645,15 @@ public class ServerLevel extends Level implements WorldGenLevel { - //timings.doSounds.stopTiming(); // Spigot // Purpur - this.handlingTick = false; - //gameprofilerfiller.pop(); // Purpur -- boolean flag = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players -- -- if (flag) { -- this.resetEmptyTime(); -- } -- -- if (flag || this.emptyTime++ < 300) { -- //gameprofilerfiller.push("entities"); // Purpur -- //timings.tickEntities.startTiming(); // Spigot // Purpur -- if (this.dragonFight != null) { -- //gameprofilerfiller.push("dragonFight"); // Purpur -- this.dragonFight.tick(); -- //gameprofilerfiller.pop(); // Purpur -- } -- -- org.spigotmc.ActivationRange.activateEntities(this); // Spigot -- //timings.entityTick.startTiming(); // Spigot // Purpur -- this.entityTickList.forEach((entity) -> { -- entity.activatedPriorityReset = false; // Pufferfish - DAB -- if (!entity.isRemoved()) { -- if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed -- entity.discard(); -- } else { -- //gameprofilerfiller.push("checkDespawn"); // Purpur -- entity.checkDespawn(); -- //gameprofilerfiller.pop(); // Purpur -- if (true || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { // Paper - now always true if in the ticking list -- Entity entity1 = entity.getVehicle(); -- -- if (entity1 != null) { -- if (!entity1.isRemoved() && entity1.hasPassenger(entity)) { -- return; -- } -- -- entity.stopRiding(); -- } -- -- //gameprofilerfiller.push("tick"); // Purpur -- // Pufferfish start - copied from this.guardEntityTick -- try { -- this.tickNonPassenger(entity); // Pufferfish - changed -- MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick -- } catch (Throwable throwable) { -- if (throwable instanceof ThreadDeath) throw throwable; // Paper -- // Paper start - Prevent tile entity and entity crashes -- final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); -- MinecraftServer.LOGGER.error(msg, throwable); -- getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); -- entity.discard(); -- // Paper end -- } -- // Pufferfish end -- //gameprofilerfiller.pop(); // Purpur -- } -- } -- } -- }); -- //timings.entityTick.stopTiming(); // Spigot // Purpur -- //timings.tickEntities.stopTiming(); // Spigot // Purpur -- //gameprofilerfiller.pop(); // Purpur -- this.tickBlockEntities(); -+ this.resetEmptyTime(); -+ if (this.dragonFight != null) { -+ this.dragonFight.tick(); - } -- -- //gameprofilerfiller.push("entityManagement"); // Purpur -- //this.entityManager.tick(); // Paper - rewrite chunk system -+ org.spigotmc.ActivationRange.activateEntities(this); // Spigot -+ this.entityTickList.forEach((entity) -> { -+ ServerEntityTickHook.callAsyncEntityTick(entity,this); -+ }); -+ this.tickBlockEntities(); - } - - @Override -@@ -794,7 +692,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - - } - -- private boolean shouldDiscardEntity(Entity entity) { -+ public boolean shouldDiscardEntity(Entity entity) { - return !this.server.isSpawningAnimals() && (entity instanceof Animal || entity instanceof WaterAnimal) ? true : !this.server.areNpcsEnabled() && entity instanceof Npc; - } - -@@ -993,7 +891,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"); -@@ -1228,13 +1126,10 @@ public class ServerLevel extends Level implements WorldGenLevel { - } else { entity.inactiveTick(); } // Paper - EAR 2 - //this.getProfiler().pop(); // Purpur - //} finally { timer.stopTiming(); } // Paper - timings // Purpur -- Iterator iterator = entity.getPassengers().iterator(); -- -- while (iterator.hasNext()) { -- Entity entity1 = (Entity) iterator.next(); - -- this.tickPassenger(entity, entity1); -- } -+ for (Entity entity1 : entity.getPassengers()) { -+ this.tickPassenger(entity, entity1); -+ } - // } finally { timer.stopTiming(); } // Paper - timings - move up - // Paper start - log detailed entity tick information - } finally { -@@ -1273,11 +1168,8 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - // Paper end - EAR 2 - //gameprofilerfiller.pop(); // Purpur -- Iterator iterator = passenger.getPassengers().iterator(); -- -- while (iterator.hasNext()) { -- Entity entity2 = (Entity) iterator.next(); - -+ for (Entity entity2 : passenger.getPassengers()) { - this.tickPassenger(passenger, entity2); - } - -@@ -1661,56 +1553,56 @@ public class ServerLevel extends Level implements WorldGenLevel { - - @Override - public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { -- if (this.isUpdatingNavigations) { -- String s = "recursive call to sendBlockUpdated"; -+ synchronized (this.navigatingMobs){ -+ if (this.isUpdatingNavigations) { -+ return; -+ } - -- Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated")); -- } -+ this.getChunkSource().blockChanged(pos); -+ if(this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates -+ VoxelShape voxelshape = oldState.getCollisionShape(this, pos); -+ VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); - -- this.getChunkSource().blockChanged(pos); -- if(this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates -- VoxelShape voxelshape = oldState.getCollisionShape(this, pos); -- VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); -+ if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) { -+ List list = new ObjectArrayList(); -+ Iterator iterator = this.navigatingMobs.iterator(); - -- if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) { -- List list = new ObjectArrayList(); -- Iterator iterator = this.navigatingMobs.iterator(); -+ while (iterator.hasNext()) { -+ // CraftBukkit start - fix SPIGOT-6362 -+ Mob entityinsentient; -+ try { -+ entityinsentient = (Mob) iterator.next(); -+ } catch (java.util.ConcurrentModificationException ex) { -+ // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register -+ // In this case we just run the update again across all the iterators as the chunk will then be loaded -+ // As this is a relative edge case it is much faster than copying navigators (on either read or write) -+ this.sendBlockUpdated(pos, oldState, newState, flags); -+ return; -+ } -+ // CraftBukkit end -+ PathNavigation navigationabstract = entityinsentient.getNavigation(); - -- while (iterator.hasNext()) { -- // CraftBukkit start - fix SPIGOT-6362 -- Mob entityinsentient; -- try { -- entityinsentient = (Mob) iterator.next(); -- } catch (java.util.ConcurrentModificationException ex) { -- // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register -- // In this case we just run the update again across all the iterators as the chunk will then be loaded -- // As this is a relative edge case it is much faster than copying navigators (on either read or write) -- this.sendBlockUpdated(pos, oldState, newState, flags); -- return; -- } -- // CraftBukkit end -- PathNavigation navigationabstract = entityinsentient.getNavigation(); -+ if (navigationabstract.shouldRecomputePath(pos)) { -+ list.add(navigationabstract); -+ } -+ } - -- if (navigationabstract.shouldRecomputePath(pos)) { -- list.add(navigationabstract); -- } -- } -+ try { -+ this.isUpdatingNavigations = true; -+ iterator = list.iterator(); - -- try { -- this.isUpdatingNavigations = true; -- iterator = list.iterator(); -+ while (iterator.hasNext()) { -+ PathNavigation navigationabstract1 = (PathNavigation) iterator.next(); - -- while (iterator.hasNext()) { -- PathNavigation navigationabstract1 = (PathNavigation) iterator.next(); -+ navigationabstract1.recomputePath(); -+ } -+ } finally { -+ this.isUpdatingNavigations = false; -+ } - -- navigationabstract1.recomputePath(); - } -- } finally { -- this.isUpdatingNavigations = false; -- } -- -+ } // Paper - } -- } // Paper - } - - @Override -@@ -1777,10 +1669,8 @@ public class ServerLevel extends Level implements WorldGenLevel { - - private void runBlockEvents() { - this.blockEventsToReschedule.clear(); -- -- while (!this.blockEvents.isEmpty()) { -- BlockEventData blockactiondata = (BlockEventData) this.blockEvents.removeFirst(); -- -+ BlockEventData blockactiondata; -+ while ((blockactiondata = (BlockEventData) this.blockEvents.pollFirst())!=null) { - if (this.shouldTickBlocksAt(blockactiondata.pos())) { - if (this.doBlockEvent(blockactiondata)) { - this.server.getPlayerList().broadcast((Player) null, (double) blockactiondata.pos().getX(), (double) blockactiondata.pos().getY(), (double) blockactiondata.pos().getZ(), 64.0D, this.dimension(), new ClientboundBlockEventPacket(blockactiondata.pos(), blockactiondata.block(), blockactiondata.paramA(), blockactiondata.paramB())); -diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -index 660693c6dc0ef86f4013df980b6d0c11c03e46cd..1ea9699ce1f77a551a45fc06dad55df4cc1a4f4d 100644 ---- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -+++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -@@ -1,14 +1,9 @@ - package net.minecraft.server.level; - --import com.mojang.datafixers.util.Pair; -+import ca.spottedleaf.starlight.common.light.StarLightEngine; - import com.mojang.logging.LogUtils; --import it.unimi.dsi.fastutil.objects.ObjectArrayList; --import it.unimi.dsi.fastutil.objects.ObjectList; --import it.unimi.dsi.fastutil.objects.ObjectListIterator; --import java.util.concurrent.CompletableFuture; --import java.util.concurrent.atomic.AtomicBoolean; --import java.util.function.IntSupplier; --import javax.annotation.Nullable; -+import io.papermc.paper.util.CoordinateUtils; -+import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; - import net.minecraft.Util; - import net.minecraft.core.BlockPos; - import net.minecraft.core.SectionPos; -@@ -17,21 +12,17 @@ import net.minecraft.util.thread.ProcessorMailbox; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.LightLayer; - import net.minecraft.world.level.chunk.ChunkAccess; -+import net.minecraft.world.level.chunk.ChunkStatus; - import net.minecraft.world.level.chunk.DataLayer; --import net.minecraft.world.level.chunk.LevelChunkSection; - import net.minecraft.world.level.chunk.LightChunkGetter; -+import net.minecraft.world.level.lighting.LayerLightEventListener; - import net.minecraft.world.level.lighting.LevelLightEngine; - import org.slf4j.Logger; - --// Paper start --import ca.spottedleaf.starlight.common.light.StarLightEngine; --import io.papermc.paper.util.CoordinateUtils; -+import javax.annotation.Nullable; -+import java.util.concurrent.CompletableFuture; -+import java.util.function.IntSupplier; - import java.util.function.Supplier; --import net.minecraft.world.level.lighting.LayerLightEventListener; --import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; --import it.unimi.dsi.fastutil.longs.LongArrayList; --import it.unimi.dsi.fastutil.longs.LongIterator; --import net.minecraft.world.level.chunk.ChunkStatus; - // Paper end - - public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable { -@@ -145,20 +136,30 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - return; - } - -- final int references = this.chunksBeingWorkedOn.addTo(key, 1); -+ int references; -+ synchronized (this.chunksBeingWorkedOn){ -+ references = this.chunksBeingWorkedOn.addTo(key, 1); -+ } - if (references == 0) { - final ChunkPos pos = new ChunkPos(chunkX, chunkZ); - world.getChunkSource().addRegionTicket(ca.spottedleaf.starlight.common.light.StarLightInterface.CHUNK_WORK_TICKET, pos, 0, pos); - } - - updateFuture.thenAcceptAsync((final Void ignore) -> { -- final int newReferences = this.chunksBeingWorkedOn.get(key); -+ int newReferences; -+ synchronized (this.chunksBeingWorkedOn){ -+ newReferences = this.chunksBeingWorkedOn.get(key); -+ } - if (newReferences == 1) { -- this.chunksBeingWorkedOn.remove(key); -+ synchronized (this.chunksBeingWorkedOn){ -+ this.chunksBeingWorkedOn.remove(key); -+ } - final ChunkPos pos = new ChunkPos(chunkX, chunkZ); - world.getChunkSource().removeRegionTicket(ca.spottedleaf.starlight.common.light.StarLightInterface.CHUNK_WORK_TICKET, pos, 0, pos); - } else { -- this.chunksBeingWorkedOn.put(key, newReferences - 1); -+ synchronized (this.chunksBeingWorkedOn){ -+ this.chunksBeingWorkedOn.put(key, newReferences - 1); -+ } - } - }, world.getChunkSource().chunkMap.mainThreadExecutor).whenComplete((final Void ignore, final Throwable thr) -> { - if (thr != null) { -diff --git a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java -index 50a9f33aa31e9273c7c52d4bb2b02f0f884f7ba5..d5802cfe08f92b55ff1fd41648abda9ef2b7dd20 100644 ---- a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java -+++ b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java -@@ -10,12 +10,14 @@ import java.util.Collections; - import java.util.Iterator; - import java.util.List; - import java.util.Map; -+import java.util.concurrent.CopyOnWriteArrayList; -+import java.util.stream.Collector; - import java.util.stream.Collectors; - - public class ClassInstanceMultiMap extends AbstractCollection { -- private final Map, List> byClass = Maps.newHashMap(); -+ private final Map, List> byClass = Maps.newConcurrentMap(); - private final Class baseClass; -- private final List allInstances = Lists.newArrayList(); -+ private final List allInstances = Lists.newCopyOnWriteArrayList(); - - public ClassInstanceMultiMap(Class elementType) { - this.baseClass = elementType; -@@ -59,12 +61,16 @@ public class ClassInstanceMultiMap extends AbstractCollection { - throw new IllegalArgumentException("Don't know how to search for " + type); - } else { - List list = this.byClass.computeIfAbsent(type, (typeClass) -> { -- return this.allInstances.stream().filter(typeClass::isInstance).collect(Collectors.toList()); -+ return this.allInstances.stream().filter(typeClass::isInstance).collect(toList()); - }); -- return Collections.unmodifiableCollection(list); -+ return (Collection) Collections.unmodifiableCollection(list); - } - } - -+ public static Collector> toList() { -+ return Collectors.toCollection(CopyOnWriteArrayList::new); -+ } -+ - @Override - public Iterator iterator() { - return (Iterator)(this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator())); -diff --git a/src/main/java/net/minecraft/util/ThreadingDetector.java b/src/main/java/net/minecraft/util/ThreadingDetector.java -index b6e98aaebe57453b8eceaa633a989aa24409830f..60162cccf765800c6172d1544f2cd9bcf30cbd97 100644 ---- a/src/main/java/net/minecraft/util/ThreadingDetector.java -+++ b/src/main/java/net/minecraft/util/ThreadingDetector.java -@@ -17,7 +17,7 @@ import org.slf4j.Logger; - public class ThreadingDetector { - private static final Logger LOGGER = LogUtils.getLogger(); - private final String name; -- private final Semaphore lock = new Semaphore(1); -+ private final Semaphore lock = new Semaphore(255); - private final Lock stackTraceLock = new ReentrantLock(); - @Nullable - private volatile Thread threadThatFailedToAcquire; -diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -index 83701fbfaa56a232593ee8f11a3afb8941238bfa..0eadacb873658a0c7bd9ab24f191bc75eaebcaca 100644 ---- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -@@ -3,11 +3,15 @@ package net.minecraft.util.thread; - import com.google.common.collect.ImmutableList; - import com.google.common.collect.Queues; - import com.mojang.logging.LogUtils; -+ -+import java.util.Deque; - import java.util.List; - import java.util.Queue; - import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.ConcurrentLinkedDeque; - import java.util.concurrent.Executor; - import java.util.concurrent.locks.LockSupport; -+import java.util.concurrent.locks.StampedLock; - import java.util.function.BooleanSupplier; - import java.util.function.Supplier; - import net.minecraft.util.profiling.metrics.MetricCategory; -@@ -19,7 +23,8 @@ import org.slf4j.Logger; - public abstract class BlockableEventLoop implements ProfilerMeasured, ProcessorHandle, Executor { - private final String name; - private static final Logger LOGGER = LogUtils.getLogger(); -- private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); -+ private final Deque pendingRunnables = new ConcurrentLinkedDeque<>(); -+ private final StampedLock lock = new StampedLock(); - private int blockingCount; - - protected BlockableEventLoop(String name) { -@@ -117,13 +122,14 @@ public abstract class BlockableEventLoop implements Profiler - } - - public boolean pollTask() { -- R runnable = this.pendingRunnables.peek(); -+ R runnable = this.pendingRunnables.poll(); - if (runnable == null) { - return false; - } else if (this.blockingCount == 0 && !this.shouldRun(runnable)) { -+ this.pendingRunnables.addFirst(runnable); - return false; - } else { -- this.doRunTask(this.pendingRunnables.remove()); -+ this.doRunTask(runnable); - return true; - } - } -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 8f3f7a4c2429dd3c9060a1d2f412e41a5448bf4c..4216b4bba7c601660b7c39733f626f95d9b20507 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -24,6 +24,8 @@ import java.util.function.BiConsumer; - import java.util.function.Predicate; - import java.util.stream.Stream; - import javax.annotation.Nullable; -+ -+import it.unimi.dsi.fastutil.objects.Object2DoubleMaps; - import net.minecraft.BlockUtil; - import net.minecraft.CrashReport; - import net.minecraft.CrashReportCategory; -@@ -164,76 +166,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - // Paper start -- public static RandomSource SHARED_RANDOM = new RandomRandomSource(); -- private static final class RandomRandomSource extends java.util.Random implements net.minecraft.world.level.levelgen.BitRandomSource { -- private boolean locked = false; -- -- @Override -- public synchronized void setSeed(long seed) { -- if (locked) { -- LOGGER.error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable()); -- } else { -- super.setSeed(seed); -- locked = true; -- } -- } -- -- @Override -- public RandomSource fork() { -- return new net.minecraft.world.level.levelgen.LegacyRandomSource(this.nextLong()); -- } -- -- @Override -- public net.minecraft.world.level.levelgen.PositionalRandomFactory forkPositional() { -- return new net.minecraft.world.level.levelgen.LegacyRandomSource.LegacyPositionalRandomFactory(this.nextLong()); -- } -- -- // these below are added to fix reobf issues that I don't wanna deal with right now -- @Override -- public int next(int bits) { -- return super.next(bits); -- } -- -- @Override -- public int nextInt(int origin, int bound) { -- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(origin, bound); -- } -- -- @Override -- public long nextLong() { -- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextLong(); -- } -- -- @Override -- public int nextInt() { -- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(); -- } -- -- @Override -- public int nextInt(int bound) { -- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(bound); -- } -- -- @Override -- public boolean nextBoolean() { -- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextBoolean(); -- } -- -- @Override -- public float nextFloat() { -- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextFloat(); -- } -- -- @Override -- public double nextDouble() { -- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextDouble(); -- } -- -- @Override -- public double nextGaussian() { -- return super.nextGaussian(); -- } -- } -+ public static RandomSource SHARED_RANDOM = RandomSource.create(); - // Paper end - public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - -@@ -580,14 +513,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.nextStep = 1.0F; - this.random = SHARED_RANDOM; // Paper - this.remainingFireTicks = -this.getFireImmuneTicks(); -- this.fluidHeight = new Object2DoubleArrayMap(2); -- this.fluidOnEyes = new HashSet(); -+ this.fluidHeight = Object2DoubleMaps.synchronize(new Object2DoubleArrayMap(2)); -+ this.fluidOnEyes = Sets.newConcurrentHashSet(); - this.firstTick = true; - this.levelCallback = EntityInLevelCallback.NULL; - this.packetPositionCodec = new VecDeltaCodec(); - this.uuid = Mth.createInsecureUUID(this.random); - this.stringUUID = this.uuid.toString(); -- this.tags = Sets.newHashSet(); -+ this.tags = Sets.newConcurrentHashSet(); - this.pistonDeltas = new double[]{0.0D, 0.0D, 0.0D}; - this.feetBlockState = null; - this.type = type; -@@ -4434,6 +4367,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - // Paper start - this.setPosRaw(x, y, z, false); - } -+ - public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) { - // Paper start - block invalid positions - if (!checkPosition(this, x, y, z)) { -@@ -4441,12 +4375,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - // Paper end - block invalid positions - // Paper end -- // Paper start - rewrite chunk system -- if (this.updatingSectionStatus) { -- LOGGER.error("Refusing to update position for entity " + this + " to position " + new Vec3(x, y, z) + " since it is processing a section status update", new Throwable()); -- return; -- } -- // Paper end - rewrite chunk system - // Paper start - fix MC-4 - if (this instanceof ItemEntity) { - if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixEntityPositionDesync) { -@@ -4562,10 +4490,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - public final void setRemoved(Entity.RemovalReason reason) { - // Paper start - rewrite chunk system - io.papermc.paper.util.TickThread.ensureTickThread(this, "Cannot remove entity off-main"); -- if (this.updatingSectionStatus) { -- LOGGER.warn("Entity " + this + " is currently prevented from being added/removed to world since it is processing section status updates", new Throwable()); -- return; -- } - // Paper end - rewrite chunk system - if (this.removalReason == null) { - this.removalReason = reason; -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 93c32dd39693b37efaa05af0486e1bdd298661f3..c72920a17178059a29d21e96aeef398f6e0bbbdc 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -17,6 +17,7 @@ import java.util.List; - import java.util.Map; - import java.util.Optional; - import java.util.UUID; -+import java.util.concurrent.CopyOnWriteArrayList; - import java.util.function.Predicate; - import javax.annotation.Nullable; - import net.minecraft.BlockUtil; -@@ -181,7 +182,7 @@ public abstract class LivingEntity extends Entity { - public static final float EXTRA_RENDER_CULLING_SIZE_WITH_BIG_HAT = 0.5F; - private final AttributeMap attributes; - public CombatTracker combatTracker = new CombatTracker(this); -- public final Map activeEffects = Maps.newHashMap(); -+ public final Map activeEffects = Maps.newConcurrentMap(); - private final NonNullList lastHandItemStacks; - private final NonNullList lastArmorItemStacks; - public boolean swinging; -@@ -257,7 +258,7 @@ public abstract class LivingEntity extends Entity { - // CraftBukkit start - public int expToDrop; - public boolean forceDrops; -- public ArrayList drops = new ArrayList(); -+ public List drops = new CopyOnWriteArrayList<>(); - public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes; - public boolean collides = true; - public Set collidableExemptions = new HashSet<>(); -@@ -875,7 +876,7 @@ public abstract class LivingEntity extends Entity { - - // CraftBukkit start - private boolean isTickingEffects = false; -- private List effectsToProcess = Lists.newArrayList(); -+ private List effectsToProcess = Lists.newCopyOnWriteArrayList(); - - private static class ProcessableEffect { - -@@ -1779,7 +1780,7 @@ public abstract class LivingEntity extends Entity { - } - }); // Paper end - this.postDeathDropItems(deathEvent); // Paper -- this.drops = new ArrayList<>(); -+ this.drops = new CopyOnWriteArrayList<>(); - // CraftBukkit end - - // this.dropInventory();// CraftBukkit - moved up -@@ -1963,6 +1964,10 @@ public abstract class LivingEntity extends Entity { - BlockPos blockposition = this.blockPosition(); - BlockState iblockdata = this.getFeetBlockState(); - -+ if (iblockdata == null){ -+ return false; -+ } -+ - if (iblockdata.is(BlockTags.CLIMBABLE)) { - this.lastClimbablePos = Optional.of(blockposition); - return true; -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java -index d3827215ef19f6e1e63f846d91ed00525a318c7a..71e44f4aa64b29610f424ea91a9b54b56a155736 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java -@@ -9,6 +9,8 @@ import java.util.function.BiPredicate; - import java.util.function.Function; - import java.util.stream.Collectors; - import javax.annotation.Nullable; -+ -+import net.himeki.mcmtfabric.parallelised.ConcurrentDoublyLinkedList; - import net.minecraft.core.BlockPos; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.sounds.SoundEvent; -@@ -40,7 +42,7 @@ public class LongJumpToRandomPos extends Behavior { - protected final int maxLongJumpHeight; - protected final int maxLongJumpWidth; - protected final float maxJumpVelocity; -- protected List jumpCandidates = Lists.newArrayList(); -+ protected List jumpCandidates = Lists.newCopyOnWriteArrayList(); - protected Optional initialPosition = Optional.empty(); - @Nullable - protected Vec3 chosenJump; -@@ -103,7 +105,7 @@ public class LongJumpToRandomPos extends Behavior { - return !blockPos2.equals(blockPos); - }).map((blockPos2) -> { - return new LongJumpToRandomPos.PossibleJump(blockPos2.immutable(), Mth.ceil(blockPos.distSqr(blockPos2))); -- }).collect(Collectors.toCollection(Lists::newArrayList)); -+ }).collect(Collectors.toCollection(Lists::newCopyOnWriteArrayList)); - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java -index fe3ab3d388f0481fb0db06b7f730f868dbf8e8a5..ac006bacbe8715e5c272c69afd1edab45a6511e8 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java -@@ -25,7 +25,7 @@ public class ShufflingList implements Iterable { - public ShufflingList(boolean isUnsafe) { - this.isUnsafe = isUnsafe; - // Paper end -- this.entries = Lists.newArrayList(); -+ this.entries = Lists.newCopyOnWriteArrayList(); - } - - private ShufflingList(List> list) { -@@ -35,7 +35,7 @@ public class ShufflingList implements Iterable { - private ShufflingList(List> list, boolean isUnsafe) { - this.isUnsafe = isUnsafe; - // Paper end -- this.entries = Lists.newArrayList(list); -+ this.entries = Lists.newCopyOnWriteArrayList(list); - } - - public static Codec> codec(Codec codec) { -@@ -44,12 +44,12 @@ public class ShufflingList implements Iterable { - }); - } - -- public ShufflingList add(U data, int weight) { -+ public synchronized ShufflingList add(U data, int weight) { - this.entries.add(new ShufflingList.WeightedEntry<>(data, weight)); - return this; - } - -- public ShufflingList shuffle() { -+ public synchronized ShufflingList shuffle() { - // Paper start - make concurrent safe, work off a clone of the list - List> list = this.isUnsafe ? Lists.newArrayList(this.entries) : this.entries; - list.forEach(entry -> entry.setRandom(this.random.nextFloat())); -@@ -58,17 +58,17 @@ public class ShufflingList implements Iterable { - // Paper end - } - -- public Stream stream() { -+ public synchronized Stream stream() { - return this.entries.stream().map(ShufflingList.WeightedEntry::getData); - } - - @Override -- public Iterator iterator() { -+ public synchronized Iterator iterator() { - return Iterators.transform(this.entries.iterator(), ShufflingList.WeightedEntry::getData); - } - - @Override -- public String toString() { -+ public synchronized String toString() { - return "ShufflingList[" + this.entries + "]"; - } - -@@ -90,16 +90,16 @@ public class ShufflingList implements Iterable { - this.randWeight = -Math.pow((double)random, (double)(1.0F / (float)this.weight)); - } - -- public T getData() { -+ public synchronized T getData() { - return this.data; - } - -- public int getWeight() { -+ public synchronized int getWeight() { - return this.weight; - } - - @Override -- public String toString() { -+ public synchronized String toString() { - return this.weight + ":" + this.data; - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -index 02978315bc2b828cc603ce7478408f3f82c249c2..fc26edc5082f701e6450ca9abf78423840cd773c 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -@@ -3,11 +3,8 @@ package net.minecraft.world.entity.ai.goal; - import com.google.common.annotations.VisibleForTesting; - import com.google.common.collect.Sets; - import com.mojang.logging.LogUtils; --import java.util.EnumMap; --import java.util.EnumSet; --import java.util.Iterator; --import java.util.Map; --import java.util.Set; -+ -+import java.util.*; - import java.util.function.Predicate; - import java.util.function.Supplier; - import java.util.stream.Stream; -@@ -27,8 +24,8 @@ public class GoalSelector { - return false; - } - }; -- private final Map lockedFlags = new EnumMap<>(Goal.Flag.class); -- private final Set availableGoals = Sets.newLinkedHashSet(); -+ private final Map lockedFlags = Collections.synchronizedMap(new EnumMap<>(Goal.Flag.class)); -+ private final Set availableGoals = Sets.newConcurrentHashSet(); - private final Supplier profiler; - private final EnumSet disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be. - private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector -@@ -114,14 +111,7 @@ public class GoalSelector { - } - } - -- Iterator> iterator = this.lockedFlags.entrySet().iterator(); -- -- while(iterator.hasNext()) { -- Map.Entry entry = iterator.next(); -- if (!entry.getValue().isRunning()) { -- iterator.remove(); -- } -- } -+ this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning()); - - //profilerFiller.pop(); // Purpur - //profilerFiller.push("goalUpdate"); // Purpur -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -index 8db20db72cd51046213625fac46c35854c59ec5d..4d40526cc90c19ff5a1569c8d6d828a0d0b73ccb 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -@@ -3,6 +3,7 @@ package net.minecraft.world.entity.ai.sensing; - import com.google.common.collect.ImmutableSet; - import com.mojang.datafixers.util.Pair; - import it.unimi.dsi.fastutil.longs.Long2LongMap; -+import it.unimi.dsi.fastutil.longs.Long2LongMaps; - import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; - import java.util.Optional; - import java.util.Set; -@@ -23,7 +24,7 @@ public class NearestBedSensor extends Sensor { - private static final int CACHE_TIMEOUT = 40; - private static final int BATCH_SIZE = 5; - private static final int RATE = 20; -- private final Long2LongMap batchCache = new Long2LongOpenHashMap(); -+ private final Long2LongMap batchCache = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); - private int triedCount; - private long lastUpdate; - -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java -index 9babe636176da3c40598eb5bdac0919a1704eaa0..58c6b1f67aedf5ab2167fd070604fc0d8f710435 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java -@@ -2,13 +2,14 @@ package net.minecraft.world.entity.ai.sensing; - - import it.unimi.dsi.fastutil.ints.IntOpenHashSet; - import it.unimi.dsi.fastutil.ints.IntSet; -+import it.unimi.dsi.fastutil.ints.IntSets; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.Mob; - - public class Sensing { - private final Mob mob; -- private final IntSet seen = new IntOpenHashSet(); -- private final IntSet unseen = new IntOpenHashSet(); -+ private final IntSet seen = IntSets.synchronize(new IntOpenHashSet()); -+ private final IntSet unseen = IntSets.synchronize(new IntOpenHashSet()); - - public Sensing(Mob owner) { - this.mob = owner; -diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java -index 9f138bc471b5c2a4fa813ff943dbe34018b8df74..5c8a90f8536c9291df5891d8c75de963b75ec4bd 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java -+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java -@@ -7,6 +7,7 @@ import com.mojang.logging.LogUtils; - import com.mojang.serialization.Codec; - import com.mojang.serialization.codecs.RecordCodecBuilder; - import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -+import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps; - import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; - import java.util.List; - import java.util.Map; -@@ -25,8 +26,9 @@ import org.slf4j.Logger; - - public class PoiSection { - private static final Logger LOGGER = LogUtils.getLogger(); -- private final Short2ObjectMap records = new Short2ObjectOpenHashMap<>(); -- private final Map, Set> byType = Maps.newHashMap(); public final Map, Set> getData() { return this.byType; } // Paper - public accessor -+ private final Short2ObjectMap records = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>()); -+ private final Map, Set> byType = Maps.newConcurrentMap(); -+ public final Map, Set> getData() { return this.byType; } // Paper - public accessor - private final Runnable setDirty; - private boolean isValid; - public final Optional noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system -diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -index c58496c84b2b3f86890050813041fa49711f3a01..3e8b68b4390a6086b1b1983fe6f8b545c2847ef8 100644 ---- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -@@ -4,6 +4,8 @@ import java.util.Iterator; - import java.util.List; - import java.util.Objects; - import java.util.UUID; -+import java.util.concurrent.locks.Lock; -+import java.util.concurrent.locks.ReentrantLock; - import javax.annotation.Nullable; - import net.minecraft.world.damagesource.DamageSource; - import net.minecraft.world.entity.Entity; -@@ -239,21 +241,24 @@ public class ItemEntity extends Entity { - this.setDeltaMovement(vec3d.x * 0.949999988079071D, vec3d.y + (double) (vec3d.y < 0.05999999865889549D ? 5.0E-4F : 0.0F), vec3d.z * 0.949999988079071D); - } - -+ private final Lock mergeLock = new ReentrantLock(); -+ - private void mergeWithNeighbours() { -- if (this.isMergable()) { -- // Spigot start -- double radius = level.spigotConfig.itemMerge; -- List list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> { -- // Spigot end -- return entityitem != this && entityitem.isMergable(); -- }); -- Iterator iterator = list.iterator(); -- -- while (iterator.hasNext()) { -- ItemEntity entityitem = (ItemEntity) iterator.next(); -- -- if (entityitem.isMergable()) { -- // Paper Start - Fix items merging through walls -+ if (!this.mergeLock.tryLock()){ -+ return; -+ } -+ try { -+ if (this.isMergable()) { -+ // Spigot start -+ double radius = level.spigotConfig.itemMerge; -+ List list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> { -+ // Spigot end -+ return entityitem != this && entityitem.isMergable(); -+ }); -+ -+ for (ItemEntity entityitem : list) { -+ if (entityitem.isMergable()) { -+ // Paper Start - Fix items merging through walls - if (this.level.paperConfig().fixes.fixItemsMergingThroughWalls) { - // Pufferfish start - skip the allocations - /* -@@ -263,17 +268,19 @@ public class ItemEntity extends Entity { - if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue; - */ - if (level.rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) == -- net.minecraft.world.phys.HitResult.Type.BLOCK) continue; -+ net.minecraft.world.phys.HitResult.Type.BLOCK) continue; - // Pufferfish end - } -- // Paper End -- this.tryToMerge(entityitem); -- if (this.isRemoved()) { -- break; -+ // Paper End -+ this.tryToMerge(entityitem); -+ if (this.isRemoved()) { -+ break; -+ } - } - } - } -- -+ }finally { -+ this.mergeLock.unlock(); - } - } - -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index 8450a22b0fc6e8dc5cad0f61ac52a82b3cd3791e..c55f98fb2a6aade8e0a6f60248f4e29f66f0c193 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -248,7 +248,9 @@ public final class ItemStack { - } - - private void updateEmptyCacheFlag() { -- if (this.emptyCacheFlag && this == ItemStack.EMPTY) throw new AssertionError("TRAP"); // CraftBukkit -+ if (this.emptyCacheFlag && this == ItemStack.EMPTY){ -+ return; -+ }//throw new AssertionError("TRAP"); // CraftBukkit - this.emptyCacheFlag = false; - this.emptyCacheFlag = this.isEmpty(); - } -diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index c0d39afe5b80159ed9aaca4ddd4763d707882f2e..a4e031682329e127a61a9fa03a7383cd3e58d503 100644 ---- a/src/main/java/net/minecraft/world/level/Explosion.java -+++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -4,12 +4,15 @@ import com.google.common.collect.Maps; - import com.google.common.collect.Sets; - import com.mojang.datafixers.util.Pair; - import it.unimi.dsi.fastutil.objects.ObjectArrayList; -+import it.unimi.dsi.fastutil.objects.ObjectList; - import it.unimi.dsi.fastutil.objects.ObjectListIterator; - import java.util.List; - import java.util.Map; - import java.util.Optional; - import java.util.Set; - import javax.annotation.Nullable; -+ -+import it.unimi.dsi.fastutil.objects.ObjectLists; - import net.minecraft.Util; - import net.minecraft.core.BlockPos; - import net.minecraft.core.particles.ParticleTypes; -@@ -81,9 +84,9 @@ public class Explosion { - } - - public Explosion(Level world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, double x, double y, double z, float power, boolean createFire, Explosion.BlockInteraction destructionType) { -- this.random = RandomSource.create(); -- this.toBlow = new ObjectArrayList(); -- this.hitPlayers = Maps.newHashMap(); -+ this.random = RandomSource.createThreadSafe(); -+ this.toBlow = new ObjectArrayList<>(); -+ this.hitPlayers = Maps.newConcurrentMap(); - this.level = world; - this.source = entity; - this.radius = (float) Math.max(power, 0.0); // CraftBukkit - clamp bad values -@@ -396,14 +399,10 @@ public class Explosion { - } - - if (this.fire) { -- ObjectListIterator objectlistiterator1 = this.toBlow.iterator(); -- -- while (objectlistiterator1.hasNext()) { -- BlockPos blockposition2 = (BlockPos) objectlistiterator1.next(); -- -+ for (BlockPos blockposition2 : this.toBlow) { - if (this.random.nextInt(3) == 0 && this.level.getBlockState(blockposition2).isAir() && this.level.getBlockState(blockposition2.below()).isSolidRender(this.level, blockposition2.below())) { - // CraftBukkit start - Ignition by explosion -- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), this).isCancelled()) { -+ if (!CraftEventFactory.callBlockIgniteEvent(this.level, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), this).isCancelled()) { - this.level.setBlockAndUpdate(blockposition2, BaseFireBlock.getState(this.level, blockposition2)); - } - // CraftBukkit end -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 958e0ee29915bddde2cb8ebfd578448b83e2b149..4a75c2895f4f5e3229be3c7b177445611a1e70b6 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -2,10 +2,12 @@ package net.minecraft.world.level; - - import co.aikar.timings.Timing; - import co.aikar.timings.Timings; -+import co.earthme.hearse.concurrent.WorkerThread; - import com.destroystokyo.paper.event.server.ServerExceptionEvent; - import com.destroystokyo.paper.exception.ServerInternalException; - import com.google.common.base.MoreObjects; - import com.google.common.collect.Lists; -+import com.google.common.collect.Maps; - import com.mojang.serialization.Codec; - import java.io.IOException; - import java.util.Iterator; -@@ -178,7 +180,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - private org.spigotmc.TickLimiter entityLimiter; - private org.spigotmc.TickLimiter tileLimiter; - private int tileTickPosition; -- public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions -+ public final Map explosionDensityCache = Maps.newConcurrentMap(); // Paper - Optimize explosions - public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here - - // Paper start - fix and optimise world upgrading -@@ -1116,7 +1118,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - } - // Paper end - // CraftBukkit end -- return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system -+ return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() && !WorkerThread.isWorker() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system - } - - public void setBlockEntity(BlockEntity blockEntity) { -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -index e254b2d04e4fc1dc76c26f61ea38aeb27755143f..948abd93c64a5b3679f3552945d7a9a2edaf7c3f 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -17,6 +17,9 @@ import java.util.function.Function; - import java.util.function.Supplier; - import java.util.stream.Stream; - import javax.annotation.Nullable; -+ -+import it.unimi.dsi.fastutil.shorts.ShortLists; -+import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentShortHashSet; - import net.minecraft.CrashReport; - import net.minecraft.CrashReportCategory; - import net.minecraft.ReportedException; -@@ -384,7 +387,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom - - public static ShortList getOrCreateOffsetList(ShortList[] lists, int index) { - if (lists[index] == null) { -- lists[index] = new ShortArrayList(); -+ lists[index] = ShortLists.synchronize(new ShortArrayList()); - } - - return lists[index]; -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 2e922bb844bc147224a60ef2aae33a0125e6ca4a..f1aae5aa6d2951e21e5e0f84fc87117d41876695 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -88,7 +88,7 @@ public class LevelChunk extends ChunkAccess { - private Supplier fullStatus; - @Nullable - private LevelChunk.PostLoadProcessor postLoad; -- private final Int2ObjectMap gameEventListenerRegistrySections; -+ private final Map gameEventListenerRegistrySections; - private final LevelChunkTicks blockTicks; - private final LevelChunkTicks fluidTicks; - -@@ -114,10 +114,10 @@ public class LevelChunk extends ChunkAccess { - this.setBlockNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world)); - this.setSkyNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world)); - // Paper end - rewrite light engine -- this.tickersInLevel = Maps.newHashMap(); -+ this.tickersInLevel = Maps.newConcurrentMap(); - this.clientLightReady = false; - this.level = (ServerLevel) world; // CraftBukkit - type -- this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap(); -+ this.gameEventListenerRegistrySections = Maps.newConcurrentMap(); - Heightmap.Types[] aheightmap_type = Heightmap.Types.values(); - int j = aheightmap_type.length; - -@@ -1075,10 +1075,8 @@ public class LevelChunk extends ChunkAccess { - - for (int i = 0; i < this.postProcessing.length; ++i) { - if (this.postProcessing[i] != null) { -- ShortListIterator shortlistiterator = this.postProcessing[i].iterator(); - -- while (shortlistiterator.hasNext()) { -- Short oshort = (Short) shortlistiterator.next(); -+ for (Short oshort : this.postProcessing[i]) { - BlockPos blockposition = ProtoChunk.unpackOffsetCoordinates(oshort, this.getSectionYFromSectionIndex(i), chunkcoordintpair); - BlockState iblockdata = this.getBlockState(blockposition); - FluidState fluid = iblockdata.getFluidState(); -diff --git a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java b/src/main/java/net/minecraft/world/level/entity/EntityLookup.java -index d45d832232be5017dde53816191c2b1830a0da32..f73f78e2f7c6e3eae66f7608a92854b3246e153d 100644 ---- a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java -+++ b/src/main/java/net/minecraft/world/level/entity/EntityLookup.java -@@ -8,13 +8,15 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; - import java.util.Map; - import java.util.UUID; - import javax.annotation.Nullable; -+ -+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; - import net.minecraft.util.AbortableIterationConsumer; - import org.slf4j.Logger; - - public class EntityLookup { - private static final Logger LOGGER = LogUtils.getLogger(); -- private final Int2ObjectMap byId = new Int2ObjectLinkedOpenHashMap<>(); -- private final Map byUuid = Maps.newHashMap(); -+ private final Int2ObjectMap byId = Int2ObjectMaps.synchronize(new Int2ObjectLinkedOpenHashMap<>()); -+ private final Map byUuid = Maps.newConcurrentMap(); - - public void getEntities(EntityTypeTest filter, AbortableIterationConsumer consumer) { - for(T entityAccess : this.byId.values()) { -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..2a14b665437336aa32ca14fb2137d5bb400e2e42 100644 ---- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -@@ -1,49 +1,31 @@ - package net.minecraft.world.level.entity; - --import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; --import it.unimi.dsi.fastutil.ints.Int2ObjectMap; --import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; --import java.util.function.Consumer; --import javax.annotation.Nullable; -+import com.google.common.collect.Lists; - import net.minecraft.world.entity.Entity; -+import java.util.List; -+import java.util.function.Consumer; - - public class EntityTickList { -- public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet 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 -- -- private void ensureActiveIsNotIterated() { -- // Paper - replace with better logic, do not delay removals -- -- } -+ public final List entities = Lists.newCopyOnWriteArrayList(); - - public void add(Entity entity) { - io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper -- this.ensureActiveIsNotIterated(); -- this.entities.add(entity); // Paper - replace with better logic, do not delay removals/additions -+ this.entities.add(entity); - } - - public void remove(Entity entity) { - io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist removal"); // Paper -- this.ensureActiveIsNotIterated(); -- this.entities.remove(entity); // Paper - replace with better logic, do not delay removals/additions -+ this.entities.remove(entity); - } - - public boolean contains(Entity entity) { -- return this.entities.contains(entity); // Paper - replace with better logic, do not delay removals/additions -+ return this.entities.contains(entity); - } - - public void forEach(Consumer action) { - io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist iteration"); // Paper -- // 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 iterator = this.entities.iterator(); -- try { -- while (iterator.hasNext()) { -- action.accept(iterator.next()); -- } -- } finally { -- iterator.finishedIterating(); -+ for (Entity entity : this.entities) { -+ action.accept(entity); - } -- // Paper end - replace with better logic, do not delay removals/additions - } - } -diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java -index a77985b2dd7137d8eea03909403fc08e89376d73..6bcbbbfc39432076a3d7714ecc2d05d9112d405c 100644 ---- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java -+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java -@@ -4,12 +4,8 @@ import com.google.common.collect.ImmutableList; - import com.google.common.collect.Queues; - import com.google.common.collect.Sets; - import com.mojang.logging.LogUtils; --import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.*; - import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; --import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; --import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; --import it.unimi.dsi.fastutil.longs.LongOpenHashSet; --import it.unimi.dsi.fastutil.longs.LongSet; - import it.unimi.dsi.fastutil.objects.ObjectIterator; - import java.io.IOException; - import java.io.UncheckedIOException; -@@ -39,15 +35,15 @@ import org.bukkit.craftbukkit.event.CraftEventFactory; - public class PersistentEntitySectionManager implements AutoCloseable { - - static final Logger LOGGER = LogUtils.getLogger(); -- final Set knownUuids = Sets.newHashSet(); -+ final Set knownUuids = Sets.newConcurrentHashSet(); - final LevelCallback callbacks; - public final EntityPersistentStorage permanentStorage; - private final EntityLookup visibleEntityStorage = new EntityLookup<>(); - final EntitySectionStorage sectionStorage; - private final LevelEntityGetter entityGetter; -- private final Long2ObjectMap chunkVisibility = new Long2ObjectOpenHashMap(); -- private final Long2ObjectMap chunkLoadStatuses = new Long2ObjectOpenHashMap(); -- private final LongSet chunksToUnload = new LongOpenHashSet(); -+ private final Long2ObjectMap chunkVisibility = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); -+ private final Long2ObjectMap chunkLoadStatuses = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); -+ private final LongSet chunksToUnload = LongSets.synchronize(new LongOpenHashSet()); - private final Queue> loadingInbox = Queues.newConcurrentLinkedQueue(); - - public PersistentEntitySectionManager(Class entityClass, LevelCallback handler, EntityPersistentStorage dataAccess) { -diff --git a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java -index 878a42695ecedf0c3f2e6310e3ce44c6b6c36858..b61ed4d03848f86ca5e93b0374bbf4ca05369ad2 100644 ---- a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java -+++ b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java -@@ -2,18 +2,21 @@ package net.minecraft.world.level.gameevent; - - import com.google.common.collect.Lists; - import com.google.common.collect.Sets; --import java.util.Iterator; --import java.util.List; --import java.util.Optional; --import java.util.Set; -+ -+import java.util.*; -+ -+import it.unimi.dsi.fastutil.objects.ObjectArrayList; -+import it.unimi.dsi.fastutil.objects.ObjectList; -+import it.unimi.dsi.fastutil.objects.ObjectLists; -+import net.himeki.mcmtfabric.parallelised.ConcurrentDoublyLinkedList; - import net.minecraft.network.protocol.game.DebugPackets; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.world.phys.Vec3; - - public class EuclideanGameEventListenerRegistry implements GameEventListenerRegistry { -- private final List listeners = Lists.newArrayList(); -- private final Set listenersToRemove = Sets.newHashSet(); -- private final List listenersToAdd = Lists.newArrayList(); -+ private final List listeners = new ConcurrentDoublyLinkedList<>(); -+ private final Set listenersToRemove = Sets.newConcurrentHashSet(); -+ private final List listenersToAdd = Lists.newCopyOnWriteArrayList(); - private boolean processing; - private final ServerLevel level; - -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..35de9e9a9d211b16a8b945bc512c128709ec6bfc 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(); - } - -@@ -61,21 +61,21 @@ public class LegacyRandomSource implements BitRandomSource { - } - - @Override -- public RandomSource at(int x, int y, int z) { -+ public synchronized RandomSource at(int x, int y, int z) { - long l = Mth.getSeed(x, y, z); - long m = l ^ this.seed; - return new LegacyRandomSource(m); - } - - @Override -- public RandomSource fromHashOf(String seed) { -+ public synchronized RandomSource fromHashOf(String seed) { - int i = seed.hashCode(); - return new LegacyRandomSource((long)i ^ this.seed); - } - - @VisibleForTesting - @Override -- public void parityConfigString(StringBuilder info) { -+ public synchronized void parityConfigString(StringBuilder info) { - info.append("LegacyPositionalRandomFactory{").append(this.seed).append("}"); - } - } -diff --git a/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java b/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java -index 3d5ce92c77bc107e2ec2f54dc849b99c3abf9718..88d78f77740ee436fedd5159f8bafe91c1eb5ec1 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java -+++ b/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java -@@ -1,17 +1,16 @@ - package net.minecraft.world.level.lighting; - --import it.unimi.dsi.fastutil.longs.Long2ByteMap; --import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; --import it.unimi.dsi.fastutil.longs.LongArrayList; --import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; --import it.unimi.dsi.fastutil.longs.LongList; -+import it.unimi.dsi.fastutil.longs.*; -+ -+import java.util.Deque; -+import java.util.concurrent.ConcurrentLinkedDeque; - import java.util.function.LongPredicate; - import net.minecraft.util.Mth; - - public abstract class DynamicGraphMinFixedPoint { - private static final int NO_COMPUTED_LEVEL = 255; - private final int levelCount; -- private final LongLinkedOpenHashSet[] queues; -+ private final Deque[] queues; - private final Long2ByteMap computedLevels; - private int firstQueuedLevel; - private volatile boolean hasWork; -@@ -21,17 +20,10 @@ public abstract class DynamicGraphMinFixedPoint { - throw new IllegalArgumentException("Level count must be < 254."); - } else { - this.levelCount = levelCount; -- this.queues = new LongLinkedOpenHashSet[levelCount]; -+ this.queues = new Deque[levelCount]; - - for(int i = 0; i < levelCount; ++i) { -- this.queues[i] = new LongLinkedOpenHashSet(expectedLevelSize, 0.5F) { -- protected void rehash(int i) { -- if (i > expectedLevelSize) { -- super.rehash(i); -- } -- -- } -- }; -+ this.queues[i] = new ConcurrentLinkedDeque(); - } - - this.computedLevels = new Long2ByteOpenHashMap(expectedTotalSize, 0.5F) { -@@ -191,8 +183,8 @@ public abstract class DynamicGraphMinFixedPoint { - } else { - while(this.firstQueuedLevel < this.levelCount && maxSteps > 0) { - --maxSteps; -- LongLinkedOpenHashSet longLinkedOpenHashSet = this.queues[this.firstQueuedLevel]; -- long l = longLinkedOpenHashSet.removeFirstLong(); -+ Deque longLinkedOpenHashSet = this.queues[this.firstQueuedLevel]; -+ long l = longLinkedOpenHashSet.removeFirst(); - int i = Mth.clamp(this.getLevel(l), 0, this.levelCount - 1); - if (longLinkedOpenHashSet.isEmpty()) { - this.checkFirstQueuedLevel(this.levelCount); -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java b/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java -index 27b9cefc172b391824ead382a712b8b9b1ddfe45..4b65331a9192b7ac75141183493126ee730e697e 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java -@@ -4,9 +4,9 @@ public class BinaryHeap { - private Node[] heap = new Node[128]; - private int size; - -- public Node insert(Node node) { -+ public synchronized Node insert(Node node) { - if (node.heapIdx >= 0) { -- throw new IllegalStateException("OW KNOWS!"); -+ return node; - } else { - if (this.size == this.heap.length) { - Node[] nodes = new Node[this.size << 1]; -@@ -21,15 +21,15 @@ public class BinaryHeap { - } - } - -- public void clear() { -+ public synchronized void clear() { - this.size = 0; - } - -- public Node peek() { -+ public synchronized Node peek() { - return this.heap[0]; - } - -- public Node pop() { -+ public synchronized Node pop() { - Node node = this.heap[0]; - this.heap[0] = this.heap[--this.size]; - this.heap[this.size] = null; -@@ -41,7 +41,7 @@ public class BinaryHeap { - return node; - } - -- public void remove(Node node) { -+ public synchronized void remove(Node node) { - this.heap[node.heapIdx] = this.heap[--this.size]; - this.heap[this.size] = null; - if (this.size > node.heapIdx) { -@@ -55,7 +55,7 @@ public class BinaryHeap { - node.heapIdx = -1; - } - -- public void changeCost(Node node, float weight) { -+ public synchronized void changeCost(Node node, float weight) { - float f = node.f; - node.f = weight; - if (weight < f) { -@@ -66,11 +66,14 @@ public class BinaryHeap { - - } - -- public int size() { -+ public synchronized int size() { - return this.size; - } - - private void upHeap(int index) { -+ if(index == -1){ -+ return; -+ } - Node node = this.heap[index]; - - int i; -@@ -90,6 +93,9 @@ public class BinaryHeap { - } - - private void downHeap(int index) { -+ if(index == -1){ -+ return; -+ } - Node node = this.heap[index]; - float f = node.f; - -@@ -135,11 +141,11 @@ public class BinaryHeap { - node.heapIdx = index; - } - -- public boolean isEmpty() { -+ public synchronized boolean isEmpty() { - return this.size == 0; - } - -- public Node[] getHeap() { -+ public synchronized Node[] getHeap() { - Node[] nodes = new Node[this.size()]; - System.arraycopy(this.heap, 0, nodes, 0, this.size()); - return nodes; -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java -index b0bae04ab5a93dd4cf1eeeb02bed1e508e1f2913..d427735eff0056c171591709829d0bb76f7bb6f3 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java -@@ -1,6 +1,7 @@ - package net.minecraft.world.level.pathfinder; - - import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; - import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; - import java.util.EnumSet; - import java.util.List; -@@ -15,7 +16,7 @@ import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.phys.AABB; - - public class FlyNodeEvaluator extends WalkNodeEvaluator { -- private final Long2ObjectMap pathTypeByPosCache = new Long2ObjectOpenHashMap<>(); -+ private final Long2ObjectMap pathTypeByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); - private static final float SMALL_MOB_INFLATED_START_NODE_BOUNDING_BOX = 1.5F; - private static final int MAX_START_NODE_CANDIDATES = 10; - -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java -index a8a2594b8f5b3ebf6a1f918c7d822ad35b051b17..c614bcfc2bbbbccc7c4aac9389d4780478e739d2 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java -@@ -1,6 +1,7 @@ - package net.minecraft.world.level.pathfinder; - - import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; - import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - import net.minecraft.core.BlockPos; - import net.minecraft.util.Mth; -@@ -11,7 +12,7 @@ import net.minecraft.world.level.PathNavigationRegion; - public abstract class NodeEvaluator { - protected PathNavigationRegion level; - protected Mob mob; -- protected final Int2ObjectMap nodes = new Int2ObjectOpenHashMap<>(); -+ protected final Int2ObjectMap nodes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>()); - protected int entityWidth; - protected int entityHeight; - protected int entityDepth; -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java -index a8af51a25b0f99c3a64d9150fdfcd6b818aa7581..cd2592552339a79361d2a4e7936731330e15f6fa 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java -@@ -31,7 +31,7 @@ public class PathFinder { - } - - @Nullable -- public Path findPath(PathNavigationRegion world, Mob mob, Set positions, float followRange, int distance, float rangeMultiplier) { -+ public synchronized Path findPath(PathNavigationRegion world, Mob mob, Set positions, float followRange, int distance, float rangeMultiplier) { - this.openSet.clear(); - this.nodeEvaluator.prepare(world, mob); - Node node = this.nodeEvaluator.getStart(); -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java -index 6084631b5b502279b84f190dc62fc76b770e368e..f526adbd31e65fc74af48f6137d293a7a7ceafbb 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java -@@ -2,6 +2,7 @@ package net.minecraft.world.level.pathfinder; - - import com.google.common.collect.Maps; - import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; - import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; - import java.util.Map; - import javax.annotation.Nullable; -@@ -17,7 +18,7 @@ import net.minecraft.world.level.material.FluidState; - - public class SwimNodeEvaluator extends NodeEvaluator { - private final boolean allowBreaching; -- private final Long2ObjectMap pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); -+ private final Long2ObjectMap pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); - - public SwimNodeEvaluator(boolean canJumpOutOfWater) { - this.allowBreaching = canJumpOutOfWater; -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -index 894881018c659d874f28f5744f0b8247cfecb1c1..ae06f7ef9c4b8147508984f8b46176de46171285 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -@@ -1,8 +1,10 @@ - package net.minecraft.world.level.pathfinder; - - import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; - import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; - import it.unimi.dsi.fastutil.objects.Object2BooleanMap; -+import it.unimi.dsi.fastutil.objects.Object2BooleanMaps; - import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; - import java.util.EnumSet; - import javax.annotation.Nullable; -@@ -33,8 +35,8 @@ public class WalkNodeEvaluator extends NodeEvaluator { - public static final double SPACE_BETWEEN_WALL_POSTS = 0.5D; - private static final double DEFAULT_MOB_JUMP_HEIGHT = 1.125D; - protected float oldWaterCost; -- private final Long2ObjectMap pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); -- private final Object2BooleanMap collisionCache = new Object2BooleanOpenHashMap<>(); -+ private final Long2ObjectMap pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); -+ private final Object2BooleanMap collisionCache = Object2BooleanMaps.synchronize(new Object2BooleanOpenHashMap<>()); - - @Override - public void prepare(PathNavigationRegion cachedWorld, Mob entity) { -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..a1ff442357dfea868c319fd3c10ae28e6fb81956 100644 ---- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java -+++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java -@@ -3,7 +3,11 @@ package net.minecraft.world.level.redstone; - import com.mojang.logging.LogUtils; - import java.util.ArrayDeque; - import java.util.ArrayList; -+import java.util.Deque; - import java.util.List; -+import java.util.concurrent.ConcurrentLinkedDeque; -+import java.util.concurrent.CopyOnWriteArrayList; -+import java.util.concurrent.locks.StampedLock; - import javax.annotation.Nullable; - import net.minecraft.core.BlockPos; - import net.minecraft.core.Direction; -@@ -16,8 +20,8 @@ public class CollectingNeighborUpdater implements NeighborUpdater { - private static final Logger LOGGER = LogUtils.getLogger(); - private final Level level; - private final int maxChainedNeighborUpdates; -- private final ArrayDeque stack = new ArrayDeque<>(); -- private final List addedThisLayer = new ArrayList<>(); -+ private final Deque stack = new ConcurrentLinkedDeque<>(); -+ private final List addedThisLayer = new CopyOnWriteArrayList<>(); - private int count = 0; - - public CollectingNeighborUpdater(Level world, int maxChainDepth) { -@@ -26,22 +30,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)); - } - -@@ -62,20 +66,18 @@ public class CollectingNeighborUpdater implements NeighborUpdater { - if (!bl) { - this.runUpdates(); - } -- - } - - private void runUpdates() { - try { -- while(!this.stack.isEmpty() || !this.addedThisLayer.isEmpty()) { -- for(int i = this.addedThisLayer.size() - 1; i >= 0; --i) { -+ while (!this.stack.isEmpty() || !this.addedThisLayer.isEmpty()) { -+ for (int i = this.addedThisLayer.size() - 1; i >= 0; --i) { - this.stack.push(this.addedThisLayer.get(i)); - } -- - this.addedThisLayer.clear(); - CollectingNeighborUpdater.NeighborUpdates neighborUpdates = this.stack.peek(); - -- while(this.addedThisLayer.isEmpty()) { -+ while (this.addedThisLayer.isEmpty()) { - if (!neighborUpdates.runNext(this.level)) { - this.stack.pop(); - break; -@@ -87,7 +89,6 @@ public class CollectingNeighborUpdater implements NeighborUpdater { - this.addedThisLayer.clear(); - this.count = 0; - } -- - } - - static record FullNeighborUpdate(BlockState state, BlockPos pos, Block block, BlockPos neighborPos, boolean movedByPiston) implements CollectingNeighborUpdater.NeighborUpdates { -diff --git a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java -index ac807277a6b26d140ea9873d17c7aa4fb5fe37b2..4c75f50ab0184637b72e08936ff8808ad6c6fb5f 100644 ---- a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java -+++ b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java -@@ -13,6 +13,8 @@ import java.util.function.Function; - import java.util.function.Predicate; - import java.util.stream.Stream; - import javax.annotation.Nullable; -+ -+import it.unimi.dsi.fastutil.objects.ObjectSets; - import net.minecraft.core.BlockPos; - import net.minecraft.nbt.ListTag; - import net.minecraft.world.level.ChunkPos; -@@ -21,7 +23,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon - private final Queue> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER); - @Nullable - private List> pendingTicks; -- private final Set> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); -+ private final Set> ticksPerPosition = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); - @Nullable - private BiConsumer, ScheduledTick> onTickAdded; - -@@ -29,11 +31,11 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon - private boolean dirty; - private long lastSaved = Long.MIN_VALUE; - -- public boolean isDirty(final long tick) { -+ public synchronized boolean isDirty(final long tick) { - return this.dirty || (!this.tickQueue.isEmpty() && tick != this.lastSaved); - } - -- public void clearDirty() { -+ public synchronized void clearDirty() { - this.dirty = false; - } - // Paper end - add dirty flag -@@ -50,17 +52,17 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon - - } - -- public void setOnTickAdded(@Nullable BiConsumer, ScheduledTick> tickConsumer) { -+ public synchronized void setOnTickAdded(@Nullable BiConsumer, ScheduledTick> tickConsumer) { - this.onTickAdded = tickConsumer; - } - - @Nullable -- public ScheduledTick peek() { -+ public synchronized ScheduledTick peek() { - return this.tickQueue.peek(); - } - - @Nullable -- public ScheduledTick poll() { -+ public synchronized ScheduledTick poll() { - ScheduledTick scheduledTick = this.tickQueue.poll(); - if (scheduledTick != null) { - this.dirty = true; // Paper - add dirty flag -@@ -71,7 +73,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon - } - - @Override -- public void schedule(ScheduledTick orderedTick) { -+ public synchronized void schedule(ScheduledTick orderedTick) { - if (this.ticksPerPosition.add(orderedTick)) { - this.dirty = true; // Paper - add dirty flag - this.scheduleUnchecked(orderedTick); -@@ -88,11 +90,11 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon - } - - @Override -- public boolean hasScheduledTick(BlockPos pos, T type) { -+ public synchronized boolean hasScheduledTick(BlockPos pos, T type) { - return this.ticksPerPosition.contains(ScheduledTick.probe(type, pos)); - } - -- public void removeIf(Predicate> predicate) { -+ public synchronized void removeIf(Predicate> predicate) { - Iterator> iterator = this.tickQueue.iterator(); - - while(iterator.hasNext()) { -@@ -105,17 +107,17 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon - - } - -- public Stream> getAll() { -+ public synchronized Stream> getAll() { - return this.tickQueue.stream(); - } - - @Override -- public int count() { -+ public synchronized int count() { - return this.tickQueue.size() + (this.pendingTicks != null ? this.pendingTicks.size() : 0); - } - - @Override -- public ListTag save(long l, Function function) { -+ public synchronized ListTag save(long l, Function function) { - this.lastSaved = l; // Paper - add dirty system to level ticks - ListTag listTag = new ListTag(); - if (this.pendingTicks != null) { -@@ -131,7 +133,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon - return listTag; - } - -- public void unpack(long time) { -+ public synchronized void unpack(long time) { - if (this.pendingTicks != null) { - // Paper start - add dirty system to level chunk ticks - if (this.tickQueue.isEmpty()) { -diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java -index 5dea8414964e0d2d1fb15a6baa27227e9722bfc7..2203adc2a68e7fb253e353098fd6ddad521e3a32 100644 ---- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java -+++ b/src/main/java/net/minecraft/world/ticks/LevelTicks.java -@@ -1,24 +1,9 @@ - package net.minecraft.world.ticks; - --import it.unimi.dsi.fastutil.longs.Long2LongMap; --import it.unimi.dsi.fastutil.longs.Long2LongMaps; --import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; --import it.unimi.dsi.fastutil.longs.Long2ObjectMap; --import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -+import it.unimi.dsi.fastutil.longs.*; - import it.unimi.dsi.fastutil.objects.ObjectIterator; - import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; --import java.util.ArrayDeque; --import java.util.ArrayList; --import java.util.Comparator; --import java.util.List; --import java.util.LongSummaryStatistics; --import java.util.PriorityQueue; --import java.util.Queue; --import java.util.Set; --import java.util.function.BiConsumer; --import java.util.function.LongPredicate; --import java.util.function.Predicate; --import java.util.function.Supplier; -+import it.unimi.dsi.fastutil.objects.ObjectSets; - import net.minecraft.Util; - import net.minecraft.core.BlockPos; - import net.minecraft.core.SectionPos; -@@ -26,31 +11,36 @@ import net.minecraft.core.Vec3i; - import net.minecraft.util.profiling.ProfilerFiller; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.levelgen.structure.BoundingBox; -+import java.util.*; -+import java.util.concurrent.ConcurrentLinkedDeque; -+import java.util.concurrent.CopyOnWriteArrayList; -+import java.util.function.BiConsumer; -+import java.util.function.LongPredicate; -+import java.util.function.Predicate; -+import java.util.function.Supplier; - - public class LevelTicks implements LevelTickAccess { - private static final Comparator> CONTAINER_DRAIN_ORDER = (a, b) -> { - return ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek()); - }; - private final LongPredicate tickCheck; -- private final Supplier profiler; -- private final Long2ObjectMap> allContainers = new Long2ObjectOpenHashMap<>(); -+ private final Long2ObjectMap> allContainers = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); - private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), (map) -> { - map.defaultReturnValue(Long.MAX_VALUE); - }); - private final Queue> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER); -- private final Queue> toRunThisTick = new ArrayDeque<>(); -- private final List> alreadyRunThisTick = new ArrayList<>(); -- private final Set> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); -+ private final Queue> toRunThisTick = new ConcurrentLinkedDeque<>(); -+ private final List> alreadyRunThisTick = new CopyOnWriteArrayList<>(); -+ private final Set> toRunThisTickSet = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); -+ - private final BiConsumer, ScheduledTick> chunkScheduleUpdater = (chunkTickScheduler, tick) -> { - if (tick.equals(chunkTickScheduler.peek())) { - this.updateContainerScheduling(tick); - } -- - }; - - public LevelTicks(LongPredicate tickingFutureReadyPredicate, Supplier profilerGetter) { - this.tickCheck = tickingFutureReadyPredicate; -- this.profiler = profilerGetter; - } - - public void addContainer(ChunkPos pos, LevelChunkTicks scheduler) { -@@ -123,7 +113,9 @@ public class LevelTicks implements LevelTickAccess { - entry.setValue(scheduledTick.triggerTick()); - } else if (this.tickCheck.test(l)) { - objectIterator.remove(); -- this.containersToTick.add(levelChunkTicks); -+ synchronized (this.containersToTick){ -+ this.containersToTick.add(levelChunkTicks); -+ } - } - } - } -@@ -133,27 +125,29 @@ public class LevelTicks implements LevelTickAccess { - - private void drainContainers(long time, int maxTicks) { - LevelChunkTicks levelChunkTicks; -- while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { -- ScheduledTick scheduledTick = levelChunkTicks.poll(); -- this.scheduleForThisTick(scheduledTick); -- this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks); -- ScheduledTick scheduledTick2 = levelChunkTicks.peek(); -- if (scheduledTick2 != null) { -- if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) { -- this.containersToTick.add(levelChunkTicks); -- } else { -- this.updateContainerScheduling(scheduledTick2); -+ synchronized (this.containersToTick){ -+ while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { -+ ScheduledTick scheduledTick = levelChunkTicks.poll(); -+ this.scheduleForThisTick(scheduledTick); -+ this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks); -+ ScheduledTick scheduledTick2 = levelChunkTicks.peek(); -+ if (scheduledTick2 != null) { -+ if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) { -+ this.containersToTick.add(levelChunkTicks); -+ } else { -+ this.updateContainerScheduling(scheduledTick2); -+ } - } - } - } -- - } - - private void rescheduleLeftoverContainers() { -- for(LevelChunkTicks levelChunkTicks : this.containersToTick) { -- this.updateContainerScheduling(levelChunkTicks.peek()); -+ synchronized (this.containersToTick){ -+ for(LevelChunkTicks levelChunkTicks : this.containersToTick) { -+ this.updateContainerScheduling(levelChunkTicks.peek()); -+ } - } -- - } - - private void updateContainerScheduling(ScheduledTick tick) { -@@ -201,7 +195,9 @@ public class LevelTicks implements LevelTickAccess { - - private void cleanupAfterTick() { - this.toRunThisTick.clear(); -- this.containersToTick.clear(); -+ synchronized (this.containersToTick){ -+ this.containersToTick.clear(); -+ } - this.alreadyRunThisTick.clear(); - this.toRunThisTickSet.clear(); - } diff --git a/patches/server/0046-MikuServer-Fix-some-issue-in-Entity-sensing-and-aiSt.patch b/patches/server/0046-MikuServer-Fix-some-issue-in-Entity-sensing-and-aiSt.patch new file mode 100644 index 00000000..0b739b8f --- /dev/null +++ b/patches/server/0046-MikuServer-Fix-some-issue-in-Entity-sensing-and-aiSt.patch @@ -0,0 +1,627 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +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 extends Sensor { ++ private final List 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 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 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 { ++ private final List playerList = ObjectLists.synchronize(new ObjectArrayList<>()); ++ private final AtomicBoolean calling = new AtomicBoolean(); ++ + @Override + public Set> 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 { + + @Override + protected void doTick(ServerLevel world, LivingEntity entity) { +- // Paper start - remove streams +- List 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 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 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 + } + } diff --git a/patches/server/0047-Hearse-Misc-changes.patch b/patches/server/0047-Hearse-Misc-changes.patch deleted file mode 100644 index f1c809c0..00000000 --- a/patches/server/0047-Hearse-Misc-changes.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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); diff --git a/patches/server/0047-MikuServer-Refactor-and-fix-a-CME-in-Gossip.patch b/patches/server/0047-MikuServer-Refactor-and-fix-a-CME-in-Gossip.patch new file mode 100644 index 00000000..8315a9d9 --- /dev/null +++ b/patches/server/0047-MikuServer-Refactor-and-fix-a-CME-in-Gossip.patch @@ -0,0 +1,195 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +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(), +- 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= 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 gossips = Maps.newHashMap(); ++ public final Map gossips = Maps.newConcurrentMap(); + + @VisibleForDebug + public Map> getGossipEntries() { +- Map> map = Maps.newHashMap(); ++ Map> 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(); diff --git a/patches/server/0048-Hearse-Fix-a-CME-in-AttributeMap.patch b/patches/server/0048-Hearse-Fix-a-CME-in-AttributeMap.patch deleted file mode 100644 index ffe50684..00000000 --- a/patches/server/0048-Hearse-Fix-a-CME-in-AttributeMap.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 attributes = Maps.newHashMap(); -- private final Set dirtyAttributes = Sets.newHashSet(); -+ private final Map attributes = Maps.newConcurrentMap(); -+ private final Set dirtyAttributes = Sets.newConcurrentHashSet(); - private final AttributeSupplier supplier; - private final java.util.function.Function createInstance; // Pufferfish - diff --git a/patches/server/0048-MikuServer-Add-shutdown-hook-to-async-executor-and-r.patch b/patches/server/0048-MikuServer-Add-shutdown-hook-to-async-executor-and-r.patch new file mode 100644 index 00000000..f0797c73 --- /dev/null +++ b/patches/server/0048-MikuServer-Add-shutdown-hook-to-async-executor-and-r.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +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); + } + } diff --git a/patches/server/0049-Hearse-Remove-MCMT-s-fix-in-scheduled-ticks-and-add-.patch b/patches/server/0049-Hearse-Remove-MCMT-s-fix-in-scheduled-ticks-and-add-.patch deleted file mode 100644 index 83e66337..00000000 --- a/patches/server/0049-Hearse-Remove-MCMT-s-fix-in-scheduled-ticks-and-add-.patch +++ /dev/null @@ -1,451 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 implements SerializableTickContainer, TickCon - private final Queue> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER); - @Nullable - private List> pendingTicks; -- private final Set> ticksPerPosition = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); -+ private final Set> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); - @Nullable - private BiConsumer, ScheduledTick> onTickAdded; - -@@ -40,8 +40,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon - } - // Paper end - add dirty flag - -- public LevelChunkTicks() { -- } -+ public LevelChunkTicks() {} - - public LevelChunkTicks(List> ticks) { - this.pendingTicks = ticks; -@@ -49,7 +48,6 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon - for(SavedTick savedTick : ticks) { - this.ticksPerPosition.add(ScheduledTick.probe(savedTick.type(), savedTick.pos())); - } -- - } - - public synchronized void setOnTickAdded(@Nullable BiConsumer, ScheduledTick> 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 implements LevelTickAccess { -- private static final Comparator> CONTAINER_DRAIN_ORDER = (a, b) -> { -- return ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek()); -- }; -+ private static final Comparator> CONTAINER_DRAIN_ORDER = (a, b) -> ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek()); - private final LongPredicate tickCheck; -- private final Long2ObjectMap> allContainers = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); -+ private final Long2ObjectMap> allContainers = new Long2ObjectOpenHashMap<>(); - private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), (map) -> { - map.defaultReturnValue(Long.MAX_VALUE); - }); - private final Queue> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER); -- private final Queue> toRunThisTick = new ConcurrentLinkedDeque<>(); -- private final List> alreadyRunThisTick = new CopyOnWriteArrayList<>(); -- private final Set> toRunThisTickSet = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); -+ private final Queue> toRunThisTick = new ArrayDeque<>(); -+ private final List> alreadyRunThisTick = new ArrayList<>(); -+ private final Set> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); -+ private final StampedLock ticksLock = new StampedLock(); //Hearse - - private final BiConsumer, ScheduledTick> chunkScheduleUpdater = (chunkTickScheduler, tick) -> { - if (tick.equals(chunkTickScheduler.peek())) { -@@ -44,34 +44,47 @@ public class LevelTicks implements LevelTickAccess { - } - - public void addContainer(ChunkPos pos, LevelChunkTicks scheduler) { -- long l = pos.toLong(); -- this.allContainers.put(l, scheduler); -- ScheduledTick 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 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 levelChunkTicks = this.allContainers.remove(l); -- this.nextTickForContainer.remove(l); -- if (levelChunkTicks != null) { -- levelChunkTicks.setOnTickAdded((BiConsumer, ScheduledTick>)null); -+ final long stamp = this.ticksLock.writeLock(); -+ try { -+ long l = pos.toLong(); -+ LevelChunkTicks levelChunkTicks = this.allContainers.remove(l); -+ this.nextTickForContainer.remove(l); -+ if (levelChunkTicks != null) { -+ levelChunkTicks.setOnTickAdded((BiConsumer, ScheduledTick>)null); -+ } -+ }finally { -+ this.ticksLock.unlockWrite(stamp); - } -- - } - - @Override - public void schedule(ScheduledTick orderedTick) { -- long l = ChunkPos.asLong(orderedTick.pos()); -- LevelChunkTicks 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 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 implements LevelTickAccess { - } - - private void sortContainersToTick(long time) { -- ObjectIterator 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 levelChunkTicks = this.allContainers.get(l); -- if (levelChunkTicks == null) { -- objectIterator.remove(); -- } else { -- ScheduledTick 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 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 levelChunkTicks = this.allContainers.get(l); -+ if (levelChunkTicks == null) { - objectIterator.remove(); -- synchronized (this.containersToTick){ -+ } else { -+ ScheduledTick 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 levelChunkTicks; -- synchronized (this.containersToTick){ -- while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { -+ final long stamp = this.ticksLock.writeLock(); -+ try { -+ LevelChunkTicks levelChunkTicks; -+ while (this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { - ScheduledTick scheduledTick = levelChunkTicks.poll(); - this.scheduleForThisTick(scheduledTick); - this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks); -@@ -139,14 +154,29 @@ public class LevelTicks implements LevelTickAccess { - } - } - } -+ }finally { -+ this.ticksLock.unlockWrite(stamp); - } - } - - private void rescheduleLeftoverContainers() { -- synchronized (this.containersToTick){ -- for(LevelChunkTicks levelChunkTicks : this.containersToTick) { -+ final List> cop = new ArrayList<>(); -+ long stamp = this.ticksLock.readLock(); -+ try { -+ for (LevelChunkTicks levelChunkTicks : this.containersToTick) { -+ cop.add(levelChunkTicks); -+ } -+ }finally { -+ this.ticksLock.unlockRead(stamp); -+ } -+ -+ stamp = this.ticksLock.writeLock(); -+ try { -+ for (LevelChunkTicks levelChunkTicks : cop){ - this.updateContainerScheduling(levelChunkTicks.peek()); - } -+ }finally { -+ this.ticksLock.unlockWrite(stamp); - } - } - -@@ -181,44 +211,81 @@ public class LevelTicks implements LevelTickAccess { - } - - private void runCollectedTicks(BiConsumer ticker) { -- while(!this.toRunThisTick.isEmpty()) { -- ScheduledTick 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 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 scheduledTick : cop){ - ticker.accept(scheduledTick.pos(), scheduledTick.type()); - } - -+ stamp = this.ticksLock.writeLock(); -+ try { -+ for (ScheduledTick 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 levelChunkTicks = this.allContainers.get(ChunkPos.asLong(pos)); -- return levelChunkTicks != null && levelChunkTicks.hasScheduledTick(pos, type); -+ final long stamp = this.ticksLock.readLock(); -+ try { -+ LevelChunkTicks 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 visitor) { -@@ -230,7 +297,20 @@ public class LevelTicks implements LevelTickAccess { - for(int m = i; m <= k; ++m) { - for(int n = j; n <= l; ++n) { - long o = ChunkPos.asLong(m, n); -- LevelChunkTicks levelChunkTicks = this.allContainers.get(o); -+ LevelChunkTicks 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 implements LevelTickAccess { - } - - public void clearArea(BoundingBox box) { -- Predicate> predicate = (tick) -> { -- return box.isInside(tick.pos()); -- }; -+ Predicate> predicate = (tick) -> box.isInside(tick.pos()); - this.forContainersInArea(box, (chunkPos, chunkTickScheduler) -> { -- ScheduledTick scheduledTick = chunkTickScheduler.peek(); -- chunkTickScheduler.removeIf(predicate); -- ScheduledTick 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 scheduledTick = chunkTickScheduler.peek(); -+ chunkTickScheduler.removeIf(predicate); -+ ScheduledTick 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 implements LevelTickAccess { - Predicate> 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 diff --git a/patches/server/0049-MikuServer-Fix-threading-issue-in-event-system.patch b/patches/server/0049-MikuServer-Fix-threading-issue-in-event-system.patch new file mode 100644 index 00000000..39ba20f5 --- /dev/null +++ b/patches/server/0049-MikuServer-Fix-threading-issue-in-event-system.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +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()); diff --git a/patches/server/0050-Hearse-Use-a-new-queue-in-thread-pool.patch b/patches/server/0050-Hearse-Use-a-new-queue-in-thread-pool.patch deleted file mode 100644 index aaf02dc8..00000000 --- a/patches/server/0050-Hearse-Use-a-new-queue-in-thread-pool.patch +++ /dev/null @@ -1,317 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 implements BlockingQueue { -+ private final List 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 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 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[] toArray(T1[] a) { -+ long id = this.editLock.readLock(); -+ try { -+ return this.internalList.toArray(a); -+ }finally { -+ this.editLock.unlockRead(id); -+ } -+ } -+ -+ @Override -+ public int drainTo(Collection c) { -+ throw new UnsupportedOperationException("drainTo"); -+ } -+ -+ @Override -+ public int drainTo(Collection c, int maxElements) { -+ throw new UnsupportedOperationException("drainTo"); -+ } -+} -+ diff --git a/patches/server/0051-Hearse-Refactor-some-codes.patch b/patches/server/0051-Hearse-Refactor-some-codes.patch deleted file mode 100644 index 5b267055..00000000 --- a/patches/server/0051-Hearse-Refactor-some-codes.patch +++ /dev/null @@ -1,359 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 implements BlockingQueue { - 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 implements BlockingQueue { - } - - @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 implements BlockingQueue { - } - - @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 implements BlockingQueue { - } - - @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 implements BlockingQueue { - 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 implements BlockingQueue { - } - - @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 implements BlockingQueue { - } - - @Override -- public boolean addAll(Collection c) { -+ public boolean addAll(@NotNull Collection c) { - final long id = this.editLock.writeLock(); - try { - return this.internalList.addAll(c); -@@ -156,7 +159,7 @@ public class ArrayListBlockingQueue implements BlockingQueue { - } - - @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 implements BlockingQueue { - } - - @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 implements BlockingQueue { - } - - @Override -- public T1[] toArray(T1[] a) { -+ public T1[] toArray(T1 @NotNull [] a) { - long id = this.editLock.readLock(); - try { - return this.internalList.toArray(a); -@@ -241,12 +244,12 @@ public class ArrayListBlockingQueue implements BlockingQueue { - } - - @Override -- public int drainTo(Collection c) { -+ public int drainTo(@NotNull Collection c) { - throw new UnsupportedOperationException("drainTo"); - } - - @Override -- public int drainTo(Collection c, int maxElements) { -+ public int drainTo(@NotNull Collection 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> 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) { diff --git a/patches/server/0052-Hearse-Fix-a-deadlock-in-EntityLookup.patch b/patches/server/0052-Hearse-Fix-a-deadlock-in-EntityLookup.patch deleted file mode 100644 index 49733c6d..00000000 --- a/patches/server/0052-Hearse-Fix-a-deadlock-in-EntityLookup.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 { - 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 regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f)); - -@@ -374,8 +376,11 @@ public final class EntityLookup implements LevelEntityGetter { - 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 { - 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 { - 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 { - - 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) { diff --git a/patches/server/0053-Hearse-ForkJoin-worker-support.patch b/patches/server/0053-Hearse-ForkJoin-worker-support.patch deleted file mode 100644 index bc20a163..00000000 --- a/patches/server/0053-Hearse-ForkJoin-worker-support.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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); -+ } -+} diff --git a/patches/server/0054-Hearse-Use-LinedBlockingQueue-XD.patch b/patches/server/0054-Hearse-Use-LinedBlockingQueue-XD.patch deleted file mode 100644 index 778b3f45..00000000 --- a/patches/server/0054-Hearse-Use-LinedBlockingQueue-XD.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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); diff --git a/patches/server/0055-Hearse-I-gave-up-to-fix-them.patch b/patches/server/0055-Hearse-I-gave-up-to-fix-them.patch deleted file mode 100644 index 64c3e11d..00000000 --- a/patches/server/0055-Hearse-I-gave-up-to-fix-them.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 diff --git a/patches/server/0056-Hearse-Add-entity-position-cache-to-fix-concurrent-p.patch b/patches/server/0056-Hearse-Add-entity-position-cache-to-fix-concurrent-p.patch deleted file mode 100644 index 4b8a7a8b..00000000 --- a/patches/server/0056-Hearse-Add-entity-position-cache-to-fix-concurrent-p.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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; -+ } -+} diff --git a/patches/server/0057-Hearse-Fix-a-sorting-exception-in-NearestLivingEntit.patch b/patches/server/0057-Hearse-Fix-a-sorting-exception-in-NearestLivingEntit.patch deleted file mode 100644 index 6a38719c..00000000 --- a/patches/server/0057-Hearse-Fix-a-sorting-exception-in-NearestLivingEntit.patch +++ /dev/null @@ -1,152 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 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 extends Sensor { -+ private final List 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 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 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() { diff --git a/patches/server/0058-Hearse-I-am-a-idiot.patch b/patches/server/0058-Hearse-I-am-a-idiot.patch deleted file mode 100644 index d2d8ecf0..00000000 --- a/patches/server/0058-Hearse-I-am-a-idiot.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 - } - diff --git a/patches/server/0059-Hearse-Fix-some-NPE.patch b/patches/server/0059-Hearse-Fix-some-NPE.patch deleted file mode 100644 index c8e084ba..00000000 --- a/patches/server/0059-Hearse-Fix-some-NPE.patch +++ /dev/null @@ -1,249 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 { - 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 regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f)); -+ protected final Long2ObjectMap 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 { - } - - 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 implements Iterable { -@@ -173,12 +179,12 @@ public final class EntityLookup implements LevelEntityGetter { - } - - @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 { - } - - @Override -- public synchronized void remove() { -+ public void remove() { - throw new UnsupportedOperationException(); - } - } -@@ -355,7 +361,15 @@ public final class EntityLookup implements LevelEntityGetter { - 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 { - 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 { - 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 { - - 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 { - 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 { - - 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 { - 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 { - 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(); diff --git a/patches/server/0060-Hearse-Refactor-paper-s-EntityLookup-and-fix-a-Index.patch b/patches/server/0060-Hearse-Refactor-paper-s-EntityLookup-and-fix-a-Index.patch deleted file mode 100644 index 8514d422..00000000 --- a/patches/server/0060-Hearse-Refactor-paper-s-EntityLookup-and-fix-a-Index.patch +++ /dev/null @@ -1,285 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 { - - 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 regions = new Long2ObjectOpenHashMap<>(128, 0.5f); -+ protected final Long2ObjectMap regions = new Long2ObjectArrayMap<>(); - - private final int minSection; // inclusive - private final int maxSection; // inclusive - private final LevelCallback worldCallback; -- private final Int2ReferenceMap entityById = new Int2ReferenceOpenHashMap<>(); -- private final Object2ReferenceMap entityByUUID = new Object2ReferenceOpenHashMap<>(); -+ private final List addedEntities = ObjectLists.synchronize(new ObjectArrayList<>()); - private final EntityList accessibleEntities = new EntityList(); - - public EntityLookup(final ServerLevel world, final LevelCallback worldCallback) { -@@ -82,52 +68,23 @@ public final class EntityLookup implements LevelEntityGetter { - @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 { - }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 implements Iterable { -@@ -205,7 +162,7 @@ public final class EntityLookup implements LevelEntityGetter { - - @Override - public void get(final EntityTypeTest filter, final AbortableIterationConsumer 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 { - } - } - -- 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 { - 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.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 { - // 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 playersAffectingSpawning = new java.util.ArrayList<>(); -+ public final List 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 - diff --git a/patches/server/0061-Lithium-ai.sensor.secondary_poi.patch b/patches/server/0061-Lithium-ai.sensor.secondary_poi.patch deleted file mode 100644 index 14754eaf..00000000 --- a/patches/server/0061-Lithium-ai.sensor.secondary_poi.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 { - - @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 resourceKey = world.dimension(); - BlockPos blockPos = entity.blockPosition(); - List list = Lists.newArrayList(); diff --git a/patches/server/0062-Lithium-collections.entity_filtering.patch b/patches/server/0062-Lithium-collections.entity_filtering.patch deleted file mode 100644 index 64e20fa2..00000000 --- a/patches/server/0062-Lithium-collections.entity_filtering.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 extends AbstractCollection { - public boolean add(T object) { - boolean bl = false; - -- for(Map.Entry, List> entry : this.byClass.entrySet()) { -+ for (Map.Entry, List> entry : this.byClass.entrySet()) { - if (entry.getKey().isInstance(object)) { - bl |= entry.getValue().add(object); - } -@@ -41,7 +41,7 @@ public class ClassInstanceMultiMap extends AbstractCollection { - public boolean remove(Object object) { - boolean bl = false; - -- for(Map.Entry, List> entry : this.byClass.entrySet()) { -+ for (Map.Entry, List> entry : this.byClass.entrySet()) { - if (entry.getKey().isInstance(object)) { - List list = entry.getValue(); - bl |= list.remove(object); -@@ -57,23 +57,36 @@ public class ClassInstanceMultiMap extends AbstractCollection { - } - - public Collection find(Class type) { -- if (!this.baseClass.isAssignableFrom(type)) { -- throw new IllegalArgumentException("Don't know how to search for " + type); -- } else { -- List list = this.byClass.computeIfAbsent(type, (typeClass) -> { -- return this.allInstances.stream().filter(typeClass::isInstance).collect(toList()); -- }); -- return (Collection) Collections.unmodifiableCollection(list); -+ // JettPack start -+ Collection collection = this.byClass.get(type); -+ -+ if (collection == null) { -+ collection = this.createAllOfType(type); - } -+ -+ return (Collection) Collections.unmodifiableCollection(collection); -+ // JettPack end - } - -- public static Collector> toList() { -- return Collectors.toCollection(CopyOnWriteArrayList::new); -+ // JettPack start -+ private Collection createAllOfType(Class type) { -+ List 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 iterator() { -- return (Iterator)(this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator())); -+ return (Iterator) (this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator())); - } - - public List getAllInstances() { diff --git a/patches/server/0063-Hearse-Multithreaded-tracker.patch b/patches/server/0063-Hearse-Multithreaded-tracker.patch deleted file mode 100644 index 01ddfe8f..00000000 --- a/patches/server/0063-Hearse-Multithreaded-tracker.patch +++ /dev/null @@ -1,272 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 list = Lists.newArrayList(); -- List 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 seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl -+ public final Set 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 set = ((LivingEntity) this.entity).getAttributes().getDirtyAttributes(); -+ List 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); - } -- - } - } diff --git a/patches/server/0064-Hearse-Some-concurrent-fixes.patch b/patches/server/0064-Hearse-Some-concurrent-fixes.patch deleted file mode 100644 index 07074d83..00000000 --- a/patches/server/0064-Hearse-Some-concurrent-fixes.patch +++ /dev/null @@ -1,104 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 gossips = Maps.newHashMap(); -+ public final Map gossips = Maps.newConcurrentMap(); - - @VisibleForDebug - public Map> 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 entities) { - this.nearbyEntities = entities; -- Object2BooleanOpenHashMap object2BooleanOpenHashMap = new Object2BooleanOpenHashMap<>(entities.size()); -- Predicate predicate = (entity) -> { -- return Sensor.isEntityTargetable(owner, entity); -- }; -- this.lineOfSightTest = (entity) -> { -- return object2BooleanOpenHashMap.computeIfAbsent(entity, predicate); -- }; -+ Object2BooleanMap object2BooleanOpenHashMap = Object2BooleanMaps.synchronize(new Object2BooleanOpenHashMap<>(entities.size())); -+ Predicate 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 findAll(Predicate 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 find(Predicate 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 extends Sensor - this.entitiesCache.sort(Comparator.comparingDouble(compareCache::distanceToSqr)); - - Brain brain = entity.getBrain(); -- final List list = this.entitiesCache.stream().map(EntityPositionCache::getCurrentEntity).toList(); -+ final List 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 { diff --git a/patches/server/0065-Hearse-Use-old-refacted-EntityLookup.patch b/patches/server/0065-Hearse-Use-old-refacted-EntityLookup.patch deleted file mode 100644 index 11d72fca..00000000 --- a/patches/server/0065-Hearse-Use-old-refacted-EntityLookup.patch +++ /dev/null @@ -1,399 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 { - - 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 regions = new Long2ObjectArrayMap<>(); -+ protected final Long2ObjectMap regions = Long2ObjectMaps.synchronize(new Long2ObjectArrayMap<>()); - - private final int minSection; // inclusive - private final int maxSection; // inclusive - private final LevelCallback worldCallback; -- private final List addedEntities = ObjectLists.synchronize(new ObjectArrayList<>()); -+ private final Int2ReferenceMap entityById = new Int2ReferenceArrayMap<>(); -+ private final Object2ReferenceMap entityByUUID = new Object2ReferenceArrayMap<>(); - private final EntityList accessibleEntities = new EntityList(); - - public EntityLookup(final ServerLevel world, final LevelCallback worldCallback) { -@@ -68,23 +84,52 @@ public final class EntityLookup implements LevelEntityGetter { - @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 { - } - - 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 implements Iterable { -@@ -136,12 +174,12 @@ public final class EntityLookup implements LevelEntityGetter { - } - - @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 { - - @Override - public void get(final EntityTypeTest filter, final AbortableIterationConsumer 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 { - } - } - -- 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 { - 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.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 { - // 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 { - - 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 { - 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 { - - 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 { - 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 { - 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(); diff --git a/patches/server/0066-Hearse-Fix-some-sort-problems-in-some-sensors.patch b/patches/server/0066-Hearse-Fix-some-sort-problems-in-some-sensors.patch deleted file mode 100644 index d424a3bf..00000000 --- a/patches/server/0066-Hearse-Fix-some-sort-problems-in-some-sensors.patch +++ /dev/null @@ -1,134 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 { - List 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 { -+ private final List playerList = ObjectLists.synchronize(new ObjectArrayList<>()); -+ private final AtomicBoolean calling = new AtomicBoolean(); -+ - @Override - public Set> 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 { - - @Override - protected void doTick(ServerLevel world, LivingEntity entity) { -- // Paper start - remove streams -- List 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 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 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 - } - } diff --git a/patches/server/0067-Hearse-Add-config-for-multithreaded-tracker.patch b/patches/server/0067-Hearse-Add-config-for-multithreaded-tracker.patch deleted file mode 100644 index d1dec2e7..00000000 --- a/patches/server/0067-Hearse-Add-config-for-multithreaded-tracker.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 diff --git a/patches/server/0068-Hearse-Correct-some-config-key-name.patch b/patches/server/0068-Hearse-Correct-some-config-key-name.patch deleted file mode 100644 index 1cf24dc4..00000000 --- a/patches/server/0068-Hearse-Correct-some-config-key-name.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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, diff --git a/patches/server/0069-Hearse-forkjoinworker-can-not-skip-the-async-check.patch b/patches/server/0069-Hearse-forkjoinworker-can-not-skip-the-async-check.patch deleted file mode 100644 index 97015256..00000000 --- a/patches/server/0069-Hearse-forkjoinworker-can-not-skip-the-async-check.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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; - } - } diff --git a/patches/server/0070-Hearse-Add-NotNull-annotation-to-EntityPositionCache.patch b/patches/server/0070-Hearse-Add-NotNull-annotation-to-EntityPositionCache.patch deleted file mode 100644 index 53e3d091..00000000 --- a/patches/server/0070-Hearse-Add-NotNull-annotation-to-EntityPositionCache.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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(); diff --git a/patches/server/0071-Hearse-Add-tracker-thread-pool-to-the-pool-managemen.patch b/patches/server/0071-Hearse-Add-tracker-thread-pool-to-the-pool-managemen.patch deleted file mode 100644 index b0fb7cad..00000000 --- a/patches/server/0071-Hearse-Add-tracker-thread-pool-to-the-pool-managemen.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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); - } diff --git a/patches/server/0072-Hearse-Add-locks-when-calling-getChunk-in-worker-thr.patch b/patches/server/0072-Hearse-Add-locks-when-calling-getChunk-in-worker-thr.patch deleted file mode 100644 index c2a26ddb..00000000 --- a/patches/server/0072-Hearse-Add-locks-when-calling-getChunk-in-worker-thr.patch +++ /dev/null @@ -1,133 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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> 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> 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); diff --git a/patches/server/0073-Hearse-Change-back-some-collections-in-CollectingNei.patch b/patches/server/0073-Hearse-Change-back-some-collections-in-CollectingNei.patch deleted file mode 100644 index 8a6ee50d..00000000 --- a/patches/server/0073-Hearse-Change-back-some-collections-in-CollectingNei.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 stack = new ConcurrentLinkedDeque<>(); -- private final List addedThisLayer = new CopyOnWriteArrayList<>(); -+ private final Deque stack = new ArrayDeque<>(); -+ private final List addedThisLayer = Lists.newArrayList(); - private int count = 0; - - public CollectingNeighborUpdater(Level world, int maxChainDepth) { diff --git a/patches/server/0074-Hearse-Port-some-C2ME-s-fixes-and-fix-a-concurrent-p.patch b/patches/server/0074-Hearse-Port-some-C2ME-s-fixes-and-fix-a-concurrent-p.patch deleted file mode 100644 index 55cd95bb..00000000 --- a/patches/server/0074-Hearse-Port-some-C2ME-s-fixes-and-fix-a-concurrent-p.patch +++ /dev/null @@ -1,156 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 seenBy = ReferenceSets.synchronize(new ReferenceOpenHashSet<>()); // Paper - optimise map impl //Hearse - multithread tracker -+ public final Set 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> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper -- ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; -+ //gameprofilerfiller.incrementCounter("getChunkCacheMiss"); // Purpur -+ CompletableFuture> 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 { diff --git a/patches/server/0075-Hearse-Fix-a-careless-error-in-PlayerChunkLoader.patch b/patches/server/0075-Hearse-Fix-a-careless-error-in-PlayerChunkLoader.patch deleted file mode 100644 index f5b18307..00000000 --- a/patches/server/0075-Hearse-Fix-a-careless-error-in-PlayerChunkLoader.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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); - } - diff --git a/patches/server/0076-Hearse-Remove-some-fixes-and-await-entity-task.patch b/patches/server/0076-Hearse-Remove-some-fixes-and-await-entity-task.patch deleted file mode 100644 index caeb3657..00000000 --- a/patches/server/0076-Hearse-Remove-some-fixes-and-await-entity-task.patch +++ /dev/null @@ -1,235 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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 -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 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 { - - 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 - } diff --git a/patches/server/0078-Hearse-Fix-some-threading-issue-in-bukkit-event-syst.patch b/patches/server/0078-Hearse-Fix-some-threading-issue-in-bukkit-event-syst.patch deleted file mode 100644 index 3a64510b..00000000 --- a/patches/server/0078-Hearse-Fix-some-threading-issue-in-bukkit-event-syst.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -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() {