9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2026-01-06 15:51:31 +00:00
Files
Leaf/patches/server/0041-Hearse-Patches.patch
2023-01-13 00:15:47 -05:00

8151 lines
300 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BuildTools <unconfigured@null.spigotmc.org>
Date: Wed, 4 Jan 2023 10:55:10 +0800
Subject: [PATCH] Hearse: Patches
Original license:
Original project: https://github.com/NaturalCodeClub/Hearse
1.Add config system
2.Parallel entity ticking(In alpha)
3.Some concurrent problems fix
diff --git a/src/main/java/co/m2ek4u/hearse/ForkJoinTickThread.java b/src/main/java/co/m2ek4u/hearse/ForkJoinTickThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f8a2669d6ad8498da7bec9be8ab419b24b83600
--- /dev/null
+++ b/src/main/java/co/m2ek4u/hearse/ForkJoinTickThread.java
@@ -0,0 +1,31 @@
+package co.m2ek4u.hearse;
+
+import it.unimi.dsi.fastutil.objects.ObjectArraySet;
+import it.unimi.dsi.fastutil.objects.ObjectSet;
+import it.unimi.dsi.fastutil.objects.ObjectSets;
+
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinWorkerThread;
+
+public class ForkJoinTickThread extends ForkJoinWorkerThread {
+ private static final ObjectSet<Thread> workerThreads = ObjectSets.synchronize(new ObjectArraySet<>());
+
+ public ForkJoinTickThread(ForkJoinPool pool) {
+ super(pool);
+ }
+
+ @Override
+ protected void onStart() {
+ workerThreads.add(this);
+ }
+
+ @Override
+ protected void onTermination(Throwable exception) {
+ workerThreads.remove(this);
+ super.onTermination(exception);
+ }
+
+ public static boolean isAnotherForKJoinTickThread(){
+ return workerThreads.contains(Thread.currentThread());
+ }
+}
diff --git a/src/main/java/co/m2ek4u/hearse/HearseConfig.java b/src/main/java/co/m2ek4u/hearse/HearseConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2760d56ede46632b0225501ca93470871b45853
--- /dev/null
+++ b/src/main/java/co/m2ek4u/hearse/HearseConfig.java
@@ -0,0 +1,45 @@
+package co.m2ek4u.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 YamlConfiguration configEntry;
+ private static final File CONFIG_FILE = new File("hearse.yml");
+
+ public static void init() {
+ configEntry = new YamlConfiguration();
+ try {
+ configEntry.load(CONFIG_FILE);
+ } catch (InvalidConfigurationException e) {
+ e.printStackTrace();
+ } catch (IOException ignored) {}
+ configEntry.options().copyDefaults(true);
+ }
+
+ public static void save(){
+ try {
+ configEntry.save(CONFIG_FILE);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ public static int getInt(String key,int defaultValue){
+ configEntry.addDefault(key,defaultValue);
+ return configEntry.getInt(key);
+ }
+
+ public static boolean getBoolean(String key,boolean defaultValue){
+ configEntry.addDefault(key,defaultValue);
+ return configEntry.getBoolean(key);
+ }
+
+ public static String getString(String key,String defaultValue){
+ configEntry.addDefault(key,defaultValue);
+ return configEntry.getString(key);
+ }
+}
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..f1043ea3fa8158b1262591abb65e3f9b1c63ba55 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,46 @@ import java.util.NoSuchElementException;
*/
public final class EntityList implements Iterable<Entity> {
- protected final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
- {
- this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
- }
-
- protected static final Entity[] EMPTY_LIST = new Entity[0];
-
- protected Entity[] entities = EMPTY_LIST;
- protected int count;
+ private final ObjectList<Entity> objectList = ObjectLists.synchronize(new ObjectArrayList<>());
public int size() {
- return this.count;
+ return this.objectList.size();
+ }
+
+ public boolean isEmpty(){
+ return this.objectList.isEmpty();
}
public boolean contains(final Entity entity) {
- return this.entityToIndex.containsKey(entity.getId());
+ return this.objectList.contains(entity);
}
public boolean remove(final Entity entity) {
- final int index = this.entityToIndex.remove(entity.getId());
- if (index == Integer.MIN_VALUE) {
- return false;
- }
-
- // move the entity at the end to this index
- final int endIndex = --this.count;
- final Entity end = this.entities[endIndex];
- if (index != endIndex) {
- // not empty after this call
- this.entityToIndex.put(end.getId(), index); // update index
- }
- this.entities[index] = end;
- this.entities[endIndex] = null;
-
- return true;
+ return this.objectList.remove(entity);
}
public boolean add(final Entity entity) {
- final int count = this.count;
- final int currIndex = this.entityToIndex.putIfAbsent(entity.getId(), count);
-
- if (currIndex != Integer.MIN_VALUE) {
- return false; // already in this list
- }
-
- Entity[] list = this.entities;
-
- if (list.length == count) {
- // resize required
- list = this.entities = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
- }
-
- list[count] = entity;
- this.count = count + 1;
-
- return true;
+ return this.objectList.add(entity);
}
public Entity getChecked(final int index) {
- if (index < 0 || index >= this.count) {
- throw new IndexOutOfBoundsException("Index: " + index + " is out of bounds, size: " + this.count);
- }
- return this.entities[index];
+ return this.objectList.get(index);
}
public Entity getUnchecked(final int index) {
- return this.entities[index];
+ return this.objectList.get(index);
}
public Entity[] getRawData() {
- return this.entities;
+ return this.objectList.toArray(Entity[]::new);
}
public void clear() {
- this.entityToIndex.clear();
- Arrays.fill(this.entities, 0, this.count, null);
- this.count = 0;
+ this.objectList.clear();
}
@Override
public Iterator<Entity> iterator() {
- return new Iterator<Entity>() {
-
- Entity lastRet;
- int current;
-
- @Override
- public boolean hasNext() {
- return this.current < EntityList.this.count;
- }
-
- @Override
- public Entity next() {
- if (this.current >= EntityList.this.count) {
- throw new NoSuchElementException();
- }
- return this.lastRet = EntityList.this.entities[this.current++];
- }
-
- @Override
- public void remove() {
- final Entity lastRet = this.lastRet;
-
- if (lastRet == null) {
- throw new IllegalStateException();
- }
- this.lastRet = null;
-
- EntityList.this.remove(lastRet);
- --this.current;
- }
- };
+ return this.objectList.iterator();
}
}
diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
index 0b060183429f4c72ec767075538477b4302bbf0d..b8d80be33322504782f309c79ca6f3389e8e77b4 100644
--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
+++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
@@ -6,10 +6,9 @@ import io.papermc.paper.configuration.GlobalConfiguration;
import io.papermc.paper.util.CoordinateUtils;
import io.papermc.paper.util.IntervalledCounter;
import io.papermc.paper.util.TickThread;
-import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
-import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap;
-import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongSet;
+import it.unimi.dsi.fastutil.objects.*;
+import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentLongLinkedOpenHashSet;
import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket;
import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket;
import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket;
@@ -22,10 +21,10 @@ import net.minecraft.world.level.chunk.LevelChunk;
import org.apache.commons.lang3.mutable.MutableObject;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.TreeSet;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;
public final class PlayerChunkLoader {
@@ -76,10 +75,10 @@ public final class PlayerChunkLoader {
}
protected final ChunkMap chunkMap;
- protected final Reference2ObjectLinkedOpenHashMap<ServerPlayer, PlayerLoaderData> playerMap = new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f);
- protected final ReferenceLinkedOpenHashSet<PlayerLoaderData> chunkSendQueue = new ReferenceLinkedOpenHashSet<>(512, 0.7f);
+ protected final Reference2ObjectMap<ServerPlayer, PlayerLoaderData> playerMap = Reference2ObjectMaps.synchronize(new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f));
+ protected final Deque<PlayerLoaderData> chunkSendQueue = new ConcurrentLinkedDeque<>();
- protected final TreeSet<PlayerLoaderData> chunkLoadQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
+ protected final NavigableSet<PlayerLoaderData> chunkLoadQueue = new ConcurrentSkipListSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
if (p1 == p2) {
return 0;
}
@@ -109,7 +108,7 @@ public final class PlayerChunkLoader {
return Integer.compare(System.identityHashCode(p1), System.identityHashCode(p2));
});
- protected final TreeSet<PlayerLoaderData> chunkSendWaitQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
+ protected final NavigableSet<PlayerLoaderData> chunkSendWaitQueue = new ConcurrentSkipListSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
if (p1 == p2) {
return 0;
}
@@ -308,8 +307,8 @@ public final class PlayerChunkLoader {
});
}
- protected final LongOpenHashSet isTargetedForPlayerLoad = new LongOpenHashSet();
- protected final LongOpenHashSet chunkTicketTracker = new LongOpenHashSet();
+ protected final LongSet isTargetedForPlayerLoad = new ConcurrentLongLinkedOpenHashSet();
+ protected final LongSet chunkTicketTracker = new ConcurrentLongLinkedOpenHashSet();
public boolean isChunkNearPlayers(final int chunkX, final int chunkZ) {
final PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInSendRange = this.broadcastMap.getObjectsInRange(chunkX, chunkZ);
@@ -523,17 +522,16 @@ public final class PlayerChunkLoader {
if (time < nextChunkSend) {
return;
}
+ PlayerLoaderData data1;
// drain entries from wait queue
- while (!this.chunkSendWaitQueue.isEmpty()) {
- final PlayerLoaderData data = this.chunkSendWaitQueue.first();
-
- if (data.nextChunkSendTarget > time) {
+ while ((data1 = this.chunkSendWaitQueue.pollFirst())!=null) {
+ if (data1.nextChunkSendTarget > time) {
break;
}
this.chunkSendWaitQueue.pollFirst();
- this.chunkSendQueue.add(data);
+ this.chunkSendQueue.add(data1);
}
if (this.chunkSendQueue.isEmpty()) {
@@ -542,10 +540,9 @@ public final class PlayerChunkLoader {
final int maxSends = this.getMaxConcurrentChunkSends();
final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time;
- for (;;) {
- if (this.chunkSendQueue.isEmpty()) {
- break;
- }
+ final Deque<PlayerLoaderData> tempCopy = new ArrayDeque<>(this.chunkSendQueue);
+ PlayerLoaderData data;
+ while ((data = tempCopy.pollFirst())!=null) {
final int currSends = concurrentChunkSends.get();
if (currSends >= maxSends) {
break;
@@ -554,19 +551,12 @@ public final class PlayerChunkLoader {
if (!concurrentChunkSends.compareAndSet(currSends, currSends + 1)) {
continue;
}
-
// send chunk
-
- final PlayerLoaderData data = this.chunkSendQueue.removeFirst();
-
+ this.chunkSendQueue.remove(data);
final ChunkPriorityHolder queuedSend = data.sendQueue.pollFirst();
if (queuedSend == null) {
concurrentChunkSends.getAndDecrement(); // we never sent, so decrease
// stop iterating over players who have nothing to send
- if (this.chunkSendQueue.isEmpty()) {
- // nothing left
- break;
- }
continue;
}
@@ -581,17 +571,18 @@ public final class PlayerChunkLoader {
this.sendingChunkCounts.addTo(data, 1);
}
+ final PlayerLoaderData finalData = data;
data.sendChunk(queuedSend.chunkX, queuedSend.chunkZ, () -> {
synchronized (this.sendingChunkCounts) {
- final int count = this.sendingChunkCounts.getInt(data);
+ final int count = this.sendingChunkCounts.getInt(finalData);
if (count == 0) {
// disconnected, so we don't need to decrement: it will be decremented for us
return;
}
if (count == 1) {
- this.sendingChunkCounts.removeInt(data);
+ this.sendingChunkCounts.removeInt(finalData);
} else {
- this.sendingChunkCounts.put(data, count - 1);
+ this.sendingChunkCounts.put(finalData, count - 1);
}
}
@@ -618,16 +609,12 @@ public final class PlayerChunkLoader {
final int maxLoads = this.getMaxChunkLoads();
final long time = System.nanoTime();
boolean updatedCounters = false;
- for (;;) {
- final PlayerLoaderData data = this.chunkLoadQueue.pollFirst();
-
+ PlayerLoaderData data;
+ while ((data = this.chunkLoadQueue.pollFirst())!=null) {
data.lastChunkLoad = time;
final ChunkPriorityHolder queuedLoad = data.loadQueue.peekFirst();
if (queuedLoad == null) {
- if (this.chunkLoadQueue.isEmpty()) {
- break;
- }
continue;
}
@@ -786,11 +773,11 @@ public final class PlayerChunkLoader {
// warning: modifications of this field must be aware that the loadQueue inside PlayerChunkLoader uses this field
// in a comparator!
- protected final ArrayDeque<ChunkPriorityHolder> loadQueue = new ArrayDeque<>();
- protected final LongOpenHashSet sentChunks = new LongOpenHashSet();
- protected final LongOpenHashSet chunksToBeSent = new LongOpenHashSet();
+ protected final Deque<ChunkPriorityHolder> loadQueue = new ConcurrentLinkedDeque<>();
+ protected final LongSet sentChunks = new ConcurrentLongLinkedOpenHashSet();
+ protected final LongSet chunksToBeSent = new ConcurrentLongLinkedOpenHashSet();
- protected final TreeSet<ChunkPriorityHolder> sendQueue = new TreeSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> {
+ protected final NavigableSet<ChunkPriorityHolder> sendQueue = new ConcurrentSkipListSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> {
final int distanceCompare = Integer.compare(p1.manhattanDistanceToPlayer, p2.manhattanDistanceToPlayer);
if (distanceCompare != 0) {
return distanceCompare;
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..0aa279028181726f2ec211915688d4434a7178d6 100644
--- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
+++ b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
@@ -6,8 +6,14 @@ import io.papermc.paper.util.CoordinateUtils;
import io.papermc.paper.util.TickThread;
import io.papermc.paper.util.WorldUtil;
import io.papermc.paper.world.ChunkEntitySlices;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceMaps;
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import net.minecraft.core.BlockPos;
import io.papermc.paper.chunk.system.ChunkSystem;
@@ -26,11 +32,8 @@ import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.UUID;
+
+import java.util.*;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -46,15 +49,15 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public final ServerLevel world;
private final StampedLock stateLock = new StampedLock();
- protected final Long2ObjectOpenHashMap<ChunkSlicesRegion> regions = new Long2ObjectOpenHashMap<>(128, 0.5f);
+ protected final Long2ObjectMap<ChunkSlicesRegion> regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f));
private final int minSection; // inclusive
private final int maxSection; // inclusive
private final LevelCallback<Entity> worldCallback;
private final StampedLock entityByLock = new StampedLock();
- private final Int2ReferenceOpenHashMap<Entity> entityById = new Int2ReferenceOpenHashMap<>();
- private final Object2ReferenceOpenHashMap<UUID, Entity> entityByUUID = new Object2ReferenceOpenHashMap<>();
+ private final Map<Integer,Entity> entityById = Int2ReferenceMaps.synchronize(new Int2ReferenceOpenHashMap<>());
+ private final Object2ReferenceMap<UUID, Entity> entityByUUID = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenHashMap<>());
private final EntityList accessibleEntities = new EntityList();
public EntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
@@ -208,8 +211,8 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public void get(final AABB box, final Consumer<Entity> action) {
List<Entity> entities = new ArrayList<>();
this.getEntitiesWithoutDragonParts(null, box, entities, null);
- for (int i = 0, len = entities.size(); i < len; ++i) {
- action.accept(entities.get(i));
+ for (Entity entity : entities) {
+ action.accept(entity);
}
}
@@ -217,8 +220,8 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AABB box, final AbortableIterationConsumer<U> action) {
List<Entity> entities = new ArrayList<>();
this.getEntitiesWithoutDragonParts(null, box, entities, null);
- for (int i = 0, len = entities.size(); i < len; ++i) {
- final U casted = filter.tryCast(entities.get(i));
+ for (Entity entity : entities) {
+ final U casted = filter.tryCast(entity);
if (casted != null && action.accept(casted).shouldAbort()) {
break;
}
@@ -231,72 +234,59 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
if (entity.updatingSectionStatus) {
// recursive status update
- LOGGER.error("Cannot recursively update entity chunk status for entity " + entity, new Throwable());
- 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 recursively update entity chunk status for entity " + entity);
return;
}
+ final Boolean ticketBlockBefore = this.world.chunkTaskScheduler.chunkHolderManager.blockTicketUpdates();
try {
- final Boolean ticketBlockBefore = this.world.chunkTaskScheduler.chunkHolderManager.blockTicketUpdates();
+ entity.updatingSectionStatus = true;
try {
- entity.updatingSectionStatus = true;
- try {
- if (created) {
- EntityLookup.this.worldCallback.onCreated(entity);
- }
+ if (created) {
+ EntityLookup.this.worldCallback.onCreated(entity);
+ }
- if (oldVisibility == newVisibility) {
- if (moved && newVisibility.isAccessible()) {
- EntityLookup.this.worldCallback.onSectionChange(entity);
- }
- return;
+ if (oldVisibility == newVisibility) {
+ if (moved && newVisibility.isAccessible()) {
+ EntityLookup.this.worldCallback.onSectionChange(entity);
}
+ return;
+ }
- if (newVisibility.ordinal() > oldVisibility.ordinal()) {
- // status upgrade
- if (!oldVisibility.isAccessible() && newVisibility.isAccessible()) {
- this.accessibleEntities.add(entity);
- EntityLookup.this.worldCallback.onTrackingStart(entity);
- }
-
- if (!oldVisibility.isTicking() && newVisibility.isTicking()) {
- EntityLookup.this.worldCallback.onTickingStart(entity);
- }
- } else {
- // status downgrade
- if (oldVisibility.isTicking() && !newVisibility.isTicking()) {
- EntityLookup.this.worldCallback.onTickingEnd(entity);
- }
-
- if (oldVisibility.isAccessible() && !newVisibility.isAccessible()) {
- this.accessibleEntities.remove(entity);
- EntityLookup.this.worldCallback.onTrackingEnd(entity);
- }
+ if (newVisibility.ordinal() > oldVisibility.ordinal()) {
+ // status upgrade
+ if (!oldVisibility.isAccessible() && newVisibility.isAccessible()) {
+ this.accessibleEntities.add(entity);
+ EntityLookup.this.worldCallback.onTrackingStart(entity);
}
- if (moved && newVisibility.isAccessible()) {
- EntityLookup.this.worldCallback.onSectionChange(entity);
+ if (!oldVisibility.isTicking() && newVisibility.isTicking()) {
+ EntityLookup.this.worldCallback.onTickingStart(entity);
+ }
+ } else {
+ // status downgrade
+ if (oldVisibility.isTicking() && !newVisibility.isTicking()) {
+ EntityLookup.this.worldCallback.onTickingEnd(entity);
}
- if (destroyed) {
- EntityLookup.this.worldCallback.onDestroyed(entity);
+ if (oldVisibility.isAccessible() && !newVisibility.isAccessible()) {
+ this.accessibleEntities.remove(entity);
+ EntityLookup.this.worldCallback.onTrackingEnd(entity);
}
- } finally {
- entity.updatingSectionStatus = false;
+ }
+
+ if (moved && newVisibility.isAccessible()) {
+ EntityLookup.this.worldCallback.onSectionChange(entity);
+ }
+
+ if (destroyed) {
+ EntityLookup.this.worldCallback.onDestroyed(entity);
}
} finally {
- this.world.chunkTaskScheduler.chunkHolderManager.unblockTicketUpdates(ticketBlockBefore);
+ entity.updatingSectionStatus = false;
}
} finally {
- if (slices != null) {
- slices.stopPreventingStatusUpdates(false);
- }
+ this.world.chunkTaskScheduler.chunkHolderManager.unblockTicketUpdates(ticketBlockBefore);
}
}
@@ -305,20 +295,20 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
}
public void addLegacyChunkEntities(final List<Entity> entities) {
- for (int i = 0, len = entities.size(); i < len; ++i) {
- this.addEntity(entities.get(i), true);
+ for (Entity entity : entities) {
+ this.addEntity(entity, true);
}
}
public void addEntityChunkEntities(final List<Entity> entities) {
- for (int i = 0, len = entities.size(); i < len; ++i) {
- this.addEntity(entities.get(i), true);
+ for (Entity entity : entities) {
+ this.addEntity(entity, true);
}
}
public void addWorldGenChunkEntities(final List<Entity> entities) {
- for (int i = 0, len = entities.size(); i < len; ++i) {
- this.addEntity(entities.get(i), false);
+ for (Entity entity : entities) {
+ this.addEntity(entity, false);
}
}
@@ -346,11 +336,6 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
return false;
}
- if (entity.updatingSectionStatus) {
- LOGGER.warn("Entity " + entity + " is currently prevented from being added/removed to world since it is processing section status updates", new Throwable());
- return false;
- }
-
if (fromDisk) {
ChunkSystem.onEntityPreAdd(this.world, entity);
if (entity.isRemoved()) {
diff --git a/src/main/java/io/papermc/paper/util/CachedLists.java b/src/main/java/io/papermc/paper/util/CachedLists.java
index e08f4e39db4ee3fed62e37364d17dcc5c5683504..22a5470862acfa470c9acd97c96fea29aede0b68 100644
--- a/src/main/java/io/papermc/paper/util/CachedLists.java
+++ b/src/main/java/io/papermc/paper/util/CachedLists.java
@@ -1,5 +1,6 @@
package io.papermc.paper.util;
+import com.google.common.collect.Lists;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import org.bukkit.Bukkit;
@@ -8,50 +9,18 @@ import java.util.List;
public final class CachedLists {
- // Paper start - optimise collisions
- static final UnsafeList<AABB> TEMP_COLLISION_LIST = new UnsafeList<>(1024);
- static boolean tempCollisionListInUse;
-
- public static UnsafeList<AABB> getTempCollisionList() {
- if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) {
- return new UnsafeList<>(16);
- }
- tempCollisionListInUse = true;
- return TEMP_COLLISION_LIST;
- }
-
- public static void returnTempCollisionList(List<AABB> list) {
- if (list != TEMP_COLLISION_LIST) {
- return;
- }
- ((UnsafeList)list).setSize(0);
- tempCollisionListInUse = false;
+ public static List<AABB> getTempCollisionList() {
+ return Lists.newCopyOnWriteArrayList();
}
- static final UnsafeList<Entity> TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024);
- static boolean tempGetEntitiesListInUse;
+ public static void returnTempCollisionList(List<AABB> list) {}
- public static UnsafeList<Entity> getTempGetEntitiesList() {
- if (!Bukkit.isPrimaryThread() || tempGetEntitiesListInUse) {
- return new UnsafeList<>(16);
- }
- tempGetEntitiesListInUse = true;
- return TEMP_GET_ENTITIES_LIST;
+ public static List<Entity> getTempGetEntitiesList() {
+ return Lists.newCopyOnWriteArrayList();
}
- public static void returnTempGetEntitiesList(List<Entity> list) {
- if (list != TEMP_GET_ENTITIES_LIST) {
- return;
- }
- ((UnsafeList)list).setSize(0);
- tempGetEntitiesListInUse = false;
- }
+ public static void returnTempGetEntitiesList(List<Entity> list) {}
// Paper end - optimise collisions
- public static void reset() {
- // Paper start - optimise collisions
- TEMP_COLLISION_LIST.completeReset();
- TEMP_GET_ENTITIES_LIST.completeReset();
- // Paper end - optimise collisions
- }
+ public static void reset() {}
}
diff --git a/src/main/java/io/papermc/paper/util/TickThread.java b/src/main/java/io/papermc/paper/util/TickThread.java
index fc57850b80303fcade89ca95794f63910404a407..da6c6b6691c9645722e5fc6a1319e9c07c130d3d 100644
--- a/src/main/java/io/papermc/paper/util/TickThread.java
+++ b/src/main/java/io/papermc/paper/util/TickThread.java
@@ -1,9 +1,12 @@
package io.papermc.paper.util;
+import co.m2ek4u.hearse.ForkJoinTickThread;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import org.bukkit.Bukkit;
+
+import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.atomic.AtomicInteger;
public class TickThread extends Thread {
@@ -74,14 +77,14 @@ public class TickThread extends Thread {
}
public static boolean isTickThread() {
- return Thread.currentThread() instanceof TickThread;
+ return Thread.currentThread() instanceof TickThread || ForkJoinTickThread.isAnotherForKJoinTickThread();
}
public static boolean isTickThreadFor(final ServerLevel world, final int chunkX, final int chunkZ) {
- return Thread.currentThread() instanceof TickThread;
+ return Thread.currentThread() instanceof TickThread || ForkJoinTickThread.isAnotherForKJoinTickThread();
}
public static boolean isTickThreadFor(final Entity entity) {
- return Thread.currentThread() instanceof TickThread;
+ return Thread.currentThread() instanceof TickThread || ForkJoinTickThread.isAnotherForKJoinTickThread();
}
}
diff --git a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java
index 0fd814f1d65c111266a2b20f86561839a4cef755..fe4d76875462ac9d408c972b968647af78f2ed14 100644
--- a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java
+++ b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java
@@ -94,7 +94,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
return 1.0 - ((double)this.indexMap.size() / (double)this.listSize);
}
- public int createRawIterator() {
+ public synchronized int createRawIterator() {
if (this.allowSafeIteration()) {
++this.iteratorCount;
}
@@ -105,7 +105,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
}
- public int advanceRawIterator(final int index) {
+ public synchronized int advanceRawIterator(final int index) {
final E[] elements = this.listElements;
int ret = index + 1;
for (int len = this.listSize; ret < len; ++ret) {
@@ -117,7 +117,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
return -1;
}
- public void finishRawIterator() {
+ public synchronized void finishRawIterator() {
if (this.allowSafeIteration() && --this.iteratorCount == 0) {
if (this.getFragFactor() >= this.maxFragFactor) {
this.defrag();
@@ -125,7 +125,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
}
- public boolean remove(final E element) {
+ public synchronized boolean remove(final E element) {
final int index = this.indexMap.removeInt(element);
if (index >= 0) {
if (this.firstInvalidIndex < 0 || index < this.firstInvalidIndex) {
@@ -144,11 +144,11 @@ public final class IteratorSafeOrderedReferenceSet<E> {
return false;
}
- public boolean contains(final E element) {
+ public synchronized boolean contains(final E element) {
return this.indexMap.containsKey(element);
}
- public boolean add(final E element) {
+ public synchronized boolean add(final E element) {
final int listSize = this.listSize;
final int previous = this.indexMap.putIfAbsent(element, listSize);
@@ -223,30 +223,30 @@ public final class IteratorSafeOrderedReferenceSet<E> {
//this.check();
}
- public E rawGet(final int index) {
+ public synchronized E rawGet(final int index) {
return this.listElements[index];
}
- public int size() {
+ public synchronized int size() {
// always returns the correct amount - listSize can be different
return this.indexMap.size();
}
- public IteratorSafeOrderedReferenceSet.Iterator<E> iterator() {
+ public synchronized IteratorSafeOrderedReferenceSet.Iterator<E> iterator() {
return this.iterator(0);
}
- public IteratorSafeOrderedReferenceSet.Iterator<E> iterator(final int flags) {
+ public synchronized IteratorSafeOrderedReferenceSet.Iterator<E> iterator(final int flags) {
if (this.allowSafeIteration()) {
++this.iteratorCount;
}
return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize);
}
- public java.util.Iterator<E> unsafeIterator() {
+ public synchronized java.util.Iterator<E> unsafeIterator() {
return this.unsafeIterator(0);
}
- public java.util.Iterator<E> unsafeIterator(final int flags) {
+ public synchronized java.util.Iterator<E> unsafeIterator(final int flags) {
return new BaseIterator<>(this, false, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize);
}
@@ -273,7 +273,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
@Override
- public boolean hasNext() {
+ public synchronized boolean hasNext() {
if (this.finished) {
return false;
}
@@ -297,7 +297,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
@Override
- public E next() {
+ public synchronized E next() {
if (!this.hasNext()) {
throw new NoSuchElementException();
}
@@ -310,7 +310,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
@Override
- public void remove() {
+ public synchronized void remove() {
final E lastReturned = this.lastReturned;
if (lastReturned == null) {
throw new IllegalStateException();
@@ -320,7 +320,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
@Override
- public void finishedIterating() {
+ public synchronized void finishedIterating() {
if (this.finished || !this.canFinish) {
throw new IllegalStateException();
}
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index f597d65d56964297eeeed6c7e77703764178fee0..09cd3f34eb95ef46df0bd1a81924a9f709bc1adc 100644
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
@@ -4,6 +4,7 @@ import com.destroystokyo.paper.util.maplist.EntityList;
import io.papermc.paper.chunk.system.entity.EntityLookup;
import io.papermc.paper.util.TickThread;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
+import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ChunkHolder;
@@ -34,7 +35,7 @@ public final class ChunkEntitySlices {
protected final EntityCollectionBySection allEntities;
protected final EntityCollectionBySection hardCollidingEntities;
- protected final Reference2ObjectOpenHashMap<Class<? extends Entity>, EntityCollectionBySection> entitiesByClass;
+ protected final Reference2ObjectMap<Class<? extends Entity>, EntityCollectionBySection> entitiesByClass;
protected final EntityList entities = new EntityList();
public ChunkHolder.FullChunkStatus status;
@@ -61,7 +62,7 @@ public final class ChunkEntitySlices {
this.allEntities = new EntityCollectionBySection(this);
this.hardCollidingEntities = new EntityCollectionBySection(this);
- this.entitiesByClass = new Reference2ObjectOpenHashMap<>();
+ this.entitiesByClass = Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>());
this.status = status;
}
@@ -140,9 +141,7 @@ public final class ChunkEntitySlices {
final Entity[] rawData = this.entities.getRawData();
final List<Entity> collectedEntities = new ArrayList<>(len);
- for (int i = 0; i < len; ++i) {
- collectedEntities.add(rawData[i]);
- }
+ collectedEntities.addAll(Arrays.asList(rawData).subList(0, len));
return collectedEntities;
}
@@ -157,7 +156,7 @@ public final class ChunkEntitySlices {
// Paper end - optimise CraftChunk#getEntities
public boolean isEmpty() {
- return this.entities.size() == 0;
+ return this.entities.isEmpty();
}
public void mergeInto(final ChunkEntitySlices slices) {
@@ -168,17 +167,6 @@ public final class ChunkEntitySlices {
}
}
- private boolean preventStatusUpdates;
- public boolean startPreventingStatusUpdates() {
- final boolean ret = this.preventStatusUpdates;
- this.preventStatusUpdates = true;
- return ret;
- }
-
- public void stopPreventingStatusUpdates(final boolean prev) {
- this.preventStatusUpdates = prev;
- }
-
public void updateStatus(final ChunkHolder.FullChunkStatus status, final EntityLookup lookup) {
this.status = status;
@@ -209,7 +197,7 @@ public final class ChunkEntitySlices {
}
for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
+ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) {
final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
if (entry.getKey().isInstance(entity)) {
@@ -234,7 +222,7 @@ public final class ChunkEntitySlices {
}
for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
+ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) {
final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
if (entry.getKey().isInstance(entity)) {
@@ -312,11 +300,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 +316,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 +326,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 +338,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 +355,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;
}
}
@@ -377,7 +365,7 @@ public final class ChunkEntitySlices {
protected final ChunkEntitySlices manager;
protected final long[] nonEmptyBitset;
protected final BasicEntityList<Entity>[] entitiesBySection;
- protected int count;
+ protected volatile int count;
public EntityCollectionBySection(final ChunkEntitySlices manager) {
this.manager = manager;
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 <T> Set<T> newHashSet() {
+ //LOGGER.info("Concurrent hash set created");
+ return Collections.newSetFromMap(new ConcurrentHashMap<T, Boolean>());
+ }
+
+ public static <T, U> Map<T, U> newHashMap() {
+ //LOGGER.info("Concurrent hash map created");
+ return new ConcurrentHashMap<T, U>();
+ }
+
+ public static <T> List<T> newLinkedList() {
+ LOGGER.info("Concurrent \"linked\" list created");
+ return new CopyOnWriteArrayList<T>();
+ }
+
+ public static <T> Collector<T, ?, List<T>> toList() {
+ return Collectors.toCollection(CopyOnWriteArrayList::new);
+ }
+
+ public static <T> Queue<T> newArrayDeque() {
+ LOGGER.info("Concurrent \"array\" deque created");
+ return new ConcurrentLinkedDeque<T>();
+ }
+
+}
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..22b9d217dc06caaf8fbec21f0e31aa1cd13144ee
--- /dev/null
+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java
@@ -0,0 +1,945 @@
+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<E>
+ */
+
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.lang3.NotImplementedException;
+
+/**
+ * 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 <i>weakly consistent</i>, returning
+ * elements reflecting the state of the deque at some point at or since the
+ * creation of the iterator. They do <em>not</em> throw
+ * {@link ConcurrentModificationException}, and may proceed concurrently with
+ * other operations.
+ *
+ * <p>
+ * This class and its iterators implement all of the <em>optional</em> methods
+ * of the {@link Collection} and {@link Iterator} interfaces. Like most other
+ * concurrent collection implementations, this class does not permit the use of
+ * <tt>null</tt> 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 <tt>removeFirstOccurrence</tt>,
+ * and {@link Collection#add} is mapped to <tt>addLast</tt>.
+ *
+ * <p>
+ * Beware that, unlike in most collections, the <tt>size</tt> method is
+ * <em>NOT</em> a constant-time operation. Because of the asynchronous nature of
+ * these deques, determining the current number of elements requires a traversal
+ * of the elements.
+ *
+ * <p>
+ * This class is <tt>Serializable</tt>, but relies on default serialization
+ * mechanisms. Usually, it is a better idea for any serializable class using a
+ * <tt>ConcurrentLinkedDeque</tt> to instead serialize a snapshot of the
+ * elements obtained by method <tt>toArray</tt>.
+ *
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
+ */
+
+public class ConcurrentDoublyLinkedList<E> extends AbstractCollection<E> implements List<E>, 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<E> toArrayList() {
+ ArrayList<E> c = new ArrayList<E>();
+ for (Node<E> 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<E> header;
+
+ /**
+ * List trailer. Last usable node is at trailer.back().
+ */
+ private final Node<E> trailer;
+
+ /**
+ * Constructs an empty deque.
+ */
+ public ConcurrentDoublyLinkedList() {
+ Node<E> h = new Node<E>(null, null, null);
+ Node<E> t = new Node<E>(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 <tt>c</tt> or any element within it is
+ * <tt>null</tt>
+ */
+ public ConcurrentDoublyLinkedList(Collection<? extends E> 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 <tt>null</tt>
+ */
+ 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 <tt>add</tt> method.
+ *
+ * @param o the element to be inserted at the end of this deque.
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ 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 <tt>true</tt> always
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public boolean offerFirst(E o) {
+ addFirst(o);
+ return true;
+ }
+
+ /**
+ * Appends the given element to the end of this deque. (Identical in function to
+ * the <tt>add</tt> method; included only for consistency.)
+ *
+ * @param o the element to be inserted at the end of this deque.
+ * @return <tt>true</tt> always
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ 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 <tt>null</tt> if empty.
+ */
+ public E peekFirst() {
+ Node<E> 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 <tt>null</tt> if empty.
+ */
+ public E peekLast() {
+ Node<E> 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 <tt>null</tt> if empty.
+ */
+ public E pollFirst() {
+ for (;;) {
+ Node<E> 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 <tt>null</tt> if empty.
+ */
+ public E pollLast() {
+ for (;;) {
+ Node<E> 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 <tt>e</tt> such that <tt>o.equals(e)</tt>, 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 <tt>true</tt> if the deque contained the specified element.
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public boolean removeFirstOccurrence(Object o) {
+ checkNullArg(o);
+ for (;;) {
+ Node<E> 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 <tt>e</tt> such that <tt>o.equals(e)</tt>, 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 <tt>true</tt> if the deque contained the specified element.
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ public boolean removeLastOccurrence(Object o) {
+ checkNullArg(o);
+ for (;;) {
+ Node<E> s = trailer;
+ for (;;) {
+ Node<E> 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 <tt>true</tt> if this deque contains at least one element <tt>e</tt>
+ * such that <tt>o.equals(e)</tt>.
+ *
+ * @param o element whose presence in this deque is to be tested.
+ * @return <tt>true</tt> if this deque contains the specified element.
+ */
+ public boolean contains(Object o) {
+ if (o == null)
+ return false;
+ for (Node<E> n = header.forward(); n != null; n = n.forward())
+ if (o.equals(n.element))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this collection contains no elements.
+ * <p>
+ *
+ * @return <tt>true</tt> 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 <tt>Integer.MAX_VALUE</tt> elements, it returns
+ * <tt>Integer.MAX_VALUE</tt>.
+ *
+ * <p>
+ * Beware that, unlike in most collections, this method is <em>NOT</em> 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<E> n = header.forward(); n != null; n = n.forward())
+ ++count;
+ return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count;
+ }
+
+ /**
+ * Removes the first element <tt>e</tt> such that <tt>o.equals(e)</tt>, 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 <tt>true</tt> if the deque contained the specified element.
+ * @throws NullPointerException if the specified element is <tt>null</tt>
+ */
+ 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 <tt>true</tt> if this deque changed as a result of the call.
+ * @throws NullPointerException if <tt>c</tt> or any element within it is
+ * <tt>null</tt>
+ */
+ public boolean addAll(Collection<? extends E> c) {
+ Iterator<? extends E> 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.
+ * <p>
+ *
+ * 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 <i>only</i> 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> 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 <tt>next</tt> method returns elements reflecting the
+ * state of the deque at some point at or since the creation of the iterator.
+ * The method does <em>not</em> throw {@link ConcurrentModificationException},
+ * and may proceed concurrently with other operations.
+ *
+ * @return an iterator over the elements in this deque
+ */
+ public Iterator<E> iterator() {
+ return new CLDIterator();
+ }
+
+ final class CLDIterator implements Iterator<E> {
+ Node<E> last;
+
+ Node<E> next = header.forward();
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public E next() {
+ Node<E> l = last = next;
+ if (l == null)
+ throw new NoSuchElementException();
+ next = next.forward();
+ return l.element;
+ }
+
+ public void remove() {
+ Node<E> l = last;
+ if (l == null)
+ throw new IllegalStateException();
+ while (!l.delete() && !l.isDeleted())
+ ;
+ }
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends E> c) {
+ throw new NotImplementedException("TODO");
+ }
+
+ @Override
+ public E get(int index) {
+ Node<E> 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<E> listIterator() {
+ throw new NotImplementedException("INVALID");
+ }
+
+ @Override
+ public ListIterator<E> listIterator(int index) {
+ throw new NotImplementedException("INVALID");
+ }
+
+ @Override
+ public List<E> 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<E> extends AtomicReference<Node<E>> {
+
+ private static final long serialVersionUID = 6640557564507962862L;
+
+ private volatile Node<E> prev;
+
+ final E element;
+
+ /** Creates a node with given contents */
+ Node(E element, Node<E> next, Node<E> prev) {
+ super(next);
+ this.prev = prev;
+ this.element = element;
+ }
+
+ /** Creates a marker node with given successor */
+ Node(Node<E> next) {
+ super(next);
+ this.prev = this;
+ this.element = null;
+ }
+
+ /**
+ * Gets next link (which is actually the value held as atomic reference).
+ */
+ private Node<E> getNext() {
+ return get();
+ }
+
+ /**
+ * Sets next link
+ *
+ * @param n the next node
+ */
+ void setNext(Node<E> n) {
+ set(n);
+ }
+
+ /**
+ * compareAndSet next link
+ */
+ private boolean casNext(Node<E> cmp, Node<E> val) {
+ return compareAndSet(cmp, val);
+ }
+
+ /**
+ * Gets prev link
+ */
+ private Node<E> getPrev() {
+ return prev;
+ }
+
+ /**
+ * Sets prev link
+ *
+ * @param b the previous node
+ */
+ void setPrev(Node<E> 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<E> f = getNext();
+ return f != null && f.isMarker();
+ }
+
+ /**
+ * Returns next node, ignoring deletion marker
+ */
+ private Node<E> nextNonmarker() {
+ Node<E> 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<E> successor() {
+ Node<E> 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<E> 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<E> findPredecessorOf(Node<E> target) {
+ Node<E> n = this;
+ for (;;) {
+ Node<E> 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<E> predecessor() {
+ Node<E> n = this;
+ for (;;) {
+ Node<E> b = n.getPrev();
+ if (b == null)
+ return n.findPredecessorOf(this);
+ Node<E> s = b.getNext();
+ if (s == this)
+ return b;
+ if (s == null || !s.isMarker()) {
+ Node<E> 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<E> forward() {
+ Node<E> 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<E> back() {
+ Node<E> 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<E> append(E element) {
+ for (;;) {
+ Node<E> f = getNext();
+ if (f == null || f.isMarker())
+ return null;
+ Node<E> x = new Node<E>(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<E> prepend(E element) {
+ for (;;) {
+ Node<E> b = predecessor();
+ if (b == null)
+ return null;
+ Node<E> x = new Node<E>(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<E> b = getPrev();
+ Node<E> f = getNext();
+ if (b != null && f != null && !f.isMarker() && casNext(f, new Node<E>(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<E> replace(E newElement) {
+ for (;;) {
+ Node<E> b = getPrev();
+ Node<E> f = getNext();
+ if (b == null || f == null || f.isMarker())
+ return null;
+ Node<E> x = new Node<E>(newElement, f, b);
+ if (casNext(f, new Node<E>(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..2bf97bd3e77fe4fec785b850524a870300ecd82c
--- /dev/null
+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java
@@ -0,0 +1,237 @@
+package net.himeki.mcmtfabric.parallelised.fastutil;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+import it.unimi.dsi.fastutil.longs.LongArrays;
+import it.unimi.dsi.fastutil.longs.LongCollection;
+import it.unimi.dsi.fastutil.longs.LongComparator;
+import it.unimi.dsi.fastutil.longs.LongIterator;
+import it.unimi.dsi.fastutil.longs.LongIterators;
+import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongListIterator;
+import it.unimi.dsi.fastutil.longs.LongSortedSet;
+
+public class ConcurrentLongLinkedOpenHashSet extends LongLinkedOpenHashSet {
+
+ private static final long serialVersionUID = -5532128240738069111L;
+
+ private final ConcurrentSkipListSet<Long> backing;
+
+ public ConcurrentLongLinkedOpenHashSet() {
+ //backing = new ConcurrentLinkedDeque<Long>();
+ backing = new ConcurrentSkipListSet<Long>();
+ }
+
+ public ConcurrentLongLinkedOpenHashSet(final int initial) {
+ //backing = new ConcurrentLinkedDeque<Long>();
+ backing = new ConcurrentSkipListSet<Long>();
+ }
+
+ 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<Long>) c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Long> 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<Long> 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> T[] toArray(@NotNull T[] ts) {
+ return null;
+ }
+
+ @Override
+ public boolean containsAll(@NotNull Collection<?> collection) {
+ return back.containsAll(collection);
+ }
+
+ @Override
+ public boolean addAll(@NotNull Collection<? extends Long> 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<Short, Boolean> 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> 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<? extends Short> 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..a14ecb2ca64316fb85e6ecb65df50d98d337aff9
--- /dev/null
+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java
@@ -0,0 +1,1685 @@
+package net.himeki.mcmtfabric.parallelised.fastutil;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import it.unimi.dsi.fastutil.longs.*;
+import it.unimi.dsi.fastutil.shorts.ShortIterator;
+import org.apache.commons.lang3.ArrayUtils;
+
+import it.unimi.dsi.fastutil.bytes.ByteCollection;
+import it.unimi.dsi.fastutil.bytes.ByteIterator;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+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.ints.Int2ObjectMap.Entry;
+import it.unimi.dsi.fastutil.objects.ObjectCollection;
+import it.unimi.dsi.fastutil.objects.ObjectIterator;
+import it.unimi.dsi.fastutil.objects.ObjectSet;
+
+public class FastUtilHackUtil {
+
+ public static class ConvertingObjectSet<E, T> implements ObjectSet<T> {
+
+ Set<E> backing;
+ Function<E, T> forward;
+ Function<T, E> back;
+
+ public ConvertingObjectSet(Set<E> backing, Function<E, T> forward, Function<T, 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((T) o));
+ } catch (ClassCastException cce) {
+ return false;
+ }
+ }
+
+ @Override
+ public Object[] toArray() {
+ return backing.stream().map(forward).toArray();
+ }
+
+ @Override
+ public <R> 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<? extends T> 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<T> iterator() {
+ final Iterator<E> backg = backing.iterator();
+ return new ObjectIterator<T>() {
+
+ @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<E,T> implements Long2ObjectMap.FastEntrySet<T> {
+
+ Set<E> backing;
+ Function<E, Long2ObjectMap.Entry<T>> forward;
+ Function<Long2ObjectMap.Entry<T>, E> back;
+
+ public ConvertingObjectSetFast(Set<E> backing,
+ Function<E, Long2ObjectMap.Entry<T>> forward,
+ Function<Long2ObjectMap.Entry<T>, 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<T>)o));
+ } catch (ClassCastException cce) {
+ return false;
+ }
+ }
+
+ @Override
+ public Object[] toArray() {
+ return backing.stream().map(forward).toArray();
+ }
+
+ @Override
+ public <R> 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<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((Long2ObjectMap.Entry<T>) 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<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((Long2ObjectMap.Entry<T>) i))
+ .collect(Collectors.toSet()));
+ } catch (ClassCastException cce) {
+ return false;
+ }
+ }
+
+ @Override
+ public void clear() {
+ backing.clear();
+
+ }
+
+ @Override
+ public ObjectIterator<Long2ObjectMap.Entry<T>> iterator() {
+ final Iterator<E> backg = backing.iterator();
+ return new ObjectIterator<Long2ObjectMap.Entry<T>>() {
+
+ @Override
+ public boolean hasNext() {
+ return backg.hasNext();
+ }
+
+ @Override
+ public Long2ObjectMap.Entry<T> next() {
+ return forward.apply(backg.next());
+ }
+
+ @Override
+ public void remove() {
+ backg.remove();
+ }
+ };
+ }
+
+ @Override
+ public boolean add(Long2ObjectMap.Entry<T> e) {
+ return backing.add(back.apply(e));
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Long2ObjectMap.Entry<T>> c) {
+ return backing.addAll(c.stream().map(back).collect(Collectors.toList()));
+ }
+
+ @Override
+ public ObjectIterator<Long2ObjectMap.Entry<T>> fastIterator() {
+ return iterator();
+ }
+
+
+ }
+
+ private static <T> Entry<T> intEntryForwards(Map.Entry<Integer, T> entry) {
+ return new Entry<T>() {
+
+ @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 <T> Map.Entry<Integer, T> intEntryBackwards(Entry<T> entry) {
+ return entry;
+ }
+
+ private static <T> Long2ObjectMap.Entry<T> longEntryForwards(Map.Entry<Long, T> entry) {
+ return new Long2ObjectMap.Entry<T>() {
+
+ @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 <T> Map.Entry<Long, T> longEntryBackwards(Long2ObjectMap.Entry<T> entry) {
+ return entry;
+ }
+
+ private static Long2ByteMap.Entry longByteEntryForwards(Map.Entry<Long, Byte> 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 <T> Map.Entry<Long, Byte> longByteEntryBackwards(Long2ByteMap.Entry entry) {
+ return entry;
+ }
+
+ private static Long2LongMap.Entry longLongEntryForwards(Map.Entry<Long, Long> 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 <T> Map.Entry<Long, Long> longLongEntryBackwards(Long2LongMap.Entry entry) {
+ return entry;
+ }
+
+ public static <T> ObjectSet<Entry<T>> entrySetIntWrap(Map<Integer, T> map) {
+ return new ConvertingObjectSet<Map.Entry<Integer, T>, Entry<T>>(map.entrySet(), FastUtilHackUtil::intEntryForwards, FastUtilHackUtil::intEntryBackwards);
+ }
+
+ public static <T> ObjectSet<Long2ObjectMap.Entry<T>> entrySetLongWrap(Map<Long, T> map) {
+ return new ConvertingObjectSet<Map.Entry<Long, T>, Long2ObjectMap.Entry<T>>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards);
+ }
+
+ public static <T> Long2ObjectMap.FastEntrySet<T> entrySetLongWrapFast(Map<Long, T> map) {
+ return new ConvertingObjectSetFast<Map.Entry<Long, T>, T>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards);
+ }
+
+ public static ObjectSet<Long2ByteMap.Entry> entrySetLongByteWrap(Map<Long, Byte> map) {
+ return new ConvertingObjectSet<Map.Entry<Long, Byte>, Long2ByteMap.Entry>(map.entrySet(), FastUtilHackUtil::longByteEntryForwards, FastUtilHackUtil::longByteEntryBackwards);
+ }
+
+ public static ObjectSet<Long2LongMap.Entry> entrySetLongLongWrap(Map<Long, Long> map) {
+ return new ConvertingObjectSet<Map.Entry<Long, Long>, Long2LongMap.Entry>(map.entrySet(), FastUtilHackUtil::longLongEntryForwards, FastUtilHackUtil::longLongEntryBackwards);
+ }
+
+
+ static class WrappingIntIterator implements IntIterator {
+
+ Iterator<Integer> backing;
+
+ public WrappingIntIterator(Iterator<Integer> 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<Long> backing;
+
+ public WrappingLongIterator(Iterator<Long> 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<Short> backing;
+
+ public WrappingShortIterator(Iterator<Short> 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<Integer> backing;
+
+ public WrappingIntSet(Set<Integer> 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> T[] toArray(T[] a) {
+ return backing.toArray(a);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return backing.containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Integer> 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<Long> longset) {
+ return new WrappingLongSet(longset);
+ }
+
+ public static class WrappingLongSet implements LongSet {
+
+ Set<Long> backing;
+
+ public WrappingLongSet(Set<Long> 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> T[] toArray(T[] a) {
+ return backing.toArray(a);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return backing.containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Long> 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<Long> longset) {
+ return new WrappingLongSortedSet(longset);
+ }
+
+ public static class WrappingLongSortedSet implements LongSortedSet {
+
+ Set<Long> backing;
+
+ public WrappingLongSortedSet(Set<Long> 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> T[] toArray(T[] a) {
+ return backing.toArray(a);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return backing.containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Long> 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<Long>(backing).iterator());
+ }
+
+ @Override
+ public LongBidirectionalIterator iterator() {
+ return FastUtilHackUtil.wrap(new LinkedList<Long>(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<Integer> intset) {
+ return new WrappingIntSet(intset);
+ }
+
+ public static class WrappingObjectCollection<V> implements ObjectCollection<V> {
+
+ Collection<V> backing;
+
+ public WrappingObjectCollection(Collection<V> 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> 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<? extends V> 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<V> iterator() {
+ return FastUtilHackUtil.itrWrap(backing);
+ }
+
+ }
+
+ public static <K> ObjectCollection<K> wrap(Collection<K> c) {
+ return new WrappingObjectCollection<K>(c);
+ }
+
+ public static class WrappingByteCollection implements ByteCollection {
+
+ Collection<Byte> backing;
+
+ public WrappingByteCollection(Collection<Byte> 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> 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<? extends Byte> 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<Byte>) c);
+ }
+
+ @Override
+ public boolean containsAll(ByteCollection c) {
+ return containsAll((Collection<Byte>) c);
+ }
+
+ @Override
+ public boolean removeAll(ByteCollection c) {
+ return removeAll((Collection<Byte>) c);
+ }
+
+ @Override
+ public boolean retainAll(ByteCollection c) {
+ return retainAll((Collection<Byte>) c);
+ }
+
+ }
+
+ public static ByteCollection wrapBytes(Collection<Byte> c) {
+ return new WrappingByteCollection(c);
+ }
+
+ public static class WrappingIntCollection implements IntCollection {
+
+ Collection<Integer> backing;
+
+ public WrappingIntCollection(Collection<Integer> 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> 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<? extends Integer> 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<Integer>) c);
+ }
+
+ @Override
+ public boolean containsAll(IntCollection c) {
+ return containsAll((Collection<Integer>) c);
+ }
+
+ @Override
+ public boolean removeAll(IntCollection c) {
+ return removeAll((Collection<Integer>) c);
+ }
+
+ @Override
+ public boolean retainAll(IntCollection c) {
+ return retainAll((Collection<Integer>) c);
+ }
+
+ }
+
+ public static IntCollection wrapInts(Collection<Integer> c) {
+ return new WrappingIntCollection(c);
+ }
+
+ public static class WrappingLongCollection implements LongCollection {
+
+ Collection<Long> backing;
+
+ public WrappingLongCollection(Collection<Long> 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> 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<? extends Long> 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<Long>) c);
+ }
+
+ @Override
+ public boolean containsAll(LongCollection c) {
+ return containsAll((Collection<Long>) c);
+ }
+
+ @Override
+ public boolean removeAll(LongCollection c) {
+ return removeAll((Collection<Long>) c);
+ }
+
+ @Override
+ public boolean retainAll(LongCollection c) {
+ return retainAll((Collection<Long>) c);
+ }
+
+ }
+
+ public static LongCollection wrapLongs(Collection<Long> c) {
+ return new WrappingLongCollection(c);
+ }
+
+
+ public static class WrappingLongListIterator implements LongListIterator {
+
+ ListIterator<Long> backing;
+
+ public WrappingLongListIterator(ListIterator<Long> 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<Long> backing;
+
+ public SlimWrappingLongListIterator(Iterator<Long> 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<Long> c) {
+ return new WrappingLongListIterator(c);
+ }
+
+ public static LongListIterator wrap(Iterator<Long> c) {
+ return new SlimWrappingLongListIterator(c);
+ }
+
+ public static class WrappingByteIterator implements ByteIterator {
+
+ Iterator<Byte> parent;
+
+ public WrappingByteIterator(Iterator<Byte> 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<Byte> backing) {
+ return new WrappingByteIterator(backing);
+ }
+
+ public static ByteIterator itrByteWrap(Iterable<Byte> backing) {
+ return new WrappingByteIterator(backing.iterator());
+ }
+
+ public static IntIterator itrIntWrap(Iterator<Integer> backing) {
+ return new WrappingIntIterator(backing);
+ }
+
+ public static IntIterator itrIntWrap(Iterable<Integer> backing) {
+ return new WrappingIntIterator(backing.iterator());
+ }
+
+ public static LongIterator itrLongWrap(Iterator<Long> backing) {
+ return new WrappingLongIterator(backing);
+ }
+
+ public static LongIterator itrLongWrap(Iterable<Long> backing) {
+ return new WrappingLongIterator(backing.iterator());
+ }
+
+ public static ShortIterator itrShortWrap(Iterator<Short> backing) {
+ return new WrappingShortIterator(backing);
+ }
+
+ public static ShortIterator itrShortWrap(Iterable<Short> backing) {
+ return new WrappingShortIterator(backing.iterator());
+ }
+
+ public static class WrapperObjectIterator<T> implements ObjectIterator<T> {
+
+ Iterator<T> parent;
+
+ public WrapperObjectIterator(Iterator<T> 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<T> implements Entry<T> {
+
+ Map.Entry<Integer, T> parent;
+
+ public IntWrapperEntry(Map.Entry<Integer, T> 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<Long, Integer> parent;
+
+ public Long2IntWrapperEntry(Map.Entry<Long, Integer> 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<T> implements ObjectIterator<Entry<T>> {
+
+ Iterator<Map.Entry<Integer, T>> parent;
+
+ public WrapperIntEntryObjectIterator(Iterator<Map.Entry<Integer, T>> parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return parent.hasNext();
+ }
+
+ @Override
+ public Entry<T> next() {
+ Map.Entry<Integer, T> val = parent.next();
+ if (val == null) return null;
+ return new IntWrapperEntry<T>(val);
+ }
+
+ @Override
+ public void remove() {
+ parent.remove();
+ }
+
+ }
+
+ public static <T> ObjectIterator<Entry<T>> intMapItrFake(Map<Integer, T> in) {
+ return new WrapperIntEntryObjectIterator<T>(in.entrySet().iterator());
+ }
+
+ public static <T> ObjectIterator<T> itrWrap(Iterator<T> in) {
+ return new WrapperObjectIterator<T>(in);
+ }
+
+ public static <T> ObjectIterator<T> itrWrap(Iterable<T> in) {
+ return new WrapperObjectIterator<T>(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<V> implements Int2ObjectMap<V> {
+
+ Map<Integer, V> backing;
+
+ public Int2ObjectConcurrentHashMap() {
+ backing = new ConcurrentHashMap<Integer, V>();
+ }
+
+ @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<? extends Integer, ? extends V> 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<Entry<V>> int2ObjectEntrySet() {
+ return FastUtilHackUtil.entrySetIntWrap(backing);
+ }
+
+
+ @Override
+ public IntSet keySet() {
+ return FastUtilHackUtil.wrapIntSet(backing.keySet());
+ }
+
+ @Override
+ public ObjectCollection<V> 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<Long, Byte> 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<? extends Long, ? extends Byte> 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<Entry> 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<Long, Integer> backing = new ConcurrentHashMap<Long, Integer>();
+ 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<? extends Long, ? extends Integer> 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<Entry> 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<Long, Integer> backing = new ConcurrentHashMap<Long, Integer>();
+
+ 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<? extends Long, ? extends Integer> 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<? extends Long, ? extends Integer> 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<? extends Long, ? extends Integer> 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<? extends Integer> 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<? super Long, ? super Integer, ? extends Integer> 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<? super Long, ? super Integer, ? extends Integer> 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<? super Integer, ? super Integer, ? extends Integer> 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<Long, Long> backing = new ConcurrentHashMap<Long, Long>();
+ 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<? extends Long, ? extends Long> 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<Entry> 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<V> implements Long2ObjectMap<V> {
+
+ Map<Long, V> backing;
+ V defaultReturn = null;
+
+ public Long2ObjectConcurrentHashMap() {
+ backing = new ConcurrentHashMap<Long, V>();
+ }
+
+ @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<? extends Long, ? extends V> 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<Entry<V>> long2ObjectEntrySet() {
+ return FastUtilHackUtil.entrySetLongWrap(backing);
+ }
+
+
+ @Override
+ public LongSet keySet() {
+ return FastUtilHackUtil.wrapLongSet(backing.keySet());
+ }
+
+ @Override
+ public ObjectCollection<V> 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<V> extends Long2ObjectOpenHashMap<V> {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -121514116954680057L;
+
+ Map<Long, V> backing;
+ V defaultReturn = null;
+
+ public Long2ObjectOpenConcurrentHashMap() {
+ backing = new ConcurrentHashMap<Long, V>();
+ }
+
+ @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<? extends Long, ? extends V> 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<V> long2ObjectEntrySet() {
+ return FastUtilHackUtil.entrySetLongWrapFast(backing);
+ }
+
+
+ @Override
+ public LongSet keySet() {
+ return FastUtilHackUtil.wrapLongSet(backing.keySet());
+ }
+
+ @Override
+ public ObjectCollection<V> 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<? super V, ? super V, ? extends V> remappingFunction) {
+ return backing.merge(k, v, remappingFunction);
+ }
+
+ @Override
+ public V merge(Long k, final V v, final java.util.function.BiFunction<? super V, ? super V, ? extends V> 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<? super Long, ? super V, ? extends V> remappingFunction) {
+ return backing.computeIfPresent(k, remappingFunction);
+ }
+
+ @Override
+ public V computeIfPresent(final Long k, final java.util.function.BiFunction<? super Long, ? super V, ? extends V> remappingFunction) {
+ return backing.computeIfPresent(k, remappingFunction);
+ }
+
+ @Override
+ public V computeIfAbsent(final long k, final java.util.function.LongFunction<? extends V> mappingFunction) {
+ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong));
+ }
+
+ public V computeIfAbsent(final Long k, final java.util.function.LongFunction<? extends V> mappingFunction) {
+ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong));
+ }
+
+ @Override
+ public V computeIfAbsentPartial(final long key, final Long2ObjectFunction<? extends V> 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<? super Long, ? super V, ? extends V> remappingFunction) {
+ return backing.compute(k, remappingFunction);
+ }
+
+ @Override
+ public V compute(final Long k, final java.util.function.BiFunction<? super Long, ? super V, ? extends V> remappingFunction) {
+ return backing.compute(k, remappingFunction);
+ }
+
+ @Override
+ public Long2ObjectOpenHashMap<V> clone() {
+ throw new IllegalArgumentException();
+ }
+
+ public void clear() {
+ backing.clear();
+ }
+
+ @Override
+ public ObjectSet<Map.Entry<Long, V>> entrySet() {
+ return new FastUtilHackUtil.ConvertingObjectSet<Map.Entry<Long, V>, Map.Entry<Long, V>>(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..565dc74cb65ef0ce9df36a72d9cd5fa440a535e6
--- /dev/null
+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java
@@ -0,0 +1,197 @@
+package net.himeki.mcmtfabric.parallelised.fastutil.sync;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import it.unimi.dsi.fastutil.longs.LongArrays;
+import it.unimi.dsi.fastutil.longs.LongCollection;
+import it.unimi.dsi.fastutil.longs.LongComparator;
+import it.unimi.dsi.fastutil.longs.LongIterator;
+import it.unimi.dsi.fastutil.longs.LongIterators;
+import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongListIterator;
+import it.unimi.dsi.fastutil.longs.LongSortedSet;
+
+public class SyncLongLinkedOpenHashSet extends LongLinkedOpenHashSet {
+
+ 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<? extends Long> 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/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
index 24c677e80af652952263253409c050641e72e3b5..39ace58ad7560d9339ea97230ee5c3ddef6cdc7d 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
@@ -2,6 +2,9 @@ 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,16 +25,18 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet<ClientGamePa
public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, ShortSet positions, LevelChunkSection section, boolean noLightingUpdates) {
this.sectionPos = sectionPos;
this.suppressLightUpdates = noLightingUpdates;
- int i = positions.size();
- 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();
+ final List<Short> copy = new ArrayList<>(positions);
+ this.positions = new short[copy.size()];
+ int counter = 0;
+ for (Short s : copy){
+ this.positions[counter] = s;
+ counter++;
+ }
+ this.states = new BlockState[this.positions.length];
- this.positions[j] = short0;
+ for (int j = 0;j < this.positions.length; ++j) {
+ short short0 = this.positions[j];
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..56a24216cfb635c77f02e3b5e248ecc699454691 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1,10 +1,10 @@
package net.minecraft.server;
+import co.aikar.timings.MinecraftTimings;
+import co.m2ek4u.hearse.ForkJoinTickThread;
+import co.m2ek4u.hearse.HearseConfig;
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;
@@ -12,59 +12,17 @@ import com.mojang.authlib.GameProfile;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.datafixers.DataFixer;
+import com.mojang.datafixers.util.Pair;
import com.mojang.logging.LogUtils;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.Lifecycle;
import it.unimi.dsi.fastutil.longs.LongIterator;
-import java.awt.image.BufferedImage;
-import java.io.BufferedWriter;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.lang.management.ThreadInfo;
-import java.lang.management.ThreadMXBean;
-import java.net.Proxy;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-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.atomic.AtomicReference;
-import java.util.function.BooleanSupplier;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import javax.annotation.Nullable;
-import javax.imageio.ImageIO;
-import net.minecraft.CrashReport;
-import net.minecraft.ReportedException;
-import net.minecraft.SharedConstants;
-import net.minecraft.SystemReport;
-import net.minecraft.Util;
+import joptsimple.OptionSet;
+import net.minecraft.*;
import net.minecraft.commands.CommandSource;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
-import net.minecraft.core.BlockPos;
-import net.minecraft.core.HolderGetter;
-import net.minecraft.core.LayeredRegistryAccess;
-import net.minecraft.core.Registry;
-import net.minecraft.core.RegistryAccess;
+import net.minecraft.core.*;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.worldgen.features.MiscOverworldFeatures;
import net.minecraft.gametest.framework.GameTestTicker;
@@ -77,15 +35,13 @@ import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket;
import net.minecraft.network.protocol.game.ClientboundSetTimePacket;
import net.minecraft.network.protocol.status.ServerStatus;
import net.minecraft.obfuscate.DontObfuscate;
+import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.server.level.DemoMode;
-import net.minecraft.server.level.PlayerRespawnLogic;
-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.bossevents.CustomBossEvents;
+import net.minecraft.server.dedicated.DedicatedServer;
+import net.minecraft.server.dedicated.DedicatedServerProperties;
+import net.minecraft.server.level.*;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
import net.minecraft.server.network.ServerConnectionListener;
@@ -100,26 +56,15 @@ import net.minecraft.server.players.GameProfileCache;
import net.minecraft.server.players.PlayerList;
import net.minecraft.server.players.ServerOpListEntry;
import net.minecraft.server.players.UserWhiteList;
-import net.minecraft.util.Crypt;
-import net.minecraft.util.CryptException;
-import net.minecraft.util.FrameTimer;
-import net.minecraft.util.ModCheck;
-import net.minecraft.util.Mth;
-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.*;
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;
@@ -133,66 +78,53 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.item.crafting.RecipeManager;
-import net.minecraft.world.level.ChunkPos;
-import net.minecraft.world.level.CustomSpawner;
-import net.minecraft.world.level.DataPackConfig;
-import net.minecraft.world.level.ForcedChunksSavedData;
-import net.minecraft.world.level.GameRules;
-import net.minecraft.world.level.GameType;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.level.LevelSettings;
-import net.minecraft.world.level.WorldDataConfiguration;
+import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.dimension.LevelStem;
-import net.minecraft.world.level.levelgen.WorldOptions;
+import net.minecraft.world.level.levelgen.*;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
+import net.minecraft.world.level.levelgen.presets.WorldPresets;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
-import net.minecraft.world.level.storage.CommandStorage;
-import net.minecraft.world.level.storage.DimensionDataStorage;
-import net.minecraft.world.level.storage.LevelData;
-import net.minecraft.world.level.storage.LevelResource;
-import net.minecraft.world.level.storage.LevelStorageSource;
-import net.minecraft.world.level.storage.PlayerDataStorage;
-import net.minecraft.world.level.storage.PrimaryLevelData;
-import net.minecraft.world.level.storage.ServerLevelData;
-import net.minecraft.world.level.storage.WorldData;
+import net.minecraft.world.level.storage.*;
import net.minecraft.world.level.storage.loot.ItemModifierManager;
import net.minecraft.world.level.storage.loot.LootTables;
import net.minecraft.world.level.storage.loot.PredicateManager;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.apache.commons.lang3.Validate;
-import org.slf4j.Logger;
-
-// CraftBukkit start
-import com.mojang.datafixers.util.Pair;
-import com.mojang.serialization.DynamicOps;
-import com.mojang.serialization.Lifecycle;
-import java.util.Random;
-// import jline.console.ConsoleReader; // Paper
-import joptsimple.OptionSet;
-import net.minecraft.core.HolderLookup;
-import net.minecraft.resources.RegistryOps;
-import net.minecraft.server.bossevents.CustomBossEvents;
-import net.minecraft.server.dedicated.DedicatedServer;
-import net.minecraft.server.dedicated.DedicatedServerProperties;
-import net.minecraft.world.level.levelgen.Heightmap;
-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
+import org.slf4j.Logger;
-import co.aikar.timings.MinecraftTimings; // Paper
+import javax.annotation.Nullable;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.net.Proxy;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTask> implements CommandSource, AutoCloseable {
@@ -284,6 +216,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
protected WorldData worldData;
private volatile boolean isSaving;
+ public boolean asyncEntityEnabled;
+ private final boolean asyncExecutorEnabled;
+ private int asyncExecutorThreadCount;
+ private final AtomicInteger threadId = new AtomicInteger();
+ public ForkJoinPool asyncExecutor;
+
// CraftBukkit start
public final WorldLoader.DataLoadContext worldLoader;
public org.bukkit.craftbukkit.CraftServer server;
@@ -410,6 +348,21 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Paper end
Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
this.paperConfigurations = services.paperConfigurations(); // Paper
+ //Hearse start
+ HearseConfig.init();
+ this.asyncEntityEnabled = HearseConfig.getBoolean("enable-async-entity",true);
+ this.asyncExecutorThreadCount = HearseConfig.getInt("async-entity-executor-thread-count",Runtime.getRuntime().availableProcessors());
+ this.asyncExecutorEnabled = this.asyncEntityEnabled;
+ if (this.asyncExecutorEnabled){
+ this.asyncExecutor = new ForkJoinPool(this.asyncExecutorThreadCount,forkJoinPool -> {
+ ForkJoinTickThread worker = new ForkJoinTickThread(forkJoinPool);
+ worker.setDaemon(true);
+ worker.setContextClassLoader(MinecraftServer.class.getClassLoader());
+ worker.setName("Hearse-ForkJoin-Worker # "+this.threadId.getAndIncrement());
+ return worker;
+ },null,true);
+ }
+ //Hearse end
}
// CraftBukkit end
@@ -915,6 +868,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// CraftBukkit end
public void stopServer() {
+ HearseConfig.save();//Hearse
// CraftBukkit start - prevent double stopping on multiple threads
synchronized(this.stopLock) {
if (this.hasStopped) return;
@@ -1333,17 +1287,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
} else {
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
if (this.haveTime()) {
- Iterator iterator = this.getAllLevels().iterator();
-
- while (iterator.hasNext()) {
- ServerLevel worldserver = (ServerLevel) iterator.next();
-
+ for (ServerLevel worldserver : this.getAllLevels()) {
if (worldserver.getChunkSource().pollTask()) {
ret = true; // Paper - force execution of all worlds, do not just bias the first
}
}
}
-
return ret; // Paper - force execution of all worlds, do not just bias the first
}
}
@@ -1522,11 +1471,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
//MinecraftTimings.timeUpdateTimer.stopTiming(); // Spigot // Paper // Purpur
this.isIteratingOverLevels = true; // Paper
- Iterator iterator = this.getAllLevels().iterator(); // Paper - move down
- while (iterator.hasNext()) {
- ServerLevel worldserver = (ServerLevel) iterator.next();
- worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
- worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
+ for (ServerLevel worldserver : this.getAllLevels()) {
+ worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
+ worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
/*this.profiler.push(() -> { // Purpur
@@ -1557,7 +1504,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
try {
crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
} catch (Throwable t) {
- if (throwable instanceof ThreadDeath) { throw (ThreadDeath)throwable; } // Paper
+ if (throwable instanceof ThreadDeath) {
+ throw (ThreadDeath) throwable;
+ } // Paper
throw new RuntimeException("Error generating crash report", t);
}
// Spigot End
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index bc46479fd0622a90fd98ac88f92b2840a22a2d04..ad000a2bb5b0bc0e231af566777b51c70c91002d 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -2,6 +2,7 @@ package net.minecraft.server.level;
import com.mojang.datafixers.util.Either;
import com.mojang.datafixers.util.Pair;
+import it.unimi.dsi.fastutil.shorts.ShortArraySet;
import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet;
import it.unimi.dsi.fastutil.shorts.ShortSet;
import java.util.ArrayList;
@@ -14,6 +15,9 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
import javax.annotation.Nullable;
+
+import it.unimi.dsi.fastutil.shorts.ShortSets;
+import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentShortHashSet;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
@@ -233,7 +237,7 @@ public class ChunkHolder {
if (i < 0 || i >= 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));
@@ -302,8 +306,7 @@ public class ChunkHolder {
for (j = 0; j < this.changedBlocksPerSection.length; ++j) {
ShortSet shortset = this.changedBlocksPerSection[j];
-
- if (shortset != null) {
+ if (shortset!=null){
int k = this.levelHeightAccessor.getSectionYFromSectionIndex(j);
SectionPos sectionposition = SectionPos.of(chunk.getPos(), k);
@@ -315,14 +318,13 @@ public class ChunkHolder {
this.broadcastBlockEntityIfNeeded(world, blockposition, iblockdata);
} else {
LevelChunkSection chunksection = chunk.getSection(j);
- ClientboundSectionBlocksUpdatePacket packetplayoutmultiblockchange = new ClientboundSectionBlocksUpdatePacket(sectionposition, shortset, chunksection, this.resendLight);
+ ClientboundSectionBlocksUpdatePacket packetplayoutmultiblockchange = new ClientboundSectionBlocksUpdatePacket(sectionposition,shortset, chunksection, this.resendLight);
this.broadcast(packetplayoutmultiblockchange, false);
packetplayoutmultiblockchange.runUpdates((blockposition1, iblockdata1) -> {
this.broadcastBlockEntityIfNeeded(world, blockposition1, iblockdata1);
});
}
-
this.changedBlocksPerSection[j] = null;
}
}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index beb7c22cb63021f26c06f91050361e1b25fcc72d..1248549d17a8c07579f413c2143bba029868d0e9 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1,57 +1,21 @@
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 it.unimi.dsi.fastutil.ints.Int2ObjectMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import io.papermc.paper.util.MCUtil;
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.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 +27,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 +48,26 @@ 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;
+import org.slf4j.Logger;
+
+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.*;
// CraftBukkit end
-import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider {
@@ -147,13 +101,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<ChunkMap.TrackedEntity> entityMap;
+ public final Map<Integer, TrackedEntity> entityMap;
private final Long2ByteMap chunkTypeCache;
private final Long2LongMap chunkSaveCooldowns;
private final Queue<Runnable> unloadQueue;
int viewDistance;
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
- public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
+ public final ReferenceSet<ChunkHolder> needsChangeBroadcasting = ReferenceSets.synchronize(new ReferenceOpenHashSet<>());
// Paper - rewrite chunk system
// Paper start - optimise checkDespawn
@@ -295,7 +249,7 @@ 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.entityMap = Maps.newConcurrentMap();
this.chunkTypeCache = new Long2ByteOpenHashMap();
this.chunkSaveCooldowns = new Long2LongOpenHashMap();
this.unloadQueue = Queues.newConcurrentLinkedQueue();
@@ -1209,11 +1163,7 @@ 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 +1181,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 +1228,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end - optimized tracker
List<ServerPlayer> list = Lists.newArrayList();
List<ServerPlayer> 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..3dd4d3b0ec9732df363966d124bbf6da96d9f047 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -30,6 +30,8 @@ import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
+
+import net.himeki.mcmtfabric.parallelised.fastutil.Long2ObjectOpenConcurrentHashMap;
import net.minecraft.core.SectionPos;
import net.minecraft.util.SortedArraySet;
import net.minecraft.util.thread.ProcessorHandle;
@@ -52,7 +54,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<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
+ final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenConcurrentHashMap<>();
// 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..4ac0a4eb95ab34a3409b66ee8d96af0846c6ec8c 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -6,12 +6,7 @@ import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.util.Either;
import java.io.File;
import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
+import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BooleanSupplier;
@@ -803,7 +798,7 @@ public class ServerChunkCache extends ChunkSource {
//gameprofilerfiller.popPush("broadcast"); // Purpur
//this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur
if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
- ReferenceOpenHashSet<ChunkHolder> copy = this.chunkMap.needsChangeBroadcasting.clone();
+ List<ChunkHolder> copy = new ArrayList<>(this.chunkMap.needsChangeBroadcasting);
this.chunkMap.needsChangeBroadcasting.clear();
for (ChunkHolder holder : copy) {
holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded
@@ -848,12 +843,10 @@ public class ServerChunkCache extends ChunkSource {
if (chunkMap.playerMobDistanceMap != null && _pufferfish_spawnCountsReady.getAndSet(false)) {
net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> {
int mapped = distanceManager.getNaturalSpawnChunkCount();
- io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Entity> objectiterator =
- level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS);
+ Iterator<Entity> objectiterator = level.entityTickList.entities.iterator();
gg.pufferfish.pufferfish.util.IterableWrapper<Entity> wrappedIterator =
new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator);
lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true);
- objectiterator.finishedIterating();
_pufferfish_spawnCountsReady.set(true);
});
}
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
index 50cf4d200bc2892f2140c9929193b4b20ad2bd17..51a65ced1619d549fb6b0c4e7305839e8db2c3e5 100644
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
@@ -102,7 +102,7 @@ public class ServerEntity {
if (entity instanceof ItemFrame) {
ItemFrame entityitemframe = (ItemFrame) entity;
- if (true || this.tickCount % 10 == 0) { // CraftBukkit - Moved below, should always enter this block
+ if (true) { // CraftBukkit - Moved below, should always enter this block
ItemStack itemstack = entityitemframe.getItem();
if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && itemstack.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 03b8ef3409fd5f7a4d4b06e13cf8eb22b3bbf8a1..59fc7c0d4c99324c57fcb2edf41c4414f689e931 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1,69 +1,33 @@
package net.minecraft.server.level;
+import co.aikar.timings.TimingHistory;
+import co.m2ek4u.hearse.ForkJoinTickThread;
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.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 +35,10 @@ import net.minecraft.server.players.SleepStatus;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.TagKey;
-import net.minecraft.util.AbortableIterationConsumer;
-import net.minecraft.util.CsvOutput;
-import net.minecraft.util.Mth;
-import net.minecraft.util.ProgressListener;
-import net.minecraft.util.Unit;
-import net.minecraft.util.profiling.ProfilerFiller;
+import net.minecraft.util.*;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.damagesource.DamageSource;
-import net.minecraft.world.entity.Entity;
-import net.minecraft.world.entity.EntityType;
-import net.minecraft.world.entity.LightningBolt;
-import net.minecraft.world.entity.LivingEntity;
-import net.minecraft.world.entity.Mob;
-import net.minecraft.world.entity.MobCategory;
-import net.minecraft.world.entity.ReputationEventHandler;
+import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.ai.village.ReputationEventType;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
@@ -102,17 +55,7 @@ import net.minecraft.world.entity.raid.Raid;
import net.minecraft.world.entity.raid.Raids;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.item.crafting.RecipeManager;
-import net.minecraft.world.level.BlockEventData;
-import net.minecraft.world.level.ChunkPos;
-import net.minecraft.world.level.CustomSpawner;
-import net.minecraft.world.level.Explosion;
-import net.minecraft.world.level.ExplosionDamageCalculator;
-import net.minecraft.world.level.ForcedChunksSavedData;
-import net.minecraft.world.level.GameRules;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.level.NaturalSpawner;
-import net.minecraft.world.level.StructureManager;
-import net.minecraft.world.level.WorldGenLevel;
+import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.Block;
@@ -128,12 +71,10 @@ import net.minecraft.world.level.chunk.storage.EntityStorage;
import net.minecraft.world.level.dimension.BuiltinDimensionTypes;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.dimension.end.EndDragonFight;
-import net.minecraft.world.level.entity.EntityPersistentStorage;
import net.minecraft.world.level.entity.EntityTickList;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.entity.LevelCallback;
import net.minecraft.world.level.entity.LevelEntityGetter;
-import net.minecraft.world.level.entity.PersistentEntitySectionManager;
import net.minecraft.world.level.gameevent.DynamicGameEventListener;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.gameevent.GameEventDispatcher;
@@ -158,21 +99,32 @@ import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.ticks.LevelTicks;
-import org.slf4j.Logger;
import org.bukkit.Bukkit;
-import org.bukkit.Location;
import org.bukkit.WeatherType;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.craftbukkit.generator.CustomWorldChunkManager;
-import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.util.WorldUUID;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.server.MapInitializeEvent;
import org.bukkit.event.weather.LightningStrikeEvent;
-import org.bukkit.event.world.GenericGameEvent;
import org.bukkit.event.world.TimeSkipEvent;
-// CraftBukkit end
-import it.unimi.dsi.fastutil.ints.IntArrayList; // Paper
+import org.slf4j.Logger;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.Executor;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class ServerLevel extends Level implements WorldGenLevel {
@@ -204,7 +156,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
final Set<Mob> navigatingMobs;
volatile boolean isUpdatingNavigations;
protected final Raids raids;
- private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents;
+ private final Deque<BlockEventData> blockEvents;
private final List<BlockEventData> blockEventsToReschedule;
private boolean handlingTick;
private final List<CustomSpawner> customSpawners;
@@ -269,10 +221,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
public final void loadChunksForMoveAsync(AABB axisalignedbb, ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority priority,
java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) {
- if (Thread.currentThread() != this.thread) {
- this.getChunkSource().mainThreadProcessor.execute(() -> {
- this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad);
- });
+ if (Thread.currentThread() != this.thread && !ForkJoinTickThread.isAnotherForKJoinTickThread()) {
+ this.getChunkSource().mainThreadProcessor.execute(() -> this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad));
return;
}
List<net.minecraft.world.level.chunk.ChunkAccess> ret = new java.util.ArrayList<>();
@@ -322,7 +272,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
for (int cx = minChunkX; cx <= maxChunkX; ++cx) {
for (int cz = minChunkZ; cz <= maxChunkZ; ++cz) {
io.papermc.paper.chunk.system.ChunkSystem.scheduleChunkLoad(
- this, cx, cz, net.minecraft.world.level.chunk.ChunkStatus.FULL, true, priority, consumer
+ this, cx, cz, net.minecraft.world.level.chunk.ChunkStatus.FULL, true, priority, consumer
);
}
}
@@ -331,7 +281,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
// Paper start - rewrite chunk system
public final io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler chunkTaskScheduler;
public final io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController chunkDataControllerNew
- = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA) {
+ = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA) {
@Override
public net.minecraft.world.level.chunk.storage.RegionFileStorage getCache() {
@@ -349,7 +299,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
};
public final io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController poiDataControllerNew
- = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA) {
+ = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA) {
@Override
public net.minecraft.world.level.chunk.storage.RegionFileStorage getCache() {
@@ -367,7 +317,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
};
public final io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController entityDataControllerNew
- = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.ENTITY_DATA) {
+ = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.ENTITY_DATA) {
@Override
public net.minecraft.world.level.chunk.storage.RegionFileStorage getCache() {
@@ -396,8 +346,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
ChunkPos nbtPos = nbt == null ? null : EntityStorage.readChunkPos(nbt);
if (nbtPos != null && !pos.equals(nbtPos)) {
throw new IllegalArgumentException(
- "Entity chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos.toString()
- + " but compound says coordinate is " + nbtPos + " for world: " + this
+ "Entity chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos.toString()
+ + " but compound says coordinate is " + nbtPos + " for world: " + this
);
}
super.write(pos, nbt);
@@ -407,8 +357,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
private void writeEntityChunk(int chunkX, int chunkZ, net.minecraft.nbt.CompoundTag compound) throws IOException {
if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
io.papermc.paper.chunk.system.io.RegionFileIOThread.scheduleSave(
- this, chunkX, chunkZ, compound,
- io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.ENTITY_DATA);
+ this, chunkX, chunkZ, compound,
+ io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.ENTITY_DATA);
return;
}
this.entityStorage.write(new ChunkPos(chunkX, chunkZ), compound);
@@ -417,8 +367,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
private net.minecraft.nbt.CompoundTag readEntityChunk(int chunkX, int chunkZ) throws IOException {
if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
return io.papermc.paper.chunk.system.io.RegionFileIOThread.loadData(
- this, chunkX, chunkZ, io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.ENTITY_DATA,
- io.papermc.paper.chunk.system.io.RegionFileIOThread.getIOBlockingPriorityForCurrentThread()
+ this, chunkX, chunkZ, io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.ENTITY_DATA,
+ io.papermc.paper.chunk.system.io.RegionFileIOThread.getIOBlockingPriorityForCurrentThread()
);
}
return this.entityStorage.read(new ChunkPos(chunkX, chunkZ));
@@ -536,8 +486,8 @@ 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.navigatingMobs = Sets.newConcurrentHashSet();
+ this.blockEvents = new ConcurrentLinkedDeque<>();
this.blockEventsToReschedule = new ArrayList(64);
this.dragonParts = new Int2ObjectOpenHashMap();
this.tickTime = flag1;
@@ -709,41 +659,44 @@ public class ServerLevel extends Level implements WorldGenLevel {
org.spigotmc.ActivationRange.activateEntities(this); // Spigot
//timings.entityTick.startTiming(); // Spigot // Purpur
this.entityTickList.forEach((entity) -> {
- entity.activatedPriorityReset = false; // Pufferfish - DAB
+ entity.activatedPriorityReset = false;
if (!entity.isRemoved()) {
- if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed
+ synchronized (this.entityTickList){
+ MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick //Hearse -- Move up
+ }
+ if (false) { // 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
+ if (MinecraftServer.getServer().asyncEntityEnabled){
+ MinecraftServer.getServer().asyncExecutor.execute(()->{
+ entity.checkDespawn();
+ Entity entity1 = entity.getVehicle();
+ if (entity1 != null) {
+ if (!entity1.isRemoved() && entity1.hasPassenger(entity)) {
+ return;
+ }
+ entity.stopRiding();
+ }
+ try {
+ this.tickNonPassenger(entity); // Pufferfish - changed
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ });
+ }else{
+ entity.checkDespawn();
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
+ try {
+ this.tickNonPassenger(entity); // Pufferfish - changed
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
}
}
}
@@ -993,7 +946,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");
@@ -1197,9 +1150,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
currentlyTickingEntity.lazySet(entity);
}
// Paper end - log detailed entity tick information
- ++TimingHistory.entityTicks; // Paper - timings
- // Spigot start
- co.aikar.timings.Timing timer; // Paper
+ ++TimingHistory.entityTicks; // Paper - timings
+ // Spigot start
+ co.aikar.timings.Timing timer; // Paper
/*if (!org.spigotmc.ActivationRange.checkIfActive(entity)) { // Paper - comment out - EAR 2, reimplement below
entity.tickCount++;
timer = entity.getType().inactiveTickTimer.startTiming(); try { // Paper - timings
@@ -1207,36 +1160,36 @@ public class ServerLevel extends Level implements WorldGenLevel {
} finally { timer.stopTiming(); } // Paper
return;
}*/ // Paper - comment out EAR 2
- // Spigot end
- // Paper start- timings
- final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity);
- //timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper // Purpur
- //try { // Purpur
- // Paper end - timings
- entity.setOldPosAndRot();
- //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur
-
- ++entity.tickCount;
+ // Spigot end
+ // Paper start- timings
+ final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity);
+ //timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper // Purpur
+ //try { // Purpur
+ // Paper end - timings
+ entity.setOldPosAndRot();
+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur
+
+ ++entity.tickCount;
/*this.getProfiler().push(() -> { // Purpur
return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString();
});*/ // Purpur
- //gameprofilerfiller.incrementCounter("tickNonPassenger"); // Purpur
- if (isActive) { // Paper - EAR 2
- TimingHistory.activatedEntityTicks++;
- entity.tick();
- entity.postTick(); // CraftBukkit
- } else { entity.inactiveTick(); } // Paper - EAR 2
- //this.getProfiler().pop(); // Purpur
- //} finally { timer.stopTiming(); } // Paper - timings // Purpur
- Iterator iterator = entity.getPassengers().iterator();
+ //gameprofilerfiller.incrementCounter("tickNonPassenger"); // Purpur
+ if (isActive) { // Paper - EAR 2
+ TimingHistory.activatedEntityTicks++;
+ entity.tick();
+ entity.postTick(); // CraftBukkit
+ } 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();
+ while (iterator.hasNext()) {
+ Entity entity1 = (Entity) iterator.next();
- this.tickPassenger(entity, entity1);
- }
- // } finally { timer.stopTiming(); } // Paper - timings - move up
- // Paper start - log detailed entity tick information
+ this.tickPassenger(entity, entity1);
+ }
+ // } finally { timer.stopTiming(); } // Paper - timings - move up
+ // Paper start - log detailed entity tick information
} finally {
if (currentlyTickingEntity.get() == entity) {
currentlyTickingEntity.lazySet(null);
@@ -1263,8 +1216,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
//gameprofilerfiller.incrementCounter("tickPassenger"); // Purpur
// Paper start - EAR 2
if (isActive) {
- passenger.rideTick();
- passenger.postTick(); // CraftBukkit
+ passenger.rideTick();
+ passenger.postTick(); // CraftBukkit
} else {
passenger.setDeltaMovement(Vec3.ZERO);
passenger.inactiveTick();
@@ -1281,7 +1234,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
this.tickPassenger(passenger, entity2);
}
- //} finally { timer.stopTiming(); }// Paper - EAR2 timings // Purpur
+ //} finally { timer.stopTiming(); }// Paper - EAR2 timings // Purpur
}
} else {
passenger.stopRiding();
@@ -1302,24 +1255,24 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
//try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) { // Purpur
- if (doFull) {
- this.saveLevelData();
- }
+ if (doFull) {
+ this.saveLevelData();
+ }
- //this.timings.worldSaveChunks.startTiming(); // Paper // Purpur
- if (!this.noSave()) chunkproviderserver.saveIncrementally();
- //this.timings.worldSaveChunks.stopTiming(); // Paper // Purpur
+ //this.timings.worldSaveChunks.startTiming(); // Paper // Purpur
+ if (!this.noSave()) chunkproviderserver.saveIncrementally();
+ //this.timings.worldSaveChunks.stopTiming(); // Paper // Purpur
- // Copied from save()
- // CraftBukkit start - moved from MinecraftServer.saveChunks
- if (doFull) { // Paper
- ServerLevel worldserver1 = this;
+ // Copied from save()
+ // CraftBukkit start - moved from MinecraftServer.saveChunks
+ if (doFull) { // Paper
+ ServerLevel worldserver1 = this;
- this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
- this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save());
- this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
- }
- // CraftBukkit end
+ this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
+ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save());
+ this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
+ }
+ // CraftBukkit end
//} // Purpur
}
// Paper end
@@ -1344,10 +1297,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
progressListener.progressStage(Component.translatable("menu.savingChunks"));
}
- //timings.worldSaveChunks.startTiming(); // Paper // Purpur
+ //timings.worldSaveChunks.startTiming(); // Paper // Purpur
if (!close) chunkproviderserver.save(flush); // Paper - rewrite chunk system
if (close) chunkproviderserver.close(true); // Paper - rewrite chunk system
- //timings.worldSaveChunks.stopTiming(); // Paper // Purpur
+ //timings.worldSaveChunks.stopTiming(); // Paper // Purpur
//}// Paper // Purpur
// Paper - rewrite chunk system - entity saving moved into ChunkHolder
@@ -1662,54 +1615,53 @@ 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";
-
- Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated"));
+ 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);
+ if (this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates
+ VoxelShape voxelshape = oldState.getCollisionShape(this, pos);
+ VoxelShape voxelshape1 = newState.getCollisionShape(this, pos);
- if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) {
- List<PathNavigation> list = new ObjectArrayList();
- Iterator iterator = this.navigatingMobs.iterator();
+ if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) {
+ List<PathNavigation> list = new ObjectArrayList();
+ Iterator iterator = this.navigatingMobs.iterator();
- while (iterator.hasNext()) {
- // CraftBukkit start - fix SPIGOT-6362
- Mob entityinsentient;
- try {
- entityinsentient = (Mob) iterator.next();
- } catch (java.util.ConcurrentModificationException ex) {
- // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register
- // In this case we just run the update again across all the iterators as the chunk will then be loaded
- // As this is a relative edge case it is much faster than copying navigators (on either read or write)
- this.sendBlockUpdated(pos, oldState, newState, flags);
- return;
- }
- // CraftBukkit end
- PathNavigation navigationabstract = entityinsentient.getNavigation();
+ while (iterator.hasNext()) {
+ // CraftBukkit start - fix SPIGOT-6362
+ Mob entityinsentient;
+ try {
+ entityinsentient = (Mob) iterator.next();
+ } catch (java.util.ConcurrentModificationException ex) {
+ // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register
+ // In this case we just run the update again across all the iterators as the chunk will then be loaded
+ // As this is a relative edge case it is much faster than copying navigators (on either read or write)
+ this.sendBlockUpdated(pos, oldState, newState, flags);
+ return;
+ }
+ // CraftBukkit end
+ PathNavigation navigationabstract = entityinsentient.getNavigation();
- if (navigationabstract.shouldRecomputePath(pos)) {
- list.add(navigationabstract);
+ if (navigationabstract.shouldRecomputePath(pos)) {
+ list.add(navigationabstract);
+ }
}
- }
- try {
- this.isUpdatingNavigations = true;
- iterator = list.iterator();
+ try {
+ this.isUpdatingNavigations = true;
+ iterator = list.iterator();
- while (iterator.hasNext()) {
- PathNavigation navigationabstract1 = (PathNavigation) iterator.next();
+ while (iterator.hasNext()) {
+ PathNavigation navigationabstract1 = (PathNavigation) iterator.next();
- navigationabstract1.recomputePath();
+ navigationabstract1.recomputePath();
+ }
+ } finally {
+ this.isUpdatingNavigations = false;
}
- } finally {
- this.isUpdatingNavigations = false;
- }
- }
+ }
} // Paper
}
@@ -1777,13 +1729,11 @@ 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 = 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()));
+ this.server.getPlayerList().broadcast(null, blockactiondata.pos().getX(), blockactiondata.pos().getY(), (double) blockactiondata.pos().getZ(), 64.0D, this.dimension(), new ClientboundBlockEventPacket(blockactiondata.pos(), blockactiondata.block(), blockactiondata.paramA(), blockactiondata.paramB()));
}
} else {
this.blockEventsToReschedule.add(blockactiondata);
diff --git a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java
index 50a9f33aa31e9273c7c52d4bb2b02f0f884f7ba5..6d94aa3c175345f701ec67175fad3fcde4481041 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<T> extends AbstractCollection<T> {
- private final Map<Class<?>, List<T>> byClass = Maps.newHashMap();
+ private final Map<Class<?>, List<T>> byClass = Maps.newConcurrentMap();
private final Class<T> baseClass;
- private final List<T> allInstances = Lists.newArrayList();
+ private final List<T> allInstances = Lists.newCopyOnWriteArrayList();
public ClassInstanceMultiMap(Class<T> elementType) {
this.baseClass = elementType;
@@ -58,22 +60,24 @@ public class ClassInstanceMultiMap<T> extends AbstractCollection<T> {
if (!this.baseClass.isAssignableFrom(type)) {
throw new IllegalArgumentException("Don't know how to search for " + type);
} else {
- List<? extends T> list = this.byClass.computeIfAbsent(type, (typeClass) -> {
- return this.allInstances.stream().filter(typeClass::isInstance).collect(Collectors.toList());
- });
- return Collections.unmodifiableCollection(list);
+ List<? extends T> list = this.byClass.computeIfAbsent(type, (typeClass) -> this.allInstances.stream().filter(typeClass::isInstance).collect(toList()));
+ return (Collection<S>) Collections.unmodifiableCollection(list);
}
}
@Override
public Iterator<T> iterator() {
- return (Iterator<T>)(this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator()));
+ return this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator());
}
public List<T> getAllInstances() {
return ImmutableList.copyOf(this.allInstances);
}
+ public static <T> Collector<T, ?, List<T>> toList() {
+ return Collectors.toCollection(CopyOnWriteArrayList::new);
+ }
+
@Override
public int size() {
return this.allInstances.size();
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..51f0439f3ad94dce17a2be6197e21d0ca3bedf9f 100644
--- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
@@ -3,9 +3,12 @@ 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.function.BooleanSupplier;
@@ -19,7 +22,7 @@ import org.slf4j.Logger;
public abstract class BlockableEventLoop<R extends Runnable> implements ProfilerMeasured, ProcessorHandle<R>, Executor {
private final String name;
private static final Logger LOGGER = LogUtils.getLogger();
- private final Queue<R> pendingRunnables = Queues.newConcurrentLinkedQueue();
+ private final Deque<R> pendingRunnables = new ConcurrentLinkedDeque<>();
private int blockingCount;
protected BlockableEventLoop(String name) {
@@ -117,13 +120,14 @@ public abstract class BlockableEventLoop<R extends Runnable> 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 75e374b262e2797b593e5f170014a6e0cd95e41e..c4b99be104c323dff9c770e2500fa14075fed06c 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -484,7 +484,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
public int sectionY = Integer.MIN_VALUE;
public int sectionZ = Integer.MIN_VALUE;
- public boolean updatingSectionStatus = false;
+ public volatile boolean updatingSectionStatus = false;
// Paper end
// Paper start - optimise entity tracking
final org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = org.spigotmc.TrackingRange.getTrackingRangeType(this);
@@ -4441,12 +4441,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
// Paper end - block invalid positions
// Paper end
- // Paper start - rewrite chunk system
- if (this.updatingSectionStatus) {
- LOGGER.error("Refusing to update position for entity " + this + " to position " + new Vec3(x, y, z) + " since it is processing a section status update", new Throwable());
- return;
- }
- // Paper end - rewrite chunk system
// Paper start - fix MC-4
if (this instanceof ItemEntity) {
if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixEntityPositionDesync) {
@@ -4562,10 +4556,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
public final void setRemoved(Entity.RemovalReason reason) {
// Paper start - rewrite chunk system
io.papermc.paper.util.TickThread.ensureTickThread(this, "Cannot remove entity off-main");
- if (this.updatingSectionStatus) {
- LOGGER.warn("Entity " + this + " is currently prevented from being added/removed to world since it is processing section status updates", new Throwable());
- return;
- }
// Paper end - rewrite chunk system
if (this.removalReason == null) {
this.removalReason = reason;
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 93c32dd39693b37efaa05af0486e1bdd298661f3..6892bd3925890a024679207c0aa7cab12dbdb82d 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;
@@ -1963,15 +1964,18 @@ public abstract class LivingEntity extends Entity {
BlockPos blockposition = this.blockPosition();
BlockState iblockdata = this.getFeetBlockState();
- if (iblockdata.is(BlockTags.CLIMBABLE)) {
- this.lastClimbablePos = Optional.of(blockposition);
- return true;
- } else if (iblockdata.getBlock() instanceof TrapDoorBlock && this.trapdoorUsableAsLadder(blockposition, iblockdata)) {
- this.lastClimbablePos = Optional.of(blockposition);
- return true;
- } else {
- return false;
+ if (iblockdata!=null){
+ if (iblockdata.is(BlockTags.CLIMBABLE)) {
+ this.lastClimbablePos = Optional.of(blockposition);
+ return true;
+ } else if (iblockdata.getBlock() instanceof TrapDoorBlock && this.trapdoorUsableAsLadder(blockposition, iblockdata)) {
+ this.lastClimbablePos = Optional.of(blockposition);
+ return true;
+ } else {
+ return false;
+ }
}
+ return false;
}
}
@@ -3405,9 +3409,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;
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index 29fa9ad2223de668c15a5e5b433704b2c4765610..9899a19d351f01f96a28f916894db6189d4c8133 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;
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..8e1d6032a1edea8d3128fd7e2e3d8fde691eca7e 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
@@ -35,12 +35,12 @@ public class LongJumpToRandomPos<E extends Mob> extends Behavior<E> {
private static final int PREPARE_JUMP_DURATION = 40;
protected static final int MIN_PATHFIND_DISTANCE_TO_VALID_JUMP = 8;
private static final int TIME_OUT_DURATION = 200;
- private static final List<Integer> ALLOWED_ANGLES = Lists.newArrayList(65, 70, 75, 80);
+ private static final List<Integer> ALLOWED_ANGLES = Collections.synchronizedList(Lists.newArrayList(65, 70, 75, 80));
private final UniformInt timeBetweenLongJumps;
protected final int maxLongJumpHeight;
protected final int maxLongJumpWidth;
protected final float maxJumpVelocity;
- protected List<LongJumpToRandomPos.PossibleJump> jumpCandidates = Lists.newArrayList();
+ protected List<LongJumpToRandomPos.PossibleJump> jumpCandidates = Lists.newCopyOnWriteArrayList();
protected Optional<Vec3> initialPosition = Optional.empty();
@Nullable
protected Vec3 chosenJump;
diff --git a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
index 097007c1c25ba55d9916fc820dd1d1149d81f6f4..16eec12db529dd513e0971289a9326652369de58 100644
--- a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
+++ b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
@@ -30,11 +30,11 @@ import org.slf4j.Logger;
public class GossipContainer {
private static final Logger LOGGER = LogUtils.getLogger();
public static final int DISCARD_THRESHOLD = 2;
- public final Map<UUID, GossipContainer.EntityGossips> gossips = Maps.newHashMap();
+ public final Map<UUID, GossipContainer.EntityGossips> gossips = Maps.newConcurrentMap();
@VisibleForDebug
public Map<UUID, Object2IntMap<GossipType>> getGossipEntries() {
- Map<UUID, Object2IntMap<GossipType>> map = Maps.newHashMap();
+ Map<UUID, Object2IntMap<GossipType>> map = Maps.newConcurrentMap();
this.gossips.keySet().forEach((uuid) -> {
GossipContainer.EntityGossips entityGossips = this.gossips.get(uuid);
map.put(uuid, entityGossips.entries);
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
index 9f138bc471b5c2a4fa813ff943dbe34018b8df74..5c8a90f8536c9291df5891d8c75de963b75ec4bd 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
@@ -7,6 +7,7 @@ import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
+import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import java.util.List;
import java.util.Map;
@@ -25,8 +26,9 @@ import org.slf4j.Logger;
public class PoiSection {
private static final Logger LOGGER = LogUtils.getLogger();
- private final Short2ObjectMap<PoiRecord> records = new Short2ObjectOpenHashMap<>();
- private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newHashMap(); public final Map<Holder<PoiType>, Set<PoiRecord>> getData() { return this.byType; } // Paper - public accessor
+ private final Short2ObjectMap<PoiRecord> records = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>());
+ private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newConcurrentMap();
+ public final Map<Holder<PoiType>, Set<PoiRecord>> getData() { return this.byType; } // Paper - public accessor
private final Runnable setDirty;
private boolean isValid;
public final Optional<PoiSection> noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index c58496c84b2b3f86890050813041fa49711f3a01..08775845760583e9f7153b99cb94f8e725171a1c 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,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
+import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
@@ -239,21 +240,25 @@ 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 ReentrantLock mergeLock = new ReentrantLock(); //MCMT -- fix some concurrent problems
+
private void mergeWithNeighbours() {
- if (this.isMergable()) {
- // Spigot start
- double radius = level.spigotConfig.itemMerge;
- List<ItemEntity> list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> {
- // Spigot end
- return entityitem != this && entityitem.isMergable();
- });
- Iterator iterator = list.iterator();
-
- while (iterator.hasNext()) {
- ItemEntity entityitem = (ItemEntity) iterator.next();
-
- if (entityitem.isMergable()) {
- // Paper Start - Fix items merging through walls
+ //Hearse -- Just softly lock it,See the pr on MCMT : https://github.com/himekifee/MCMTFabric/pull/42/commits/16749534d808dab5bab434b293337a3cd558a4cf
+ if (!this.mergeLock.tryLock()){
+ return;
+ }
+ try {
+ if (this.isMergable()) {
+ // Spigot start
+ double radius = level.spigotConfig.itemMerge;
+ List<ItemEntity> list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> {
+ // Spigot end
+ return entityitem != this && entityitem.isMergable();
+ });
+
+ for (ItemEntity entityitem : list) {
+ if (entityitem.isMergable()) {
+ // Paper Start - Fix items merging through walls
if (this.level.paperConfig().fixes.fixItemsMergingThroughWalls) {
// Pufferfish start - skip the allocations
/*
@@ -263,17 +268,19 @@ public class ItemEntity extends Entity {
if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue;
*/
if (level.rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) ==
- net.minecraft.world.phys.HitResult.Type.BLOCK) continue;
+ net.minecraft.world.phys.HitResult.Type.BLOCK) continue;
// Pufferfish end
}
- // Paper End
- this.tryToMerge(entityitem);
- if (this.isRemoved()) {
- break;
+ // Paper End
+ this.tryToMerge(entityitem);
+ if (this.isRemoved()) {
+ break;
+ }
}
}
}
-
+ }finally {
+ this.mergeLock.unlock();
}
}
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..2db5342ec8e1c22ee890cc2b4f34a8cea32a3e1e 100644
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
@@ -281,9 +281,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
this.mobTick(true);
}
}
- maybeDecayGossip();
+ this.maybeDecayGossip();
// Paper end
-
super.inactiveTick();
}
// Spigot End
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..213556c4df3453774fcae29b50803bfd609c1852 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -16,6 +16,8 @@ import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;
+
+import net.himeki.mcmtfabric.parallelised.fastutil.Int2ObjectConcurrentHashMap;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
@@ -114,10 +116,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 = new Int2ObjectConcurrentHashMap<>();
Heightmap.Types[] aheightmap_type = Heightmap.Types.values();
int j = aheightmap_type.length;
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
index d783072bc964e45c308197e6f79874eb4a09f871..e63b13484abb7a4f9995f7b8725277caa3edc146 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
@@ -21,6 +21,9 @@ import java.util.concurrent.CompletionException;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import javax.annotation.Nullable;
+
+import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentLongLinkedOpenHashSet;
+import net.himeki.mcmtfabric.parallelised.fastutil.Long2ObjectOpenConcurrentHashMap;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.core.RegistryAccess;
@@ -38,8 +41,8 @@ public class SectionStorage<R> extends RegionFileStorage implements AutoCloseabl
private static final Logger LOGGER = LogUtils.getLogger();
private static final String SECTIONS_TAG = "Sections";
// Paper - remove mojang I/O thread
- private final Long2ObjectMap<Optional<R>> storage = new Long2ObjectOpenHashMap<>();
- private final LongLinkedOpenHashSet dirty = new LongLinkedOpenHashSet();
+ private final Long2ObjectMap<Optional<R>> storage = new Long2ObjectOpenConcurrentHashMap<>();
+ private final LongLinkedOpenHashSet dirty = new ConcurrentLongLinkedOpenHashSet();
private final Function<Runnable, Codec<R>> codec;
private final Function<Runnable, R> factory;
private final DataFixer fixerUpper;
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<T extends EntityAccess> {
private static final Logger LOGGER = LogUtils.getLogger();
- private final Int2ObjectMap<T> byId = new Int2ObjectLinkedOpenHashMap<>();
- private final Map<UUID, T> byUuid = Maps.newHashMap();
+ private final Int2ObjectMap<T> byId = Int2ObjectMaps.synchronize(new Int2ObjectLinkedOpenHashMap<>());
+ private final Map<UUID, T> byUuid = Maps.newConcurrentMap();
public <U extends T> void getEntities(EntityTypeTest<T, U> filter, AbortableIterationConsumer<U> 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..c6ad5f54ef0609c8cc2a13844576c5540efb17f3 100644
--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
@@ -1,29 +1,38 @@
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 co.m2ek4u.hearse.ForkJoinTickThread;
+import co.m2ek4u.hearse.HearseConfig;
+import it.unimi.dsi.fastutil.objects.ObjectArraySet;
+import it.unimi.dsi.fastutil.objects.ObjectSets;
+import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.Entity;
-public class EntityTickList {
- public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Entity> entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? // Pufferfish - private->public
-
- private void ensureActiveIsNotIterated() {
- // Paper - replace with better logic, do not delay removals
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
- }
+public class EntityTickList {
+ public final Set<Entity> entities = ObjectSets.synchronize(new ObjectArraySet<>());
+ private static final AtomicInteger threadId = new AtomicInteger();
+ private static final int threadCount = HearseConfig.getInt("entity-list-foreacher-threadcount",Math.min(Runtime.getRuntime().availableProcessors()/4,2));
+ private static final ForkJoinPool FOREACH_POOL = new ForkJoinPool(threadCount,forkJoinPool -> {
+ ForkJoinTickThread worker = new ForkJoinTickThread(forkJoinPool);
+ worker.setDaemon(true);
+ worker.setContextClassLoader(MinecraftServer.class.getClassLoader());
+ worker.setName("Hearse-ForkJoin-Worker # "+threadId.getAndIncrement());
+ return worker;
+ },null,true);
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
}
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
}
@@ -33,17 +42,12 @@ public class EntityTickList {
public void forEach(Consumer<Entity> 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<Entity> iterator = this.entities.iterator();
- try {
- while (iterator.hasNext()) {
- action.accept(iterator.next());
- }
- } finally {
- iterator.finishedIterating();
+
+ if (MinecraftServer.getServer().asyncEntityEnabled){
+ final List<Entity> copiedList = new ArrayList<>(this.entities);
+ FOREACH_POOL.submit(()->copiedList.parallelStream().forEach(action)).join();
+ }else{
+ this.entities.forEach(action);
}
- // 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<T extends EntityAccess> implements AutoCloseable {
static final Logger LOGGER = LogUtils.getLogger();
- final Set<UUID> knownUuids = Sets.newHashSet();
+ final Set<UUID> knownUuids = Sets.newConcurrentHashSet();
final LevelCallback<T> callbacks;
public final EntityPersistentStorage<T> permanentStorage;
private final EntityLookup<T> visibleEntityStorage = new EntityLookup<>();
final EntitySectionStorage<T> sectionStorage;
private final LevelEntityGetter<T> entityGetter;
- private final Long2ObjectMap<Visibility> chunkVisibility = new Long2ObjectOpenHashMap();
- private final Long2ObjectMap<PersistentEntitySectionManager.ChunkLoadStatus> chunkLoadStatuses = new Long2ObjectOpenHashMap();
- private final LongSet chunksToUnload = new LongOpenHashSet();
+ private final Long2ObjectMap<Visibility> chunkVisibility = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap());
+ private final Long2ObjectMap<PersistentEntitySectionManager.ChunkLoadStatus> chunkLoadStatuses = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap());
+ private final LongSet chunksToUnload = LongSets.synchronize(new LongOpenHashSet());
private final Queue<ChunkEntities<T>> loadingInbox = Queues.newConcurrentLinkedQueue();
public PersistentEntitySectionManager(Class<T> entityClass, LevelCallback<T> handler, EntityPersistentStorage<T> 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..b0efa8304dedbc8e2aaa21889278191d9c57b81d 100644
--- a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
+++ b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
@@ -6,14 +6,16 @@ import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+
+import net.himeki.mcmtfabric.parallelised.ConcurrentDoublyLinkedList;
import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.phys.Vec3;
public class EuclideanGameEventListenerRegistry implements GameEventListenerRegistry {
- private final List<GameEventListener> listeners = Lists.newArrayList();
- private final Set<GameEventListener> listenersToRemove = Sets.newHashSet();
- private final List<GameEventListener> listenersToAdd = Lists.newArrayList();
+ private final List<GameEventListener> listeners = new ConcurrentDoublyLinkedList<>();
+ private final Set<GameEventListener> listenersToRemove = Sets.newConcurrentHashSet();
+ private final List<GameEventListener> listenersToAdd = Lists.newCopyOnWriteArrayList();
private boolean processing;
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..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/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java b/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java
index 3d5ce92c77bc107e2ec2f54dc849b99c3abf9718..3c94993f713bb16f17b760b827b797cac29bdace 100644
--- a/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java
+++ b/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java
@@ -6,6 +6,9 @@ import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongList;
import java.util.function.LongPredicate;
+
+import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentLongLinkedOpenHashSet;
+import net.himeki.mcmtfabric.parallelised.fastutil.Long2ByteConcurrentHashMap;
import net.minecraft.util.Mth;
public abstract class DynamicGraphMinFixedPoint {
@@ -24,7 +27,7 @@ public abstract class DynamicGraphMinFixedPoint {
this.queues = new LongLinkedOpenHashSet[levelCount];
for(int i = 0; i < levelCount; ++i) {
- this.queues[i] = new LongLinkedOpenHashSet(expectedLevelSize, 0.5F) {
+ this.queues[i] = new ConcurrentLongLinkedOpenHashSet(expectedLevelSize, 0.5F) {
protected void rehash(int i) {
if (i > expectedLevelSize) {
super.rehash(i);
@@ -34,14 +37,7 @@ public abstract class DynamicGraphMinFixedPoint {
};
}
- this.computedLevels = new Long2ByteOpenHashMap(expectedTotalSize, 0.5F) {
- protected void rehash(int i) {
- if (i > expectedTotalSize) {
- super.rehash(i);
- }
-
- }
- };
+ this.computedLevels = new Long2ByteConcurrentHashMap(expectedTotalSize, 0.5F);
this.computedLevels.defaultReturnValue((byte)-1);
this.firstQueuedLevel = 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..5db8b0b5a5b43f722a2cf672dfca852d18f78505 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java
@@ -4,10 +4,8 @@ public class BinaryHeap {
private Node[] heap = new Node[128];
private int size;
- public Node insert(Node node) {
- if (node.heapIdx >= 0) {
- throw new IllegalStateException("OW KNOWS!");
- } else {
+ public synchronized Node insert(Node node) {
+ if (node.heapIdx < 0) {
if (this.size == this.heap.length) {
Node[] nodes = new Node[this.size << 1];
System.arraycopy(this.heap, 0, nodes, 0, this.size);
@@ -17,19 +15,19 @@ public class BinaryHeap {
this.heap[this.size] = node;
node.heapIdx = this.size;
this.upHeap(this.size++);
- return node;
}
+ return node;
}
- 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 +39,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 +53,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 +64,16 @@ public class BinaryHeap {
}
- public int size() {
+ public synchronized int size() {
return this.size;
}
private void upHeap(int index) {
+ //MCMT -- Fix array out of bounds exception
+ if (index == -1){
+ return;
+ }
+
Node node = this.heap[index];
int i;
@@ -90,6 +93,11 @@ public class BinaryHeap {
}
private void downHeap(int index) {
+ //MCMT -- Fix array out of bounds exception
+ if (index == -1){
+ return;
+ }
+
Node node = this.heap[index];
float f = node.f;
@@ -135,11 +143,11 @@ public class BinaryHeap {
node.heapIdx = index;
}
- public boolean isEmpty() {
+ public synchronized boolean isEmpty() {
return this.size == 0;
}
- public Node[] getHeap() {
+ public synchronized Node[] getHeap() {
Node[] nodes = new Node[this.size()];
System.arraycopy(this.heap, 0, nodes, 0, this.size());
return nodes;
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java
index b0bae04ab5a93dd4cf1eeeb02bed1e508e1f2913..d427735eff0056c171591709829d0bb76f7bb6f3 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java
@@ -1,6 +1,7 @@
package net.minecraft.world.level.pathfinder;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.EnumSet;
import java.util.List;
@@ -15,7 +16,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
public class FlyNodeEvaluator extends WalkNodeEvaluator {
- private final Long2ObjectMap<BlockPathTypes> pathTypeByPosCache = new Long2ObjectOpenHashMap<>();
+ private final Long2ObjectMap<BlockPathTypes> pathTypeByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
private static final float SMALL_MOB_INFLATED_START_NODE_BOUNDING_BOX = 1.5F;
private static final int MAX_START_NODE_CANDIDATES = 10;
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java
index a8a2594b8f5b3ebf6a1f918c7d822ad35b051b17..890f510d34fe81b2afbc8b66e974fd427ac6145a 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java
@@ -1,8 +1,10 @@
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.server.MinecraftServer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.level.BlockGetter;
@@ -11,7 +13,7 @@ import net.minecraft.world.level.PathNavigationRegion;
public abstract class NodeEvaluator {
protected PathNavigationRegion level;
protected Mob mob;
- protected final Int2ObjectMap<Node> nodes = new Int2ObjectOpenHashMap<>();
+ protected final Int2ObjectMap<Node> nodes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
protected int entityWidth;
protected int entityHeight;
protected int entityDepth;
@@ -39,9 +41,7 @@ public abstract class NodeEvaluator {
}
protected Node getNode(int x, int y, int z) {
- return this.nodes.computeIfAbsent(Node.createHash(x, y, z), (l) -> {
- return new Node(x, y, z);
- });
+ return this.nodes.computeIfAbsent(Node.createHash(x, y, z), (l) -> new Node(x, y, z));
}
public abstract Node getStart();
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/Path.java b/src/main/java/net/minecraft/world/level/pathfinder/Path.java
index 2a335f277bd0e4b8ad0f60d8226eb8aaa80a871f..96765e6fe34ed5bce3ebe9859714d9bd805d7d22 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/Path.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/Path.java
@@ -21,7 +21,7 @@ public class Path {
private final BlockPos target;
private final float distToTarget;
private final boolean reached;
- public boolean hasNext() { return getNextNodeIndex() < this.nodes.size(); } // Paper
+ public synchronized boolean hasNext() { return getNextNodeIndex() < this.nodes.size(); } // Paper
public Path(List<Node> nodes, BlockPos target, boolean reachesTarget) {
this.nodes = nodes;
@@ -30,51 +30,51 @@ public class Path {
this.reached = reachesTarget;
}
- public void advance() {
+ public synchronized void advance() {
++this.nextNodeIndex;
}
- public boolean notStarted() {
+ public synchronized boolean notStarted() {
return this.nextNodeIndex <= 0;
}
- public boolean isDone() {
+ public synchronized boolean isDone() {
return this.nextNodeIndex >= this.nodes.size();
}
@Nullable
- public Node getEndNode() {
+ public synchronized Node getEndNode() {
return !this.nodes.isEmpty() ? this.nodes.get(this.nodes.size() - 1) : null;
}
- public Node getNode(int index) {
+ public synchronized Node getNode(int index) {
return this.nodes.get(index);
}
- public void truncateNodes(int length) {
+ public synchronized void truncateNodes(int length) {
if (this.nodes.size() > length) {
this.nodes.subList(length, this.nodes.size()).clear();
}
}
- public void replaceNode(int index, Node node) {
+ public synchronized void replaceNode(int index, Node node) {
this.nodes.set(index, node);
}
- public int getNodeCount() {
+ public synchronized int getNodeCount() {
return this.nodes.size();
}
- public int getNextNodeIndex() {
+ public synchronized int getNextNodeIndex() {
return this.nextNodeIndex;
}
- public void setNextNodeIndex(int nodeIndex) {
+ public synchronized void setNextNodeIndex(int nodeIndex) {
this.nextNodeIndex = nodeIndex;
}
- public Vec3 getEntityPosAtNode(Entity entity, int index) {
+ public synchronized Vec3 getEntityPosAtNode(Entity entity, int index) {
Node node = this.nodes.get(index);
double d = (double)node.x + (double)((int)(entity.getBbWidth() + 1.0F)) * 0.5D;
double e = (double)node.y;
@@ -82,28 +82,28 @@ public class Path {
return new Vec3(d, e, f);
}
- public BlockPos getNodePos(int index) {
+ public synchronized BlockPos getNodePos(int index) {
return this.nodes.get(index).asBlockPos();
}
- public Vec3 getNextEntityPos(Entity entity) {
+ public synchronized Vec3 getNextEntityPos(Entity entity) {
return this.getEntityPosAtNode(entity, this.nextNodeIndex);
}
- public BlockPos getNextNodePos() {
+ public synchronized BlockPos getNextNodePos() {
return this.nodes.get(this.nextNodeIndex).asBlockPos();
}
- public Node getNextNode() {
+ public synchronized Node getNextNode() {
return this.nodes.get(this.nextNodeIndex);
}
@Nullable
- public Node getPreviousNode() {
+ public synchronized Node getPreviousNode() {
return this.nextNodeIndex > 0 ? this.nodes.get(this.nextNodeIndex - 1) : null;
}
- public boolean sameAs(@Nullable Path o) {
+ public synchronized boolean sameAs(@Nullable Path o) {
if (o == null) {
return false;
} else if (o.nodes.size() != this.nodes.size()) {
@@ -121,7 +121,7 @@ public class Path {
}
}
- public boolean canReach() {
+ public synchronized boolean canReach() {
return this.reached;
}
@@ -133,16 +133,16 @@ public class Path {
}
@VisibleForDebug
- public Node[] getOpenSet() {
+ public synchronized Node[] getOpenSet() {
return this.openSet;
}
@VisibleForDebug
- public Node[] getClosedSet() {
+ public synchronized Node[] getClosedSet() {
return this.closedSet;
}
- public void writeToStream(FriendlyByteBuf buffer) {
+ public synchronized void writeToStream(FriendlyByteBuf buffer) {
if (this.targetNodes != null && !this.targetNodes.isEmpty()) {
buffer.writeBoolean(this.reached);
buffer.writeInt(this.nextNodeIndex);
@@ -213,15 +213,15 @@ public class Path {
}
@Override
- public String toString() {
+ public synchronized String toString() {
return "Path(length=" + this.nodes.size() + ")";
}
- public BlockPos getTarget() {
+ public synchronized BlockPos getTarget() {
return this.target;
}
- public float getDistToTarget() {
+ public synchronized float getDistToTarget() {
return this.distToTarget;
}
}
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..eee47eb38754766f5ca3ff98cc4f875a7afba686 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
@@ -1,21 +1,15 @@
package net.minecraft.world.level.pathfinder;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.util.profiling.ProfilerFiller;
-import net.minecraft.util.profiling.metrics.MetricCategory;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.level.PathNavigationRegion;
+import javax.annotation.Nullable;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
public class PathFinder {
private static final float FUDGING = 1.5F;
@@ -31,7 +25,7 @@ public class PathFinder {
}
@Nullable
- public Path findPath(PathNavigationRegion world, Mob mob, Set<BlockPos> positions, float followRange, int distance, float rangeMultiplier) {
+ public synchronized Path findPath(PathNavigationRegion world, Mob mob, Set<BlockPos> positions, float followRange, int distance, float rangeMultiplier) {
this.openSet.clear();
this.nodeEvaluator.prepare(world, mob);
Node node = this.nodeEvaluator.getStart();
@@ -76,10 +70,9 @@ public class PathFinder {
node.closed = true;
// Paper start - optimize collection
- for(int i1 = 0; i1 < positions.size(); i1++) {
- final Map.Entry<Target, BlockPos> entry = positions.get(i1);
+ for (final Map.Entry<Target, BlockPos> entry : positions) {
Target target = entry.getKey();
- if (node.distanceManhattan(target) <= (float)distance) {
+ if (node.distanceManhattan(target) <= (float) distance) {
target.setReached();
entryList.add(entry);
// Paper end
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java
index 6084631b5b502279b84f190dc62fc76b770e368e..f526adbd31e65fc74af48f6137d293a7a7ceafbb 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java
@@ -2,6 +2,7 @@ package net.minecraft.world.level.pathfinder;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.Map;
import javax.annotation.Nullable;
@@ -17,7 +18,7 @@ import net.minecraft.world.level.material.FluidState;
public class SwimNodeEvaluator extends NodeEvaluator {
private final boolean allowBreaching;
- private final Long2ObjectMap<BlockPathTypes> pathTypesByPosCache = new Long2ObjectOpenHashMap<>();
+ private final Long2ObjectMap<BlockPathTypes> pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
public SwimNodeEvaluator(boolean canJumpOutOfWater) {
this.allowBreaching = canJumpOutOfWater;
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
index 894881018c659d874f28f5744f0b8247cfecb1c1..ae06f7ef9c4b8147508984f8b46176de46171285 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
@@ -1,8 +1,10 @@
package net.minecraft.world.level.pathfinder;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMaps;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import java.util.EnumSet;
import javax.annotation.Nullable;
@@ -33,8 +35,8 @@ public class WalkNodeEvaluator extends NodeEvaluator {
public static final double SPACE_BETWEEN_WALL_POSTS = 0.5D;
private static final double DEFAULT_MOB_JUMP_HEIGHT = 1.125D;
protected float oldWaterCost;
- private final Long2ObjectMap<BlockPathTypes> pathTypesByPosCache = new Long2ObjectOpenHashMap<>();
- private final Object2BooleanMap<AABB> collisionCache = new Object2BooleanOpenHashMap<>();
+ private final Long2ObjectMap<BlockPathTypes> pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
+ private final Object2BooleanMap<AABB> collisionCache = Object2BooleanMaps.synchronize(new Object2BooleanOpenHashMap<>());
@Override
public void prepare(PathNavigationRegion cachedWorld, Mob entity) {
diff --git a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
index b1c594dc6a6b8a6c737b99272acab9e7dbd0ed63..4aedee56077159aaf613033b688d2be6833f1ad1 100644
--- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
+++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
@@ -3,7 +3,10 @@ package net.minecraft.world.level.redstone;
import com.mojang.logging.LogUtils;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.List;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@@ -16,8 +19,8 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
private static final Logger LOGGER = LogUtils.getLogger();
private final Level level;
private final int maxChainedNeighborUpdates;
- private final ArrayDeque<CollectingNeighborUpdater.NeighborUpdates> stack = new ArrayDeque<>();
- private final List<CollectingNeighborUpdater.NeighborUpdates> addedThisLayer = new ArrayList<>();
+ private final Deque<NeighborUpdates> stack = new ConcurrentLinkedDeque<>();
+ private final List<CollectingNeighborUpdater.NeighborUpdates> addedThisLayer = new CopyOnWriteArrayList<>();
private int count = 0;
public CollectingNeighborUpdater(Level world, int maxChainDepth) {
@@ -26,22 +29,22 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
}
@Override
- public void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) {
+ public synchronized void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) {
this.addAndRun(pos, new CollectingNeighborUpdater.ShapeUpdate(direction, neighborState, pos.immutable(), neighborPos.immutable(), flags));
}
@Override
- public void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) {
+ public synchronized void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) {
this.addAndRun(pos, new CollectingNeighborUpdater.SimpleNeighborUpdate(pos, sourceBlock, sourcePos.immutable()));
}
@Override
- public void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
+ public synchronized void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
this.addAndRun(pos, new CollectingNeighborUpdater.FullNeighborUpdate(state, pos.immutable(), sourceBlock, sourcePos.immutable(), notify));
}
@Override
- public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) {
+ public synchronized void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) {
this.addAndRun(pos, new CollectingNeighborUpdater.MultiNeighborUpdate(pos.immutable(), sourceBlock, except));
}
diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java
index 5dea8414964e0d2d1fb15a6baa27227e9722bfc7..9953559a282afb6cb38b6cf495cc9250e3e13dae 100644
--- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java
+++ b/src/main/java/net/minecraft/world/ticks/LevelTicks.java
@@ -1,10 +1,7 @@
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 co.m2ek4u.hearse.HearseConfig;
+import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
import java.util.ArrayDeque;
@@ -15,10 +12,14 @@ import java.util.LongSummaryStatistics;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
+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;
+
+import it.unimi.dsi.fastutil.objects.ObjectSets;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
@@ -28,24 +29,23 @@ import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
public class LevelTicks<T> implements LevelTickAccess<T> {
- private static final Comparator<LevelChunkTicks<?>> CONTAINER_DRAIN_ORDER = (a, b) -> {
- return ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek());
- };
+ private static final Comparator<LevelChunkTicks<?>> CONTAINER_DRAIN_ORDER = (a, b) -> ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek());
private final LongPredicate tickCheck;
private final Supplier<ProfilerFiller> profiler;
- private final Long2ObjectMap<LevelChunkTicks<T>> allContainers = new Long2ObjectOpenHashMap<>();
- private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), (map) -> {
+ private final Long2ObjectMap<LevelChunkTicks<T>> allContainers = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
+ private final Long2LongMap nextTickForContainer = Long2LongMaps.synchronize(Util.make(new Long2LongOpenHashMap(), (map) -> {
map.defaultReturnValue(Long.MAX_VALUE);
- });
+ }));
private final Queue<LevelChunkTicks<T>> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER);
- private final Queue<ScheduledTick<T>> toRunThisTick = new ArrayDeque<>();
- private final List<ScheduledTick<T>> alreadyRunThisTick = new ArrayList<>();
- private final Set<ScheduledTick<?>> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH);
+ private final Queue<ScheduledTick<T>> toRunThisTick = new ConcurrentLinkedQueue<>();
+ private final List<ScheduledTick<T>> alreadyRunThisTick = new CopyOnWriteArrayList<>();
+ private final Set<ScheduledTick<?>> toRunThisTickSet = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH));
+ private static int taskLimit = HearseConfig.getInt("scheduled-task-limit-per-tick",65535);
+
private final BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> chunkScheduleUpdater = (chunkTickScheduler, tick) -> {
if (tick.equals(chunkTickScheduler.peek())) {
this.updateContainerScheduling(tick);
}
-
};
public LevelTicks(LongPredicate tickingFutureReadyPredicate, Supplier<ProfilerFiller> profilerGetter) {
@@ -123,7 +123,9 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
entry.setValue(scheduledTick.triggerTick());
} else if (this.tickCheck.test(l)) {
objectIterator.remove();
- this.containersToTick.add(levelChunkTicks);
+ synchronized (this.containersToTick){
+ this.containersToTick.add(levelChunkTicks);
+ }
}
}
}
@@ -133,27 +135,29 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
private void drainContainers(long time, int maxTicks) {
LevelChunkTicks<T> levelChunkTicks;
- while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) {
- ScheduledTick<T> scheduledTick = levelChunkTicks.poll();
- this.scheduleForThisTick(scheduledTick);
- this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks);
- ScheduledTick<T> scheduledTick2 = levelChunkTicks.peek();
- if (scheduledTick2 != null) {
- if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) {
- this.containersToTick.add(levelChunkTicks);
- } else {
- this.updateContainerScheduling(scheduledTick2);
+ synchronized (this.containersToTick){
+ while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) {
+ ScheduledTick<T> scheduledTick = levelChunkTicks.poll();
+ this.scheduleForThisTick(scheduledTick);
+ this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks);
+ ScheduledTick<T> scheduledTick2 = levelChunkTicks.peek();
+ if (scheduledTick2 != null) {
+ if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) {
+ this.containersToTick.add(levelChunkTicks);
+ } else {
+ this.updateContainerScheduling(scheduledTick2);
+ }
}
}
}
-
}
private void rescheduleLeftoverContainers() {
- for(LevelChunkTicks<T> levelChunkTicks : this.containersToTick) {
- this.updateContainerScheduling(levelChunkTicks.peek());
+ synchronized (this.containersToTick){
+ for(LevelChunkTicks<T> levelChunkTicks : this.containersToTick) {
+ this.updateContainerScheduling(levelChunkTicks.peek());
+ }
}
-
}
private void updateContainerScheduling(ScheduledTick<T> tick) {
@@ -186,24 +190,38 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
return this.toRunThisTick.size() < maxTicks;
}
+ private boolean allRunned;
+
private void runCollectedTicks(BiConsumer<BlockPos, T> ticker) {
+ int cycleCounter = 0;
while(!this.toRunThisTick.isEmpty()) {
+ if (cycleCounter >= taskLimit){
+ break;
+ }
ScheduledTick<T> scheduledTick = this.toRunThisTick.poll();
if (!this.toRunThisTickSet.isEmpty()) {
this.toRunThisTickSet.remove(scheduledTick);
}
-
this.alreadyRunThisTick.add(scheduledTick);
ticker.accept(scheduledTick.pos(), scheduledTick.type());
+ cycleCounter++;
}
-
+ if (cycleCounter >= taskLimit){
+ this.allRunned = false;
+ return;
+ }
+ this.allRunned = true;
}
private void cleanupAfterTick() {
- this.toRunThisTick.clear();
- this.containersToTick.clear();
+ if (this.allRunned){
+ this.toRunThisTick.clear();
+ synchronized (this.containersToTick){
+ this.containersToTick.clear();
+ }
+ this.toRunThisTickSet.clear();
+ }
this.alreadyRunThisTick.clear();
- this.toRunThisTickSet.clear();
}
@Override