diff --git a/README.md b/README.md index 8645f0ab..ac44495d 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ - Disabling the UseItemOnPacket Too Far check. - Update all dependencies to the latest. - Parallel entity ticking(Half of async)(In alpha) + - Parallel world ticking - Some Purpur patches. - ... @@ -64,4 +65,4 @@ Credits: - [Petal](https://github.com/Bloom-host/Petal) - [Carpet Fixes](https://github.com/fxmorin/carpet-fixes) - [VMP](https://github.com/RelativityMC/VMP-fabric) -- [Hearse](https://github.com/NaturalCodeClub/Hearse) +- [Hearse](https://github.com/NaturalCodeClub/HearseRewrite) diff --git a/patches/api/0011-Hearse-Add-mcmt-s-collections-and-fix-some-concurren.patch b/patches/api/0011-Hearse-Add-mcmt-s-collections-and-fix-some-concurren.patch new file mode 100644 index 00000000..823bc00b --- /dev/null +++ b/patches/api/0011-Hearse-Add-mcmt-s-collections-and-fix-some-concurren.patch @@ -0,0 +1,4532 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 21:08:52 +0800 +Subject: [PATCH] Hearse: Add mcmt's collections and fix some concurrent + problem + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3c29129dc02ddcfaad026d1f81e5da879a0d64cb +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java +@@ -0,0 +1,4 @@ ++package net.himeki.mcmtfabric.parallelised; ++ ++public class ConcurrentArrayDeque { ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java +new file mode 100644 +index 0000000000000000000000000000000000000000..67dd5fe624fe4428d8907000cb23a33485fd6bd9 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java +@@ -0,0 +1,41 @@ ++package net.himeki.mcmtfabric.parallelised; ++ ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++ ++import java.util.*; ++import java.util.concurrent.ConcurrentHashMap; ++import java.util.concurrent.ConcurrentLinkedDeque; ++import java.util.concurrent.CopyOnWriteArrayList; ++import java.util.stream.Collector; ++import java.util.stream.Collectors; ++ ++public class ConcurrentCollections { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ public static Set newHashSet() { ++ //LOGGER.info("Concurrent hash set created"); ++ return Collections.newSetFromMap(new ConcurrentHashMap()); ++ } ++ ++ public static Map newHashMap() { ++ //LOGGER.info("Concurrent hash map created"); ++ return new ConcurrentHashMap(); ++ } ++ ++ public static List newLinkedList() { ++ LOGGER.info("Concurrent \"linked\" list created"); ++ return new CopyOnWriteArrayList(); ++ } ++ ++ public static Collector> toList() { ++ return Collectors.toCollection(CopyOnWriteArrayList::new); ++ } ++ ++ public static Queue newArrayDeque() { ++ LOGGER.info("Concurrent \"array\" deque created"); ++ return new ConcurrentLinkedDeque(); ++ } ++ ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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 ++ */ ++ ++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 weakly consistent, returning ++ * elements reflecting the state of the deque at some point at or since the ++ * creation of the iterator. They do not throw ++ * {@link ConcurrentModificationException}, and may proceed concurrently with ++ * other operations. ++ * ++ *

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

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

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

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

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

++ * ++ * If the deque fits in the specified array with room to spare (i.e., the array ++ * has more elements than the deque), the element in the array immediately ++ * following the end of the collection is set to null. This is useful in ++ * determining the length of the deque only if the caller knows that the ++ * deque does not contain any null elements. ++ * ++ * @param a the array into which the elements of the deque are to be stored, if ++ * it is big enough; otherwise, a new array of the same runtime type is ++ * allocated for this purpose. ++ * @return an array containing the elements of the deque. ++ * @throws ArrayStoreException if the runtime type of a is not a supertype of ++ * the runtime type of every element in this deque. ++ * @throws NullPointerException if the specified array is null. ++ */ ++ public T[] toArray(T[] a) { ++ return toArrayList().toArray(a); ++ } ++ ++ /** ++ * Returns a weakly consistent iterator over the elements in this deque, in ++ * first-to-last order. The next method returns elements reflecting the ++ * state of the deque at some point at or since the creation of the iterator. ++ * The method does not throw {@link ConcurrentModificationException}, ++ * and may proceed concurrently with other operations. ++ * ++ * @return an iterator over the elements in this deque ++ */ ++ public Iterator iterator() { ++ return new CLDIterator(); ++ } ++ ++ final class CLDIterator implements Iterator { ++ Node last; ++ ++ Node next = header.forward(); ++ ++ public boolean hasNext() { ++ return next != null; ++ } ++ ++ public E next() { ++ Node l = last = next; ++ if (l == null) ++ throw new NoSuchElementException(); ++ next = next.forward(); ++ return l.element; ++ } ++ ++ public void remove() { ++ Node l = last; ++ if (l == null) ++ throw new IllegalStateException(); ++ while (!l.delete() && !l.isDeleted()) ++ ; ++ } ++ } ++ ++ @Override ++ public boolean addAll(int index, Collection c) { ++ throw new NotImplementedException("TODO"); ++ } ++ ++ @Override ++ public E get(int index) { ++ Node current = header.successor(); ++ if (current == null) { ++ throw new IndexOutOfBoundsException(); ++ } ++ for (; index > 0; index --) { ++ current = current.successor(); ++ if (current == null) { ++ throw new IndexOutOfBoundsException(); ++ } ++ } ++ return current.element; ++ } ++ ++ @Override ++ public E set(int index, E element) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public void add(int index, E element) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public E remove(int index) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public int indexOf(Object o) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public int lastIndexOf(Object o) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public ListIterator listIterator() { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public ListIterator listIterator(int index) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public List subList(int fromIndex, int toIndex) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++} ++ ++/** ++ * Linked Nodes. As a minor efficiency hack, this class opportunistically ++ * inherits from AtomicReference, with the atomic ref used as the "next" link. ++ * ++ * Nodes are in doubly-linked lists. There are three kinds of special nodes, ++ * distinguished by: * The list header has a null prev link * The list trailer ++ * has a null next link * A deletion marker has a prev link pointing to itself. ++ * All three kinds of special nodes have null element fields. ++ * ++ * Regular nodes have non-null element, next, and prev fields. To avoid visible ++ * inconsistencies when deletions overlap element replacement, replacements are ++ * done by replacing the node, not just setting the element. ++ * ++ * Nodes can be traversed by read-only ConcurrentLinkedDeque class operations ++ * just by following raw next pointers, so long as they ignore any special nodes ++ * seen along the way. (This is automated in method forward.) However, traversal ++ * using prev pointers is not guaranteed to see all live nodes since a prev ++ * pointer of a deleted node can become unrecoverably stale. ++ */ ++ ++class Node extends AtomicReference> { ++ ++ private static final long serialVersionUID = 6640557564507962862L; ++ ++ private volatile Node prev; ++ ++ final E element; ++ ++ /** Creates a node with given contents */ ++ Node(E element, Node next, Node prev) { ++ super(next); ++ this.prev = prev; ++ this.element = element; ++ } ++ ++ /** Creates a marker node with given successor */ ++ Node(Node next) { ++ super(next); ++ this.prev = this; ++ this.element = null; ++ } ++ ++ /** ++ * Gets next link (which is actually the value held as atomic reference). ++ */ ++ private Node getNext() { ++ return get(); ++ } ++ ++ /** ++ * Sets next link ++ * ++ * @param n the next node ++ */ ++ void setNext(Node n) { ++ set(n); ++ } ++ ++ /** ++ * compareAndSet next link ++ */ ++ private boolean casNext(Node cmp, Node val) { ++ return compareAndSet(cmp, val); ++ } ++ ++ /** ++ * Gets prev link ++ */ ++ private Node getPrev() { ++ return prev; ++ } ++ ++ /** ++ * Sets prev link ++ * ++ * @param b the previous node ++ */ ++ void setPrev(Node b) { ++ prev = b; ++ } ++ ++ /** ++ * Returns true if this is a header, trailer, or marker node ++ */ ++ boolean isSpecial() { ++ return element == null; ++ } ++ ++ /** ++ * Returns true if this is a trailer node ++ */ ++ boolean isTrailer() { ++ return getNext() == null; ++ } ++ ++ /** ++ * Returns true if this is a header node ++ */ ++ boolean isHeader() { ++ return getPrev() == null; ++ } ++ ++ /** ++ * Returns true if this is a marker node ++ */ ++ boolean isMarker() { ++ return getPrev() == this; ++ } ++ ++ /** ++ * Returns true if this node is followed by a marker, meaning that it is ++ * deleted. ++ * ++ * @return true if this node is deleted ++ */ ++ boolean isDeleted() { ++ Node f = getNext(); ++ return f != null && f.isMarker(); ++ } ++ ++ /** ++ * Returns next node, ignoring deletion marker ++ */ ++ private Node nextNonmarker() { ++ Node f = getNext(); ++ return (f == null || !f.isMarker()) ? f : f.getNext(); ++ } ++ ++ /** ++ * Returns the next non-deleted node, swinging next pointer around any ++ * encountered deleted nodes, and also patching up successor''s prev link to ++ * point back to this. Returns null if this node is trailer so has no successor. ++ * ++ * @return successor, or null if no such ++ */ ++ Node successor() { ++ Node f = nextNonmarker(); ++ for (;;) { ++ if (f == null) ++ return null; ++ if (!f.isDeleted()) { ++ if (f.getPrev() != this && !isDeleted()) ++ f.setPrev(this); // relink f's prev ++ return f; ++ } ++ Node s = f.nextNonmarker(); ++ if (f == getNext()) ++ casNext(f, s); // unlink f ++ f = s; ++ } ++ } ++ ++ /** ++ * Returns the apparent predecessor of target by searching forward for it ++ * starting at this node, patching up pointers while traversing. Used by ++ * predecessor(). ++ * ++ * @return target's predecessor, or null if not found ++ */ ++ private Node findPredecessorOf(Node target) { ++ Node n = this; ++ for (;;) { ++ Node f = n.successor(); ++ if (f == target) ++ return n; ++ if (f == null) ++ return null; ++ n = f; ++ } ++ } ++ ++ /** ++ * Returns the previous non-deleted node, patching up pointers as needed. ++ * Returns null if this node is header so has no successor. May also return null ++ * if this node is deleted, so doesn't have a distinct predecessor. ++ * ++ * @return predecessor or null if not found ++ */ ++ Node predecessor() { ++ Node n = this; ++ for (;;) { ++ Node b = n.getPrev(); ++ if (b == null) ++ return n.findPredecessorOf(this); ++ Node s = b.getNext(); ++ if (s == this) ++ return b; ++ if (s == null || !s.isMarker()) { ++ Node p = b.findPredecessorOf(this); ++ if (p != null) ++ return p; ++ } ++ n = b; ++ } ++ } ++ ++ /** ++ * Returns the next node containing a nondeleted user element. Use for forward ++ * list traversal. ++ * ++ * @return successor, or null if no such ++ */ ++ Node forward() { ++ Node f = successor(); ++ return (f == null || f.isSpecial()) ? null : f; ++ } ++ ++ /** ++ * Returns previous node containing a nondeleted user element, if possible. Use ++ * for backward list traversal, but beware that if this method is called from a ++ * deleted node, it might not be able to determine a usable predecessor. ++ * ++ * @return predecessor, or null if no such could be found ++ */ ++ Node back() { ++ Node f = predecessor(); ++ return (f == null || f.isSpecial()) ? null : f; ++ } ++ ++ /** ++ * Tries to insert a node holding element as successor, failing if this node is ++ * deleted. ++ * ++ * @param element the element ++ * @return the new node, or null on failure. ++ */ ++ Node append(E element) { ++ for (;;) { ++ Node f = getNext(); ++ if (f == null || f.isMarker()) ++ return null; ++ Node x = new Node(element, f, this); ++ if (casNext(f, x)) { ++ f.setPrev(x); // optimistically link ++ return x; ++ } ++ } ++ } ++ ++ /** ++ * Tries to insert a node holding element as predecessor, failing if no live ++ * predecessor can be found to link to. ++ * ++ * @param element the element ++ * @return the new node, or null on failure. ++ */ ++ Node prepend(E element) { ++ for (;;) { ++ Node b = predecessor(); ++ if (b == null) ++ return null; ++ Node x = new Node(element, this, b); ++ if (b.casNext(this, x)) { ++ setPrev(x); // optimistically link ++ return x; ++ } ++ } ++ } ++ ++ /** ++ * Tries to mark this node as deleted, failing if already deleted or if this ++ * node is header or trailer ++ * ++ * @return true if successful ++ */ ++ boolean delete() { ++ Node b = getPrev(); ++ Node f = getNext(); ++ if (b != null && f != null && !f.isMarker() && casNext(f, new Node(f))) { ++ if (b.casNext(this, f)) ++ f.setPrev(b); ++ return true; ++ } ++ return false; ++ } ++ ++ /** ++ * Tries to insert a node holding element to replace this node. failing if ++ * already deleted. ++ * ++ * @param newElement the new element ++ * @return the new node, or null on failure. ++ */ ++ Node replace(E newElement) { ++ for (;;) { ++ Node b = getPrev(); ++ Node f = getNext(); ++ if (b == null || f == null || f.isMarker()) ++ return null; ++ Node x = new Node(newElement, f, b); ++ if (casNext(f, new Node(x))) { ++ b.successor(); // to relink b ++ x.successor(); // to relink f ++ return x; ++ } ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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 backing; ++ ++ public ConcurrentLongLinkedOpenHashSet() { ++ //backing = new ConcurrentLinkedDeque(); ++ backing = new ConcurrentSkipListSet(); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final int initial) { ++ //backing = new ConcurrentLinkedDeque(); ++ backing = new ConcurrentSkipListSet(); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final int initial, final float dnc) { ++ this(initial); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final LongCollection c) { ++ this(c.size()); ++ addAll(c); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final LongCollection c, final float f) { ++ this(c.size(), f); ++ addAll(c); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final LongIterator i, final float f) { ++ this(16, f); ++ while (i.hasNext()) ++ add(i.nextLong()); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final LongIterator i) { ++ this(i, -1); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final Iterator i, final float f) { ++ this(LongIterators.asLongIterator(i), f); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final Iterator i) { ++ this(LongIterators.asLongIterator(i)); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length, final float f) { ++ this(length < 0 ? 0 : length, f); ++ LongArrays.ensureOffsetLength(a, offset, length); ++ for (int i = 0; i < length; i++) ++ add(a[offset + i]); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length) { ++ this(a, offset, length, DEFAULT_LOAD_FACTOR); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final long[] a, final float f) { ++ this(a, 0, a.length, f); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final long[] a) { ++ this(a, -1); ++ } ++ ++ @Override ++ public boolean add(final long k) { ++ boolean out = backing.add(k); ++ /* ++ if (!firstDef) { ++ first = k; ++ firstDef = true; ++ } ++ last = k; ++ */ ++ return out; ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return addAll((Collection) c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean addAndMoveToFirst(final long k) { ++ boolean out = backing.add(k); ++ //first = k; ++ return out; ++ } ++ ++ @Override ++ public boolean addAndMoveToLast(final long k) { ++ boolean out = backing.add(k); ++ //last = k; ++ return out; ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public LongLinkedOpenHashSet clone() { ++ return new ConcurrentLongLinkedOpenHashSet(backing.iterator()); ++ } ++ ++ @Override ++ public LongComparator comparator() { ++ return null; ++ } ++ ++ @Override ++ public boolean contains(final long k) { ++ return backing.contains(k); ++ } ++ ++ @Override ++ public long firstLong() { ++ /* ++ if (backing.size() == 0) throw new NoSuchElementException(); ++ return first; ++ */ ++ return backing.first(); ++ } ++ ++ @Override ++ public int hashCode() { ++ return backing.hashCode(); ++ } ++ ++ @Override ++ public LongSortedSet headSet(long to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public LongListIterator iterator() { ++ return FastUtilHackUtil.wrap(backing.iterator()); ++ } ++ ++ @Override ++ public LongListIterator iterator(long from) { ++ throw new IllegalStateException(); ++ //return FastUtilHackUtil.wrap(backing.iterator()); ++ } ++ ++ @Override ++ public long lastLong() { ++ /* ++ if (backing.size() == 0) throw new NoSuchElementException(); ++ return last; ++ */ ++ return backing.last(); ++ } ++ ++ @Override ++ public boolean remove(final long k) { ++ /* ++ if (k == first) { ++ first = backing.iterator().next(); ++ } ++ if (k == last) { ++ last = backing.iterator().next(); ++ } ++ */ ++ return backing.remove(k); ++ } ++ ++ @Override ++ public long removeFirstLong() { ++ long fl = this.firstLong(); ++ this.remove(fl); ++ //first = backing.iterator().next(); ++ return fl; ++ } ++ ++ @Override ++ public long removeLastLong() { ++ long fl = this.lastLong(); ++ this.remove(fl); ++ //last = backing.iterator().next(); ++ return fl; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public LongSortedSet subSet(long from, long to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongSortedSet tailSet(long from) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public boolean trim() { ++ return true; ++ } ++ ++ @Override ++ public boolean trim(final int n) { ++ return true; ++ } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..93bd066ec2013e42a85fcf21344fe41f3ad69598 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java +@@ -0,0 +1,144 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.longs.*; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.Collection; ++import java.util.concurrent.ConcurrentSkipListSet; ++ ++public class ConcurrentLongSortedSet implements LongSortedSet { ++ ++ ConcurrentSkipListSet back = new ConcurrentSkipListSet<>(); ++ ++ @Override ++ public LongBidirectionalIterator iterator(long fromElement) { ++ return null; ++ } ++ ++ @Override ++ public int size() { ++ return back.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return back.isEmpty(); ++ } ++ ++ @Override ++ public LongBidirectionalIterator iterator() { ++ return null; ++ } ++ ++ @NotNull ++ @Override ++ public Object[] toArray() { ++ return back.toArray(); ++ } ++ ++ @NotNull ++ @Override ++ public T[] toArray(@NotNull T[] ts) { ++ return null; ++ } ++ ++ @Override ++ public boolean containsAll(@NotNull Collection collection) { ++ return back.containsAll(collection); ++ } ++ ++ @Override ++ public boolean addAll(@NotNull Collection collection) { ++ return back.addAll(collection); ++ } ++ ++ @Override ++ public boolean removeAll(@NotNull Collection collection) { ++ return back.removeAll(collection); ++ } ++ ++ @Override ++ public boolean retainAll(@NotNull Collection collection) { ++ return back.retainAll(collection); ++ } ++ ++ @Override ++ public void clear() { ++ back.clear(); ++ } ++ ++ @Override ++ public boolean add(long key) { ++ return back.add(key); ++ } ++ ++ @Override ++ public boolean contains(long key) { ++ return back.contains(key); ++ } ++ ++ @Override ++ public long[] toLongArray() { ++ return new long[0]; ++ } ++ ++ @Override ++ public long[] toArray(long[] a) { ++ return new long[0]; ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return back.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(LongCollection c) { ++ return back.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(LongCollection c) { ++ return back.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(LongCollection c) { ++ return back.retainAll(c); ++ } ++ ++ @Override ++ public boolean remove(long k) { ++ return back.remove(k); ++ } ++ ++ @Override ++ public LongSortedSet subSet(long fromElement, long toElement) { ++ return new LongAVLTreeSet(back.subSet(fromElement,toElement)); ++ } ++ ++ @Override ++ public LongSortedSet headSet(long toElement) { ++ return new LongAVLTreeSet(back.headSet(toElement)); ++ } ++ ++ @Override ++ public LongSortedSet tailSet(long fromElement) { ++ return new LongAVLTreeSet(back.tailSet(fromElement)); ++ } ++ ++ @Override ++ public LongComparator comparator() { ++ return null; ++ } ++ ++ @Override ++ public long firstLong() { ++ return back.first(); ++ } ++ ++ @Override ++ public long lastLong() { ++ return back.last(); ++ } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ff1a4f87356459d3bc990a77c3081932046da5b1 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java +@@ -0,0 +1,112 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.shorts.ShortCollection; ++import it.unimi.dsi.fastutil.shorts.ShortIterator; ++import it.unimi.dsi.fastutil.shorts.ShortSet; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.Collection; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class ConcurrentShortHashSet implements ShortSet { ++ ++ ConcurrentHashMap.KeySetView backing = ConcurrentHashMap.newKeySet(); ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public ShortIterator iterator() { ++ return new FastUtilHackUtil.WrappingShortIterator(backing.iterator()); ++ } ++ ++ @NotNull ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @NotNull ++ @Override ++ public T[] toArray(@NotNull T[] ts) { ++ return (T[]) backing.toArray(); ++ } ++ ++ @Override ++ public boolean containsAll(@NotNull Collection collection) { ++ return backing.containsAll(collection); ++ } ++ ++ @Override ++ public boolean addAll(@NotNull Collection collection) { ++ return backing.addAll(collection); ++ } ++ ++ @Override ++ public boolean removeAll(@NotNull Collection collection) { ++ return backing.removeAll(collection); ++ } ++ ++ @Override ++ public boolean retainAll(@NotNull Collection collection) { ++ return backing.retainAll(collection); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ ++ } ++ ++ @Override ++ public boolean add(short key) { ++ return backing.add(key); ++ } ++ ++ @Override ++ public boolean contains(short key) { ++ return backing.contains(key); ++ } ++ ++ @Override ++ public short[] toShortArray() { ++ return new short[0]; ++ } ++ ++ @Override ++ public short[] toArray(short[] a) { ++ return new short[0]; ++ } ++ ++ @Override ++ public boolean addAll(ShortCollection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(ShortCollection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(ShortCollection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(ShortCollection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public boolean remove(short k) { ++ return backing.remove(k); ++ } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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 implements ObjectSet { ++ ++ Set backing; ++ Function forward; ++ Function back; ++ ++ public ConvertingObjectSet(Set backing, Function forward, Function back) { ++ this.backing = backing; ++ this.forward = forward; ++ this.back = back; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean contains(Object o) { ++ try { ++ return backing.contains(back.apply((T) o)); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.stream().map(forward).toArray(); ++ } ++ ++ @Override ++ public R[] toArray(R[] a) { ++ return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); ++ } ++ ++ @Override ++ public boolean add(T e) { ++ return backing.add(back.apply(e)); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean remove(Object o) { ++ try { ++ return backing.remove(back.apply((T) o)); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean containsAll(Collection c) { ++ try { ++ return backing.containsAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c.stream().map(i -> back.apply(i)).collect(Collectors.toSet())); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean removeAll(Collection c) { ++ try { ++ return backing.removeAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean retainAll(Collection c) { ++ try { ++ return backing.retainAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ ++ } ++ ++ @Override ++ public ObjectIterator iterator() { ++ final Iterator backg = backing.iterator(); ++ return new ObjectIterator() { ++ ++ @Override ++ public boolean hasNext() { ++ return backg.hasNext(); ++ } ++ ++ @Override ++ public T next() { ++ return forward.apply(backg.next()); ++ } ++ ++ @Override ++ public void remove() { ++ backg.remove(); ++ } ++ }; ++ } ++ ++ ++ } ++ ++ public static class ConvertingObjectSetFast implements Long2ObjectMap.FastEntrySet { ++ ++ Set backing; ++ Function> forward; ++ Function, E> back; ++ ++ public ConvertingObjectSetFast(Set backing, ++ Function> forward, ++ Function, E> back) { ++ this.backing = backing; ++ this.forward = forward; ++ this.back = back; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean contains(Object o) { ++ try { ++ return backing.contains(back.apply((Long2ObjectMap.Entry)o)); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.stream().map(forward).toArray(); ++ } ++ ++ @Override ++ public R[] toArray(R[] a) { ++ return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean remove(Object o) { ++ try { ++ return backing.remove(back.apply((Long2ObjectMap.Entry)o)); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean containsAll(Collection c) { ++ try { ++ return backing.containsAll(c.stream() ++ .map(i -> back.apply((Long2ObjectMap.Entry) i)) ++ .collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean removeAll(Collection c) { ++ try { ++ return backing.removeAll(c.stream().map(i -> back ++ .apply((Long2ObjectMap.Entry) i)) ++ .collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean retainAll(Collection c) { ++ try { ++ return backing.retainAll(c.stream() ++ .map(i -> back.apply((Long2ObjectMap.Entry) i)) ++ .collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ ++ } ++ ++ @Override ++ public ObjectIterator> iterator() { ++ final Iterator backg = backing.iterator(); ++ return new ObjectIterator>() { ++ ++ @Override ++ public boolean hasNext() { ++ return backg.hasNext(); ++ } ++ ++ @Override ++ public Long2ObjectMap.Entry next() { ++ return forward.apply(backg.next()); ++ } ++ ++ @Override ++ public void remove() { ++ backg.remove(); ++ } ++ }; ++ } ++ ++ @Override ++ public boolean add(Long2ObjectMap.Entry e) { ++ return backing.add(back.apply(e)); ++ } ++ ++ @Override ++ public boolean addAll(Collection> c) { ++ return backing.addAll(c.stream().map(back).collect(Collectors.toList())); ++ } ++ ++ @Override ++ public ObjectIterator> fastIterator() { ++ return iterator(); ++ } ++ ++ ++ } ++ ++ private static Entry intEntryForwards(Map.Entry entry) { ++ return new Entry() { ++ ++ @Override ++ public T getValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public T setValue(T value) { ++ return entry.setValue(value); ++ } ++ ++ @Override ++ public int getIntKey() { ++ return entry.getKey(); ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (obj == entry) { ++ return true; ++ } ++ return super.equals(obj); ++ } ++ ++ @Override ++ public int hashCode() { ++ return entry.hashCode(); ++ } ++ }; ++ } ++ ++ private static Map.Entry intEntryBackwards(Entry entry) { ++ return entry; ++ } ++ ++ private static Long2ObjectMap.Entry longEntryForwards(Map.Entry entry) { ++ return new Long2ObjectMap.Entry() { ++ ++ @Override ++ public T getValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public T setValue(T value) { ++ return entry.setValue(value); ++ } ++ ++ @Override ++ public long getLongKey() { ++ return entry.getKey(); ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (obj == entry) { ++ return true; ++ } ++ return super.equals(obj); ++ } ++ ++ @Override ++ public int hashCode() { ++ return entry.hashCode(); ++ } ++ }; ++ } ++ ++ private static Map.Entry longEntryBackwards(Long2ObjectMap.Entry entry) { ++ return entry; ++ } ++ ++ private static Long2ByteMap.Entry longByteEntryForwards(Map.Entry entry) { ++ return new Long2ByteMap.Entry() { ++ ++ @Override ++ public Byte getValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public byte setValue(byte value) { ++ return entry.setValue(value); ++ } ++ ++ @Override ++ public byte getByteValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public long getLongKey() { ++ return entry.getKey(); ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (obj == entry) { ++ return true; ++ } ++ return super.equals(obj); ++ } ++ ++ @Override ++ public int hashCode() { ++ return entry.hashCode(); ++ } ++ ++ }; ++ } ++ ++ private static Map.Entry longByteEntryBackwards(Long2ByteMap.Entry entry) { ++ return entry; ++ } ++ ++ private static Long2LongMap.Entry longLongEntryForwards(Map.Entry entry) { ++ return new Long2LongMap.Entry() { ++ ++ @Override ++ public Long getValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public long setValue(long value) { ++ return entry.setValue(value); ++ } ++ ++ @Override ++ public long getLongValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public long getLongKey() { ++ return entry.getKey(); ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (obj == entry) { ++ return true; ++ } ++ return super.equals(obj); ++ } ++ ++ @Override ++ public int hashCode() { ++ return entry.hashCode(); ++ } ++ ++ }; ++ } ++ ++ private static Map.Entry longLongEntryBackwards(Long2LongMap.Entry entry) { ++ return entry; ++ } ++ ++ public static ObjectSet> entrySetIntWrap(Map map) { ++ return new ConvertingObjectSet, Entry>(map.entrySet(), FastUtilHackUtil::intEntryForwards, FastUtilHackUtil::intEntryBackwards); ++ } ++ ++ public static ObjectSet> entrySetLongWrap(Map map) { ++ return new ConvertingObjectSet, Long2ObjectMap.Entry>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); ++ } ++ ++ public static Long2ObjectMap.FastEntrySet entrySetLongWrapFast(Map map) { ++ return new ConvertingObjectSetFast, T>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); ++ } ++ ++ public static ObjectSet entrySetLongByteWrap(Map map) { ++ return new ConvertingObjectSet, Long2ByteMap.Entry>(map.entrySet(), FastUtilHackUtil::longByteEntryForwards, FastUtilHackUtil::longByteEntryBackwards); ++ } ++ ++ public static ObjectSet entrySetLongLongWrap(Map map) { ++ return new ConvertingObjectSet, Long2LongMap.Entry>(map.entrySet(), FastUtilHackUtil::longLongEntryForwards, FastUtilHackUtil::longLongEntryBackwards); ++ } ++ ++ ++ static class WrappingIntIterator implements IntIterator { ++ ++ Iterator backing; ++ ++ public WrappingIntIterator(Iterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public int nextInt() { ++ return backing.next(); ++ } ++ ++ @Override ++ public Integer next() { ++ return backing.next(); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ } ++ ++ static class WrappingLongIterator implements LongIterator { ++ ++ Iterator backing; ++ ++ public WrappingLongIterator(Iterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public long nextLong() { ++ return backing.next(); ++ } ++ ++ @Override ++ public Long next() { ++ return backing.next(); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ } ++ ++ static class WrappingShortIterator implements ShortIterator { ++ ++ Iterator backing; ++ ++ public WrappingShortIterator(Iterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public short nextShort() { ++ return backing.next(); ++ } ++ ++ @Override ++ public Short next() { ++ return backing.next(); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ } ++ ++ public static class WrappingIntSet implements IntSet { ++ ++ Set backing; ++ ++ public WrappingIntSet(Set backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean add(int key) { ++ return backing.add(key); ++ } ++ ++ @Override ++ public boolean contains(int key) { ++ return backing.contains(key); ++ } ++ ++ @Override ++ public int[] toIntArray() { ++ return backing.stream().mapToInt(i -> i).toArray(); ++ } ++ ++ @Override ++ public int[] toIntArray(int[] a) { ++ if (a.length >= size()) { ++ return null; ++ } else { ++ return toIntArray(); ++ } ++ } ++ ++ @Override ++ public int[] toArray(int[] a) { ++ return toIntArray(a); ++ } ++ ++ @Override ++ public boolean addAll(IntCollection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(IntCollection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(IntCollection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(IntCollection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public IntIterator iterator() { ++ return new WrappingIntIterator(backing.iterator()); ++ } ++ ++ @Override ++ public boolean remove(int k) { ++ return backing.remove(k); ++ } ++ ++ } ++ ++ public static LongSet wrapLongSet(Set longset) { ++ return new WrappingLongSet(longset); ++ } ++ ++ public static class WrappingLongSet implements LongSet { ++ ++ Set backing; ++ ++ public WrappingLongSet(Set backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean add(long key) { ++ return backing.add(key); ++ } ++ ++ @Override ++ public boolean contains(long key) { ++ return backing.contains(key); ++ } ++ ++ @Override ++ public long[] toLongArray() { ++ return backing.stream().mapToLong(i -> i).toArray(); ++ } ++ ++ @Override ++ public long[] toLongArray(long[] a) { ++ if (a.length >= size()) { ++ return null; ++ } else { ++ return toLongArray(); ++ } ++ } ++ ++ @Override ++ public long[] toArray(long[] a) { ++ return toLongArray(a); ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(LongCollection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(LongCollection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(LongCollection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public LongIterator iterator() { ++ return new WrappingLongIterator(backing.iterator()); ++ } ++ ++ @Override ++ public boolean remove(long k) { ++ return backing.remove(k); ++ } ++ ++ } ++ ++ public static LongSortedSet wrapLongSortedSet(Set longset) { ++ return new WrappingLongSortedSet(longset); ++ } ++ ++ public static class WrappingLongSortedSet implements LongSortedSet { ++ ++ Set backing; ++ ++ public WrappingLongSortedSet(Set backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean add(long key) { ++ return backing.add(key); ++ } ++ ++ @Override ++ public boolean contains(long key) { ++ return backing.contains(key); ++ } ++ ++ @Override ++ public long[] toLongArray() { ++ return backing.stream().mapToLong(i -> i).toArray(); ++ } ++ ++ @Override ++ public long[] toLongArray(long[] a) { ++ if (a.length >= size()) { ++ return null; ++ } else { ++ return toLongArray(); ++ } ++ } ++ ++ @Override ++ public long[] toArray(long[] a) { ++ return toLongArray(a); ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(LongCollection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(LongCollection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(LongCollection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public boolean remove(long k) { ++ return backing.remove(k); ++ } ++ ++ @Override ++ public LongBidirectionalIterator iterator(long fromElement) { ++ throw new UnsupportedOperationException(); ++ //return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); ++ } ++ ++ @Override ++ public LongBidirectionalIterator iterator() { ++ return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); ++ } ++ ++ @Override ++ public LongSortedSet subSet(long fromElement, long toElement) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongSortedSet headSet(long toElement) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongSortedSet tailSet(long fromElement) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongComparator comparator() { ++ return null; ++ } ++ ++ @Override ++ public long firstLong() { ++ return backing.stream().findAny().get(); ++ } ++ ++ @Override ++ public long lastLong() { ++ return backing.stream().findAny().get(); ++ } ++ ++ } ++ ++ public static IntSet wrapIntSet(Set intset) { ++ return new WrappingIntSet(intset); ++ } ++ ++ public static class WrappingObjectCollection implements ObjectCollection { ++ ++ Collection backing; ++ ++ public WrappingObjectCollection(Collection backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(Object o) { ++ return backing.contains(o); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean add(V e) { ++ return backing.add(e); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ return backing.remove(o); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public ObjectIterator iterator() { ++ return FastUtilHackUtil.itrWrap(backing); ++ } ++ ++ } ++ ++ public static ObjectCollection wrap(Collection c) { ++ return new WrappingObjectCollection(c); ++ } ++ ++ public static class WrappingByteCollection implements ByteCollection { ++ ++ Collection backing; ++ ++ public WrappingByteCollection(Collection backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(byte o) { ++ return backing.contains(o); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean add(byte e) { ++ return backing.add(e); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ return backing.remove(o); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public ByteIterator iterator() { ++ return FastUtilHackUtil.itrByteWrap(backing); ++ } ++ ++ @Override ++ public boolean rem(byte key) { ++ return this.remove(key); ++ } ++ ++ @Override ++ public byte[] toByteArray() { ++ return null; ++ } ++ ++ @Override ++ public byte[] toByteArray(byte[] a) { ++ return toArray(a); ++ } ++ ++ @Override ++ public byte[] toArray(byte[] a) { ++ return ArrayUtils.toPrimitive(backing.toArray(new Byte[0])); ++ } ++ ++ @Override ++ public boolean addAll(ByteCollection c) { ++ return addAll((Collection) c); ++ } ++ ++ @Override ++ public boolean containsAll(ByteCollection c) { ++ return containsAll((Collection) c); ++ } ++ ++ @Override ++ public boolean removeAll(ByteCollection c) { ++ return removeAll((Collection) c); ++ } ++ ++ @Override ++ public boolean retainAll(ByteCollection c) { ++ return retainAll((Collection) c); ++ } ++ ++ } ++ ++ public static ByteCollection wrapBytes(Collection c) { ++ return new WrappingByteCollection(c); ++ } ++ ++ public static class WrappingIntCollection implements IntCollection { ++ ++ Collection backing; ++ ++ public WrappingIntCollection(Collection backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(int o) { ++ return backing.contains(o); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean add(int e) { ++ return backing.add(e); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ return backing.remove(o); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public IntIterator iterator() { ++ return FastUtilHackUtil.itrIntWrap(backing); ++ } ++ ++ @Override ++ public boolean rem(int key) { ++ return this.remove(key); ++ } ++ ++ @Override ++ public int[] toIntArray() { ++ return null; ++ } ++ ++ @Override ++ public int[] toIntArray(int[] a) { ++ return toArray(a); ++ } ++ ++ @Override ++ public int[] toArray(int[] a) { ++ return ArrayUtils.toPrimitive(backing.toArray(new Integer[0])); ++ } ++ ++ @Override ++ public boolean addAll(IntCollection c) { ++ return addAll((Collection) c); ++ } ++ ++ @Override ++ public boolean containsAll(IntCollection c) { ++ return containsAll((Collection) c); ++ } ++ ++ @Override ++ public boolean removeAll(IntCollection c) { ++ return removeAll((Collection) c); ++ } ++ ++ @Override ++ public boolean retainAll(IntCollection c) { ++ return retainAll((Collection) c); ++ } ++ ++ } ++ ++ public static IntCollection wrapInts(Collection c) { ++ return new WrappingIntCollection(c); ++ } ++ ++ public static class WrappingLongCollection implements LongCollection { ++ ++ Collection backing; ++ ++ public WrappingLongCollection(Collection backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(long o) { ++ return backing.contains(o); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean add(long e) { ++ return backing.add(e); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ return backing.remove(o); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public LongIterator iterator() { ++ return FastUtilHackUtil.itrLongWrap(backing); ++ } ++ ++ @Override ++ public boolean rem(long key) { ++ return this.remove(key); ++ } ++ ++ @Override ++ public long[] toLongArray() { ++ return null; ++ } ++ ++ @Override ++ public long[] toLongArray(long[] a) { ++ return toArray(a); ++ } ++ ++ @Override ++ public long[] toArray(long[] a) { ++ return ArrayUtils.toPrimitive(backing.toArray(new Long[0])); ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return addAll((Collection) c); ++ } ++ ++ @Override ++ public boolean containsAll(LongCollection c) { ++ return containsAll((Collection) c); ++ } ++ ++ @Override ++ public boolean removeAll(LongCollection c) { ++ return removeAll((Collection) c); ++ } ++ ++ @Override ++ public boolean retainAll(LongCollection c) { ++ return retainAll((Collection) c); ++ } ++ ++ } ++ ++ public static LongCollection wrapLongs(Collection c) { ++ return new WrappingLongCollection(c); ++ } ++ ++ ++ public static class WrappingLongListIterator implements LongListIterator { ++ ++ ListIterator backing; ++ ++ public WrappingLongListIterator(ListIterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public long previousLong() { ++ return backing.previous(); ++ } ++ ++ @Override ++ public long nextLong() { ++ return backing.next(); ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public boolean hasPrevious() { ++ return backing.hasPrevious(); ++ } ++ ++ @Override ++ public int nextIndex() { ++ return backing.nextIndex(); ++ } ++ ++ @Override ++ public int previousIndex() { ++ return backing.previousIndex(); ++ } ++ ++ @Override ++ public void add(long k) { ++ backing.add(k); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ @Override ++ public void set(long k) { ++ backing.set(k); ++ } ++ } ++ ++ public static class SlimWrappingLongListIterator implements LongListIterator { ++ ++ Iterator backing; ++ ++ public SlimWrappingLongListIterator(Iterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public long previousLong() { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public long nextLong() { ++ return backing.next(); ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public boolean hasPrevious() { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public int nextIndex() { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public int previousIndex() { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public void add(long k) { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ @Override ++ public void set(long k) { ++ throw new IllegalStateException(); ++ } ++ } ++ ++ public static LongListIterator wrap(ListIterator c) { ++ return new WrappingLongListIterator(c); ++ } ++ ++ public static LongListIterator wrap(Iterator c) { ++ return new SlimWrappingLongListIterator(c); ++ } ++ ++ public static class WrappingByteIterator implements ByteIterator { ++ ++ Iterator parent; ++ ++ public WrappingByteIterator(Iterator parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return parent.hasNext(); ++ } ++ ++ @Override ++ public Byte next() { ++ return parent.next(); ++ } ++ ++ @Override ++ public void remove() { ++ parent.remove(); ++ } ++ ++ @Override ++ public byte nextByte() { ++ return next(); ++ } ++ ++ } ++ ++ public static ByteIterator itrByteWrap(Iterator backing) { ++ return new WrappingByteIterator(backing); ++ } ++ ++ public static ByteIterator itrByteWrap(Iterable backing) { ++ return new WrappingByteIterator(backing.iterator()); ++ } ++ ++ public static IntIterator itrIntWrap(Iterator backing) { ++ return new WrappingIntIterator(backing); ++ } ++ ++ public static IntIterator itrIntWrap(Iterable backing) { ++ return new WrappingIntIterator(backing.iterator()); ++ } ++ ++ public static LongIterator itrLongWrap(Iterator backing) { ++ return new WrappingLongIterator(backing); ++ } ++ ++ public static LongIterator itrLongWrap(Iterable backing) { ++ return new WrappingLongIterator(backing.iterator()); ++ } ++ ++ public static ShortIterator itrShortWrap(Iterator backing) { ++ return new WrappingShortIterator(backing); ++ } ++ ++ public static ShortIterator itrShortWrap(Iterable backing) { ++ return new WrappingShortIterator(backing.iterator()); ++ } ++ ++ public static class WrapperObjectIterator implements ObjectIterator { ++ ++ Iterator parent; ++ ++ public WrapperObjectIterator(Iterator parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return parent.hasNext(); ++ } ++ ++ @Override ++ public T next() { ++ return parent.next(); ++ } ++ ++ @Override ++ public void remove() { ++ parent.remove(); ++ } ++ ++ } ++ ++ public static class IntWrapperEntry implements Entry { ++ ++ Map.Entry parent; ++ ++ public IntWrapperEntry(Map.Entry parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public T getValue() { ++ return parent.getValue(); ++ } ++ ++ @Override ++ public T setValue(T value) { ++ return parent.setValue(value); ++ } ++ ++ @Override ++ public int getIntKey() { ++ return parent.getKey(); ++ } ++ ++ @Override ++ public Integer getKey() { ++ return parent.getKey(); ++ } ++ ++ } ++ ++ public static class Long2IntWrapperEntry implements Long2IntMap.Entry { ++ ++ Map.Entry parent; ++ ++ public Long2IntWrapperEntry(Map.Entry parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public long getLongKey() { ++ return parent.getKey(); ++ } ++ ++ @Override ++ public int getIntValue() { ++ return parent.getValue(); ++ } ++ ++ @Override ++ public int setValue(int value) { ++ return parent.setValue(value); ++ } ++ ++ ++ } ++ ++ public static class WrapperIntEntryObjectIterator implements ObjectIterator> { ++ ++ Iterator> parent; ++ ++ public WrapperIntEntryObjectIterator(Iterator> parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return parent.hasNext(); ++ } ++ ++ @Override ++ public Entry next() { ++ Map.Entry val = parent.next(); ++ if (val == null) return null; ++ return new IntWrapperEntry(val); ++ } ++ ++ @Override ++ public void remove() { ++ parent.remove(); ++ } ++ ++ } ++ ++ public static ObjectIterator> intMapItrFake(Map in) { ++ return new WrapperIntEntryObjectIterator(in.entrySet().iterator()); ++ } ++ ++ public static ObjectIterator itrWrap(Iterator in) { ++ return new WrapperObjectIterator(in); ++ } ++ ++ public static ObjectIterator itrWrap(Iterable in) { ++ return new WrapperObjectIterator(in.iterator()); ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bb6c592fd34423fdd910feae83a058d288da537a +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java +@@ -0,0 +1,93 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ++import it.unimi.dsi.fastutil.ints.IntSet; ++import it.unimi.dsi.fastutil.objects.ObjectCollection; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++import org.apache.commons.lang3.NotImplementedException; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class Int2ObjectConcurrentHashMap implements Int2ObjectMap { ++ ++ Map backing; ++ ++ public Int2ObjectConcurrentHashMap() { ++ backing = new ConcurrentHashMap(); ++ } ++ ++ @Override ++ public V get(int key) { ++ return backing.get(key); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean containsValue(Object value) { ++ return backing.containsValue(value); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(V rv) { ++ throw new NotImplementedException("MCMT - Not implemented"); ++ } ++ ++ @Override ++ public V defaultReturnValue() { ++ return null; ++ } ++ ++ @Override ++ public ObjectSet> int2ObjectEntrySet() { ++ return FastUtilHackUtil.entrySetIntWrap(backing); ++ } ++ ++ ++ @Override ++ public IntSet keySet() { ++ return FastUtilHackUtil.wrapIntSet(backing.keySet()); ++ } ++ ++ @Override ++ public ObjectCollection values() { ++ return FastUtilHackUtil.wrap(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(int key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public V put(int key, V value) { ++ return backing.put(key, value); ++ } ++ ++ @Override ++ public V put(Integer key, V value) { ++ return backing.put(key, value); ++ } ++ ++ @Override ++ public V remove(int key) { ++ return backing.remove(key); ++ } ++ ++ @Override ++ public void clear() { backing.clear(); } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e0fab16860e1be817fd10dbec684f295f2e291dd +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java +@@ -0,0 +1,99 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.bytes.ByteCollection; ++import it.unimi.dsi.fastutil.longs.Long2ByteMap; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class Long2ByteConcurrentHashMap implements Long2ByteMap { ++ ++ Map backing; ++ byte defaultReturn = 0; ++ byte nullKey = 0; ++ ++ public Long2ByteConcurrentHashMap() { ++ backing = new ConcurrentHashMap<>(); ++ } ++ ++ public Long2ByteConcurrentHashMap(int initialCapacity, float loadFactor) { ++ backing = new ConcurrentHashMap<>(initialCapacity, loadFactor); ++ } ++ ++ @Override ++ public byte get(long key) { ++ Byte out = backing.get(key); ++ return out == null ? defaultReturn : out; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean containsValue(byte value) { ++ return backing.containsValue(value); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(byte rv) { ++ defaultReturn = rv; ++ } ++ ++ @Override ++ public byte defaultReturnValue() { ++ return defaultReturn; ++ } ++ ++ @Override ++ public ObjectSet long2ByteEntrySet() { ++ return FastUtilHackUtil.entrySetLongByteWrap(backing); ++ } ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public ByteCollection values() { ++ return FastUtilHackUtil.wrapBytes(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public byte put(long key, byte value) { ++ return put((Long) key, (Byte) value); ++ } ++ ++ @Override ++ public Byte put(Long key, Byte value) { ++ Byte out = backing.put(key, value); ++ return out == null ? Byte.valueOf(defaultReturn) : out; ++ } ++ ++ @Override ++ public byte remove(long key) { ++ Byte out = backing.remove(key); ++ return out == null ? defaultReturn : out; ++ } ++ ++ ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..261f06a88021a95b6a0500444665547aeb4ae2c1 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java +@@ -0,0 +1,74 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.ints.IntCollection; ++import it.unimi.dsi.fastutil.longs.Long2IntMap; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class Long2IntConcurrentHashMap implements Long2IntMap { ++ ++ public Map backing = new ConcurrentHashMap(); ++ int defaultRV = 0; ++ ++ @Override ++ public int get(long key) { ++ if (backing.containsKey(key)) { ++ return backing.get(key); ++ } else return defaultRV; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(int rv) { ++ defaultRV = rv; ++ } ++ ++ @Override ++ public int defaultReturnValue() { ++ return defaultRV; ++ } ++ ++ @Override ++ public ObjectSet long2IntEntrySet() { ++ return null; ++ } ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public IntCollection values() { ++ return FastUtilHackUtil.wrapInts(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public boolean containsValue(int value) { ++ return backing.containsValue(value); ++ } ++ ++ ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..dc2342486318721d399c7c60a0a859befb4d1c9f +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java +@@ -0,0 +1,375 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.Hash; ++import it.unimi.dsi.fastutil.ints.IntCollection; ++import it.unimi.dsi.fastutil.longs.*; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++@SuppressWarnings("deprecation") ++public class Long2IntConcurrentNonLinkedOpenMap extends Long2IntLinkedOpenHashMap { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -2082212127278131631L; ++ ++ public Map backing = new ConcurrentHashMap(); ++ ++ public Long2IntConcurrentNonLinkedOpenMap(final int expected, final float f) { ++ ++ } ++ ++ /** ++ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. ++ * ++ * @param expected the expected number of elements in the hash map. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final int expected) { ++ } ++ ++ /** ++ * Creates a new hash map with initial expected ++ * {@link Hash#DEFAULT_INITIAL_SIZE} entries and ++ * {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap() { ++ } ++ ++ /** ++ * Creates a new hash map copying a given one. ++ * ++ * @param m a {@link Map} to be copied into the new hash map. ++ * @param f the load factor. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final Map m, final float f) { ++ putAll(m); ++ } ++ ++ /** ++ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor ++ * copying a given one. ++ * ++ * @param m a {@link Map} to be copied into the new hash map. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final Map m) { ++ this(m, DEFAULT_LOAD_FACTOR); ++ } ++ ++ /** ++ * Creates a new hash map copying a given type-specific one. ++ * ++ * @param m a type-specific map to be copied into the new hash map. ++ * @param f the load factor. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m, final float f) { ++ this(m.size(), f); ++ putAll(m); ++ } ++ ++ /** ++ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor ++ * copying a given type-specific one. ++ * ++ * @param m a type-specific map to be copied into the new hash map. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m) { ++ this(m, DEFAULT_LOAD_FACTOR); ++ } ++ ++ /** ++ * Creates a new hash map using the elements of two parallel arrays. ++ * ++ * @param k the array of keys of the new hash map. ++ * @param v the array of corresponding values in the new hash map. ++ * @param f the load factor. ++ * @throws IllegalArgumentException if {@code k} and {@code v} have different ++ * lengths. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v, final float f) { ++ if (k.length != v.length) ++ throw new IllegalArgumentException( ++ "The key array and the value array have different lengths (" + k.length + " and " + v.length + ")"); ++ for (int i = 0; i < k.length; i++) ++ this.put(k[i], v[i]); ++ } ++ ++ /** ++ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor ++ * using the elements of two parallel arrays. ++ * ++ * @param k the array of keys of the new hash map. ++ * @param v the array of corresponding values in the new hash map. ++ * @throws IllegalArgumentException if {@code k} and {@code v} have different ++ * lengths. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v) { ++ this(k, v, DEFAULT_LOAD_FACTOR); ++ } ++ ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ public int put(final long k, final int v) { ++ Integer out = backing.put(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int addTo(final long k, final int incr) { ++ Integer out = backing.put(k, this.get(k)+incr); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int remove(final long k) { ++ Integer out = backing.remove(k); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int removeFirstInt() { ++ Integer out = this.remove(backing.keySet().stream().findAny().get()); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int removeLastInt() { ++ Integer out = this.remove(backing.keySet().stream().findAny().get()); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public int getAndMoveToFirst(final long k) { ++ Integer out = backing.get(k); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int getAndMoveToLast(final long k) { ++ Integer out = backing.get(k); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int putAndMoveToFirst(final long k, final int v) { ++ Integer out = backing.put(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int putAndMoveToLast(final long k, final int v) { ++ Integer out = backing.put(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int get(final long k) { ++ Integer out = backing.get(k); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public boolean containsKey(final long k) { ++ return backing.containsKey(k); ++ } ++ ++ public boolean containsValue(final int v) { ++ return backing.containsValue(v); ++ } ++ ++ public int getOrDefault(final long k, final int defaultValue) { ++ Integer out = backing.getOrDefault(k, defaultValue); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int putIfAbsent(final long k, final int v) { ++ Integer out = backing.putIfAbsent(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public boolean remove(final long k, final int v) { ++ return backing.remove(k, v); ++ } ++ ++ ++ public boolean replace(final long k, final int oldValue, final int v) { ++ return backing.replace(k, oldValue, v); ++ } ++ ++ ++ public int replace(final long k, final int v) { ++ Integer out = backing.replace(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public int computeIfAbsent(final long k, final java.util.function.LongToIntFunction mappingFunction) { ++ Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.applyAsInt(l)); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public int computeIfAbsentNullable(final long k, ++ final java.util.function.LongFunction mappingFunction) { ++ Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.apply(l)); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public int computeIfPresent(final long k, ++ final java.util.function.BiFunction remappingFunction) { ++ if (this.containsKey(k)) { ++ Integer out = backing.put(k, remappingFunction.apply(k, backing.get(k))); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ return defaultReturnValue(); ++ ++ } ++ ++ @Override ++ public int compute(final long k, ++ final java.util.function.BiFunction remappingFunction) { ++ Integer out = backing.compute(k, remappingFunction); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ @Override ++ public int merge(final long k, final int v, ++ final java.util.function.BiFunction remappingFunction) { ++ Integer out = backing.merge(k, v, remappingFunction); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public long firstLongKey() { ++ return backing.keySet().stream().findAny().get(); ++ } ++ ++ @Override ++ public long lastLongKey() { ++ return backing.keySet().stream().findAny().get(); ++ } ++ ++ @Override ++ public Long2IntSortedMap tailMap(long from) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public Long2IntSortedMap headMap(long to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public Long2IntSortedMap subMap(long from, long to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongComparator comparator() { ++ return null; ++ } ++ ++ ++ @Override ++ public FastSortedEntrySet long2IntEntrySet() { ++ //TODO implement ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongSortedSet keySet() { ++ return FastUtilHackUtil.wrapLongSortedSet(backing.keySet()); ++ } ++ ++ ++ @Override ++ public IntCollection values() { ++ return FastUtilHackUtil.wrapInts(backing.values()); ++ } ++ ++ public boolean trim() { ++ return true; ++ } ++ ++ public boolean trim(final int n) { ++ return true; ++ } ++ ++ ++ @Override ++ public Long2IntConcurrentNonLinkedOpenMap clone() { ++ return new Long2IntConcurrentNonLinkedOpenMap(backing); ++ } ++ ++ @Override ++ public int hashCode() { ++ return backing.hashCode(); ++ } ++ ++ ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3205d30a03f99caf7dfa05237b2bc31182b2db20 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java +@@ -0,0 +1,97 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.longs.Long2LongMap; ++import it.unimi.dsi.fastutil.longs.LongCollection; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++ ++public class Long2LongConcurrentHashMap implements Long2LongMap { ++ ++ public Map backing = new ConcurrentHashMap(); ++ long defaultRV = 0; ++ ++ public Long2LongConcurrentHashMap(long defaultRV) { ++ this.defaultRV = defaultRV; ++ } ++ ++ @Override ++ public long get(long key) { ++ if (backing.containsKey(key)) { ++ return backing.get(key); ++ } else return defaultRV; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public long put(final long key, final long val) { ++ backing.put(key,val); ++ return val; ++ } ++ ++ @Override ++ public Long put(final Long key, final Long val) { ++ backing.put(key,val); ++ return val; ++ } ++ ++ @Override ++ public long remove(final long key) { ++ return backing.remove(key); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(long rv) { ++ defaultRV = rv; ++ } ++ ++ @Override ++ public long defaultReturnValue() { ++ return defaultRV; ++ } ++ ++ @Override ++ public ObjectSet long2LongEntrySet() { ++ return FastUtilHackUtil.entrySetLongLongWrap(backing); ++ } ++ ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public LongCollection values() { ++ return FastUtilHackUtil.wrapLongs(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public boolean containsValue(long value) { ++ return backing.containsValue(value); ++ } ++ ++ ++} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a5ed71564d4c9a986f77cbc0397130aa38f97a91 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java +@@ -0,0 +1,93 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.longs.Long2ObjectMap; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectCollection; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class Long2ObjectConcurrentHashMap implements Long2ObjectMap { ++ ++ Map backing; ++ V defaultReturn = null; ++ ++ public Long2ObjectConcurrentHashMap() { ++ backing = new ConcurrentHashMap(); ++ } ++ ++ @Override ++ public V get(long key) { ++ V out = backing.get(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean containsValue(Object value) { ++ return backing.containsValue(value); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(V rv) { ++ defaultReturn = rv; ++ } ++ ++ @Override ++ public V defaultReturnValue() { ++ return defaultReturn; ++ } ++ ++ @Override ++ public ObjectSet> long2ObjectEntrySet() { ++ return FastUtilHackUtil.entrySetLongWrap(backing); ++ } ++ ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public ObjectCollection values() { ++ return FastUtilHackUtil.wrap(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public V put(long key, V value) { ++ return put((Long)key, value); ++ } ++ ++ @Override ++ public V put(Long key, V value) { ++ V out = backing.put(key, value); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); ++ } ++ ++ @Override ++ public V remove(long key) { ++ V out = backing.remove(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a7d6be048ab3b8bd38231fce16eca0ac78e24690 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java +@@ -0,0 +1,233 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.longs.Long2ObjectFunction; ++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectCollection; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++import java.util.function.Function; ++ ++public class Long2ObjectOpenConcurrentHashMap extends Long2ObjectOpenHashMap { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -121514116954680057L; ++ ++ Map backing; ++ V defaultReturn = null; ++ ++ public Long2ObjectOpenConcurrentHashMap() { ++ backing = new ConcurrentHashMap(); ++ } ++ ++ @Override ++ public V get(long key) { ++ V out = backing.get(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++ ++ @Override ++ public V get(Object key) { ++ V out = backing.get(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean containsValue(Object value) { ++ return backing.containsValue(value); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(V rv) { ++ defaultReturn = rv; ++ } ++ ++ @Override ++ public V defaultReturnValue() { ++ return defaultReturn; ++ } ++ ++ @Override ++ public FastEntrySet long2ObjectEntrySet() { ++ return FastUtilHackUtil.entrySetLongWrapFast(backing); ++ } ++ ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public ObjectCollection values() { ++ return FastUtilHackUtil.wrap(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public V put(long key, V value) { ++ return put((Long)key, value); ++ } ++ ++ @Override ++ public V put(Long key, V value) { ++ V out = backing.put(key, value); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); ++ } ++ ++ @Override ++ public V remove(long key) { ++ V out = backing.remove(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++ ++ @Override ++ public boolean trim() { return true; } ++ ++ @Override ++ public boolean trim(final int n) { return true; } ++ ++ @Override ++ public boolean replace(final long k, final V oldValue, final V v) { ++ return backing.replace(k, oldValue, v); ++ } ++ ++ @Override ++ public V replace(final long k, final V v) { ++ return backing.replace(k, v); ++ } ++ ++ @Override ++ public boolean replace(final Long k, final V oldValue, final V v) { ++ return backing.replace(k, oldValue, v); ++ } ++ ++ @Override ++ public V replace(final Long k, final V v) { ++ return backing.replace(k, v); ++ } ++ ++ @Override ++ public boolean remove(final long k, final Object v) { ++ return backing.remove(k, v); ++ } ++ ++ @Override ++ public V putIfAbsent(final long k, final V v) { ++ return backing.putIfAbsent(k, v); ++ } ++ ++ @Override ++ public V putIfAbsent(final Long k, final V v) { ++ return backing.putIfAbsent(k, v); ++ } ++ ++ @Override ++ public V merge(final long k, final V v, final java.util.function.BiFunction remappingFunction) { ++ return backing.merge(k, v, remappingFunction); ++ } ++ ++ @Override ++ public V merge(Long k, final V v, final java.util.function.BiFunction remappingFunction) { ++ return backing.merge(k, v, remappingFunction); ++ } ++ ++ @Override ++ public int hashCode() { ++ return backing.hashCode(); ++ } ++ ++ @Override ++ public V getOrDefault(final long k, final V defaultValue) { ++ return backing.getOrDefault(k, defaultValue); ++ } ++ ++ @Override ++ public V getOrDefault(Object k, final V defaultValue) { ++ return backing.getOrDefault(k, defaultValue); ++ } ++ ++ @Override ++ public V computeIfPresent(final long k, final java.util.function.BiFunction remappingFunction) { ++ return backing.computeIfPresent(k, remappingFunction); ++ } ++ ++ @Override ++ public V computeIfPresent(final Long k, final java.util.function.BiFunction remappingFunction) { ++ return backing.computeIfPresent(k, remappingFunction); ++ } ++ ++ @Override ++ public V computeIfAbsent(final long k, final java.util.function.LongFunction mappingFunction) { ++ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); ++ } ++ ++ public V computeIfAbsent(final Long k, final java.util.function.LongFunction mappingFunction) { ++ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); ++ } ++ ++ @Override ++ public V computeIfAbsentPartial(final long key, final Long2ObjectFunction mappingFunction) { ++ if (!mappingFunction.containsKey(key)) ++ return defaultReturn; ++ return backing.computeIfAbsent(key, (llong) -> mappingFunction.apply(llong)); ++ } ++ ++ @Override ++ public V compute(final long k, final java.util.function.BiFunction remappingFunction) { ++ return backing.compute(k, remappingFunction); ++ } ++ ++ @Override ++ public V compute(final Long k, final java.util.function.BiFunction remappingFunction) { ++ return backing.compute(k, remappingFunction); ++ } ++ ++ @Override ++ public Long2ObjectOpenHashMap clone() { ++ throw new IllegalArgumentException(); ++ } ++ ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public ObjectSet> entrySet() { ++ return new FastUtilHackUtil.ConvertingObjectSet, Map.Entry>(backing.entrySet(), Function.identity(), Function.identity()); ++ } ++ ++ @Override ++ public V remove(Object key) { ++ return backing.remove(key); ++ } ++ ++ @Override ++ public boolean remove(Object key, Object value) { ++ return backing.remove(key, value); ++ } ++ ++} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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 c) { ++ return super.addAll(c); ++ } ++ ++ @Override ++ public synchronized boolean addAndMoveToFirst(final long k) { ++ return super.addAndMoveToFirst(k); ++ } ++ ++ @Override ++ public synchronized boolean addAndMoveToLast(final long k) { ++ return super.addAndMoveToFirst(k); ++ } ++ ++ @Override ++ public synchronized void clear() { ++ super.clear(); ++ } ++ ++ @Override ++ public synchronized LongLinkedOpenHashSet clone() { ++ return new SyncLongLinkedOpenHashSet(this); ++ } ++ ++ @Override ++ public synchronized LongComparator comparator() { ++ return super.comparator(); ++ } ++ ++ @Override ++ public synchronized boolean contains(final long k) { ++ return super.contains(k); ++ } ++ ++ @Override ++ public synchronized long firstLong() { ++ return super.firstLong(); ++ } ++ ++ @Override ++ public synchronized int hashCode() { ++ return super.hashCode(); ++ } ++ ++ @Override ++ public synchronized LongSortedSet headSet(long to) { ++ return super.headSet(to); ++ } ++ ++ @Override ++ public synchronized boolean isEmpty() { ++ return super.isEmpty(); ++ } ++ ++ @Override ++ public synchronized LongListIterator iterator() { ++ return super.iterator(); ++ } ++ ++ @Override ++ public synchronized LongListIterator iterator(long from) { ++ return super.iterator(from); ++ } ++ ++ @Override ++ public synchronized long lastLong() { ++ return super.lastLong(); ++ } ++ ++ @Override ++ public synchronized boolean remove(final long k) { ++ return super.remove(k); ++ } ++ ++ @Override ++ public synchronized long removeFirstLong() { ++ return super.removeFirstLong(); ++ } ++ ++ @Override ++ public synchronized long removeLastLong() { ++ return super.removeLastLong(); ++ } ++ ++ @Override ++ public synchronized int size() { ++ return super.size(); ++ } ++ ++ @Override ++ public synchronized LongSortedSet subSet(long from, long to) { ++ return super.subSet(from, to); ++ } ++ ++ @Override ++ public synchronized LongSortedSet tailSet(long from) { ++ return super.tailSet(from); ++ } ++ ++ @Override ++ public synchronized boolean trim() { ++ return super.trim(); ++ } ++ ++ @Override ++ public synchronized boolean trim(final int n) { ++ return super.trim(n); ++ } ++} diff --git a/patches/api/0012-Hearse-I-am-an-idiot.patch b/patches/api/0012-Hearse-I-am-an-idiot.patch new file mode 100644 index 00000000..d0622388 --- /dev/null +++ b/patches/api/0012-Hearse-I-am-an-idiot.patch @@ -0,0 +1,4531 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 21:11:42 +0800 +Subject: [PATCH] Hearse: I am an idiot + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java +deleted file mode 100644 +index 3c29129dc02ddcfaad026d1f81e5da879a0d64cb..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java ++++ /dev/null +@@ -1,4 +0,0 @@ +-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 +deleted file mode 100644 +index 67dd5fe624fe4428d8907000cb23a33485fd6bd9..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java ++++ /dev/null +@@ -1,41 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised; +- +-import org.apache.logging.log4j.LogManager; +-import org.apache.logging.log4j.Logger; +- +-import java.util.*; +-import java.util.concurrent.ConcurrentHashMap; +-import java.util.concurrent.ConcurrentLinkedDeque; +-import java.util.concurrent.CopyOnWriteArrayList; +-import java.util.stream.Collector; +-import java.util.stream.Collectors; +- +-public class ConcurrentCollections { +- +- private static final Logger LOGGER = LogManager.getLogger(); +- +- public static Set newHashSet() { +- //LOGGER.info("Concurrent hash set created"); +- return Collections.newSetFromMap(new ConcurrentHashMap()); +- } +- +- public static Map newHashMap() { +- //LOGGER.info("Concurrent hash map created"); +- return new ConcurrentHashMap(); +- } +- +- public static List newLinkedList() { +- LOGGER.info("Concurrent \"linked\" list created"); +- return new CopyOnWriteArrayList(); +- } +- +- public static Collector> toList() { +- return Collectors.toCollection(CopyOnWriteArrayList::new); +- } +- +- public static Queue newArrayDeque() { +- LOGGER.info("Concurrent \"array\" deque created"); +- return new ConcurrentLinkedDeque(); +- } +- +-} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java +deleted file mode 100644 +index 22b9d217dc06caaf8fbec21f0e31aa1cd13144ee..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java ++++ /dev/null +@@ -1,945 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised; +- +-/* +- * From: http://www.java2s.com/Code/Java/Collections-Data-Structure/ConcurrentDoublyLinkedList.htm +- * +- * Written by Doug Lea with assistance from members of JCP JSR-166 +- * Expert Group and released to the public domain, as explained at +- * http://creativecommons.org/licenses/publicdomain +- * +- * Modified to actually implement List +- */ +- +-import 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 weakly consistent, returning +- * elements reflecting the state of the deque at some point at or since the +- * creation of the iterator. They do not throw +- * {@link ConcurrentModificationException}, and may proceed concurrently with +- * other operations. +- * +- *

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

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

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

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

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

+- * +- * If the deque fits in the specified array with room to spare (i.e., the array +- * has more elements than the deque), the element in the array immediately +- * following the end of the collection is set to null. This is useful in +- * determining the length of the deque only if the caller knows that the +- * deque does not contain any null elements. +- * +- * @param a the array into which the elements of the deque are to be stored, if +- * it is big enough; otherwise, a new array of the same runtime type is +- * allocated for this purpose. +- * @return an array containing the elements of the deque. +- * @throws ArrayStoreException if the runtime type of a is not a supertype of +- * the runtime type of every element in this deque. +- * @throws NullPointerException if the specified array is null. +- */ +- public T[] toArray(T[] a) { +- return toArrayList().toArray(a); +- } +- +- /** +- * Returns a weakly consistent iterator over the elements in this deque, in +- * first-to-last order. The next method returns elements reflecting the +- * state of the deque at some point at or since the creation of the iterator. +- * The method does not throw {@link ConcurrentModificationException}, +- * and may proceed concurrently with other operations. +- * +- * @return an iterator over the elements in this deque +- */ +- public Iterator iterator() { +- return new CLDIterator(); +- } +- +- final class CLDIterator implements Iterator { +- Node last; +- +- Node next = header.forward(); +- +- public boolean hasNext() { +- return next != null; +- } +- +- public E next() { +- Node l = last = next; +- if (l == null) +- throw new NoSuchElementException(); +- next = next.forward(); +- return l.element; +- } +- +- public void remove() { +- Node l = last; +- if (l == null) +- throw new IllegalStateException(); +- while (!l.delete() && !l.isDeleted()) +- ; +- } +- } +- +- @Override +- public boolean addAll(int index, Collection c) { +- throw new NotImplementedException("TODO"); +- } +- +- @Override +- public E get(int index) { +- Node current = header.successor(); +- if (current == null) { +- throw new IndexOutOfBoundsException(); +- } +- for (; index > 0; index --) { +- current = current.successor(); +- if (current == null) { +- throw new IndexOutOfBoundsException(); +- } +- } +- return current.element; +- } +- +- @Override +- public E set(int index, E element) { +- throw new NotImplementedException("INVALID"); +- } +- +- @Override +- public void add(int index, E element) { +- throw new NotImplementedException("INVALID"); +- } +- +- @Override +- public E remove(int index) { +- throw new NotImplementedException("INVALID"); +- } +- +- @Override +- public int indexOf(Object o) { +- throw new NotImplementedException("INVALID"); +- } +- +- @Override +- public int lastIndexOf(Object o) { +- throw new NotImplementedException("INVALID"); +- } +- +- @Override +- public ListIterator listIterator() { +- throw new NotImplementedException("INVALID"); +- } +- +- @Override +- public ListIterator listIterator(int index) { +- throw new NotImplementedException("INVALID"); +- } +- +- @Override +- public List subList(int fromIndex, int toIndex) { +- throw new NotImplementedException("INVALID"); +- } +- +-} +- +-/** +- * Linked Nodes. As a minor efficiency hack, this class opportunistically +- * inherits from AtomicReference, with the atomic ref used as the "next" link. +- * +- * Nodes are in doubly-linked lists. There are three kinds of special nodes, +- * distinguished by: * The list header has a null prev link * The list trailer +- * has a null next link * A deletion marker has a prev link pointing to itself. +- * All three kinds of special nodes have null element fields. +- * +- * Regular nodes have non-null element, next, and prev fields. To avoid visible +- * inconsistencies when deletions overlap element replacement, replacements are +- * done by replacing the node, not just setting the element. +- * +- * Nodes can be traversed by read-only ConcurrentLinkedDeque class operations +- * just by following raw next pointers, so long as they ignore any special nodes +- * seen along the way. (This is automated in method forward.) However, traversal +- * using prev pointers is not guaranteed to see all live nodes since a prev +- * pointer of a deleted node can become unrecoverably stale. +- */ +- +-class Node extends AtomicReference> { +- +- private static final long serialVersionUID = 6640557564507962862L; +- +- private volatile Node prev; +- +- final E element; +- +- /** Creates a node with given contents */ +- Node(E element, Node next, Node prev) { +- super(next); +- this.prev = prev; +- this.element = element; +- } +- +- /** Creates a marker node with given successor */ +- Node(Node next) { +- super(next); +- this.prev = this; +- this.element = null; +- } +- +- /** +- * Gets next link (which is actually the value held as atomic reference). +- */ +- private Node getNext() { +- return get(); +- } +- +- /** +- * Sets next link +- * +- * @param n the next node +- */ +- void setNext(Node n) { +- set(n); +- } +- +- /** +- * compareAndSet next link +- */ +- private boolean casNext(Node cmp, Node val) { +- return compareAndSet(cmp, val); +- } +- +- /** +- * Gets prev link +- */ +- private Node getPrev() { +- return prev; +- } +- +- /** +- * Sets prev link +- * +- * @param b the previous node +- */ +- void setPrev(Node b) { +- prev = b; +- } +- +- /** +- * Returns true if this is a header, trailer, or marker node +- */ +- boolean isSpecial() { +- return element == null; +- } +- +- /** +- * Returns true if this is a trailer node +- */ +- boolean isTrailer() { +- return getNext() == null; +- } +- +- /** +- * Returns true if this is a header node +- */ +- boolean isHeader() { +- return getPrev() == null; +- } +- +- /** +- * Returns true if this is a marker node +- */ +- boolean isMarker() { +- return getPrev() == this; +- } +- +- /** +- * Returns true if this node is followed by a marker, meaning that it is +- * deleted. +- * +- * @return true if this node is deleted +- */ +- boolean isDeleted() { +- Node f = getNext(); +- return f != null && f.isMarker(); +- } +- +- /** +- * Returns next node, ignoring deletion marker +- */ +- private Node nextNonmarker() { +- Node f = getNext(); +- return (f == null || !f.isMarker()) ? f : f.getNext(); +- } +- +- /** +- * Returns the next non-deleted node, swinging next pointer around any +- * encountered deleted nodes, and also patching up successor''s prev link to +- * point back to this. Returns null if this node is trailer so has no successor. +- * +- * @return successor, or null if no such +- */ +- Node successor() { +- Node f = nextNonmarker(); +- for (;;) { +- if (f == null) +- return null; +- if (!f.isDeleted()) { +- if (f.getPrev() != this && !isDeleted()) +- f.setPrev(this); // relink f's prev +- return f; +- } +- Node s = f.nextNonmarker(); +- if (f == getNext()) +- casNext(f, s); // unlink f +- f = s; +- } +- } +- +- /** +- * Returns the apparent predecessor of target by searching forward for it +- * starting at this node, patching up pointers while traversing. Used by +- * predecessor(). +- * +- * @return target's predecessor, or null if not found +- */ +- private Node findPredecessorOf(Node target) { +- Node n = this; +- for (;;) { +- Node f = n.successor(); +- if (f == target) +- return n; +- if (f == null) +- return null; +- n = f; +- } +- } +- +- /** +- * Returns the previous non-deleted node, patching up pointers as needed. +- * Returns null if this node is header so has no successor. May also return null +- * if this node is deleted, so doesn't have a distinct predecessor. +- * +- * @return predecessor or null if not found +- */ +- Node predecessor() { +- Node n = this; +- for (;;) { +- Node b = n.getPrev(); +- if (b == null) +- return n.findPredecessorOf(this); +- Node s = b.getNext(); +- if (s == this) +- return b; +- if (s == null || !s.isMarker()) { +- Node p = b.findPredecessorOf(this); +- if (p != null) +- return p; +- } +- n = b; +- } +- } +- +- /** +- * Returns the next node containing a nondeleted user element. Use for forward +- * list traversal. +- * +- * @return successor, or null if no such +- */ +- Node forward() { +- Node f = successor(); +- return (f == null || f.isSpecial()) ? null : f; +- } +- +- /** +- * Returns previous node containing a nondeleted user element, if possible. Use +- * for backward list traversal, but beware that if this method is called from a +- * deleted node, it might not be able to determine a usable predecessor. +- * +- * @return predecessor, or null if no such could be found +- */ +- Node back() { +- Node f = predecessor(); +- return (f == null || f.isSpecial()) ? null : f; +- } +- +- /** +- * Tries to insert a node holding element as successor, failing if this node is +- * deleted. +- * +- * @param element the element +- * @return the new node, or null on failure. +- */ +- Node append(E element) { +- for (;;) { +- Node f = getNext(); +- if (f == null || f.isMarker()) +- return null; +- Node x = new Node(element, f, this); +- if (casNext(f, x)) { +- f.setPrev(x); // optimistically link +- return x; +- } +- } +- } +- +- /** +- * Tries to insert a node holding element as predecessor, failing if no live +- * predecessor can be found to link to. +- * +- * @param element the element +- * @return the new node, or null on failure. +- */ +- Node prepend(E element) { +- for (;;) { +- Node b = predecessor(); +- if (b == null) +- return null; +- Node x = new Node(element, this, b); +- if (b.casNext(this, x)) { +- setPrev(x); // optimistically link +- return x; +- } +- } +- } +- +- /** +- * Tries to mark this node as deleted, failing if already deleted or if this +- * node is header or trailer +- * +- * @return true if successful +- */ +- boolean delete() { +- Node b = getPrev(); +- Node f = getNext(); +- if (b != null && f != null && !f.isMarker() && casNext(f, new Node(f))) { +- if (b.casNext(this, f)) +- f.setPrev(b); +- return true; +- } +- return false; +- } +- +- /** +- * Tries to insert a node holding element to replace this node. failing if +- * already deleted. +- * +- * @param newElement the new element +- * @return the new node, or null on failure. +- */ +- Node replace(E newElement) { +- for (;;) { +- Node b = getPrev(); +- Node f = getNext(); +- if (b == null || f == null || f.isMarker()) +- return null; +- Node x = new Node(newElement, f, b); +- if (casNext(f, new Node(x))) { +- b.successor(); // to relink b +- x.successor(); // to relink f +- return x; +- } +- } +- } +-} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java +deleted file mode 100644 +index 2bf97bd3e77fe4fec785b850524a870300ecd82c..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java ++++ /dev/null +@@ -1,237 +0,0 @@ +-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 backing; +- +- public ConcurrentLongLinkedOpenHashSet() { +- //backing = new ConcurrentLinkedDeque(); +- backing = new ConcurrentSkipListSet(); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final int initial) { +- //backing = new ConcurrentLinkedDeque(); +- backing = new ConcurrentSkipListSet(); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final int initial, final float dnc) { +- this(initial); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final LongCollection c) { +- this(c.size()); +- addAll(c); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final LongCollection c, final float f) { +- this(c.size(), f); +- addAll(c); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final LongIterator i, final float f) { +- this(16, f); +- while (i.hasNext()) +- add(i.nextLong()); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final LongIterator i) { +- this(i, -1); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final Iterator i, final float f) { +- this(LongIterators.asLongIterator(i), f); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final Iterator i) { +- this(LongIterators.asLongIterator(i)); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length, final float f) { +- this(length < 0 ? 0 : length, f); +- LongArrays.ensureOffsetLength(a, offset, length); +- for (int i = 0; i < length; i++) +- add(a[offset + i]); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length) { +- this(a, offset, length, DEFAULT_LOAD_FACTOR); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final long[] a, final float f) { +- this(a, 0, a.length, f); +- } +- +- public ConcurrentLongLinkedOpenHashSet(final long[] a) { +- this(a, -1); +- } +- +- @Override +- public boolean add(final long k) { +- boolean out = backing.add(k); +- /* +- if (!firstDef) { +- first = k; +- firstDef = true; +- } +- last = k; +- */ +- return out; +- } +- +- @Override +- public boolean addAll(LongCollection c) { +- return addAll((Collection) c); +- } +- +- @Override +- public boolean addAll(Collection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean addAndMoveToFirst(final long k) { +- boolean out = backing.add(k); +- //first = k; +- return out; +- } +- +- @Override +- public boolean addAndMoveToLast(final long k) { +- boolean out = backing.add(k); +- //last = k; +- return out; +- } +- +- @Override +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public LongLinkedOpenHashSet clone() { +- return new ConcurrentLongLinkedOpenHashSet(backing.iterator()); +- } +- +- @Override +- public LongComparator comparator() { +- return null; +- } +- +- @Override +- public boolean contains(final long k) { +- return backing.contains(k); +- } +- +- @Override +- public long firstLong() { +- /* +- if (backing.size() == 0) throw new NoSuchElementException(); +- return first; +- */ +- return backing.first(); +- } +- +- @Override +- public int hashCode() { +- return backing.hashCode(); +- } +- +- @Override +- public LongSortedSet headSet(long to) { +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public LongListIterator iterator() { +- return FastUtilHackUtil.wrap(backing.iterator()); +- } +- +- @Override +- public LongListIterator iterator(long from) { +- throw new IllegalStateException(); +- //return FastUtilHackUtil.wrap(backing.iterator()); +- } +- +- @Override +- public long lastLong() { +- /* +- if (backing.size() == 0) throw new NoSuchElementException(); +- return last; +- */ +- return backing.last(); +- } +- +- @Override +- public boolean remove(final long k) { +- /* +- if (k == first) { +- first = backing.iterator().next(); +- } +- if (k == last) { +- last = backing.iterator().next(); +- } +- */ +- return backing.remove(k); +- } +- +- @Override +- public long removeFirstLong() { +- long fl = this.firstLong(); +- this.remove(fl); +- //first = backing.iterator().next(); +- return fl; +- } +- +- @Override +- public long removeLastLong() { +- long fl = this.lastLong(); +- this.remove(fl); +- //last = backing.iterator().next(); +- return fl; +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public LongSortedSet subSet(long from, long to) { +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public LongSortedSet tailSet(long from) { +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public boolean trim() { +- return true; +- } +- +- @Override +- public boolean trim(final int n) { +- return true; +- } +-} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java +deleted file mode 100644 +index 93bd066ec2013e42a85fcf21344fe41f3ad69598..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java ++++ /dev/null +@@ -1,144 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised.fastutil; +- +-import it.unimi.dsi.fastutil.longs.*; +-import org.jetbrains.annotations.NotNull; +- +-import java.util.Collection; +-import java.util.concurrent.ConcurrentSkipListSet; +- +-public class ConcurrentLongSortedSet implements LongSortedSet { +- +- ConcurrentSkipListSet back = new ConcurrentSkipListSet<>(); +- +- @Override +- public LongBidirectionalIterator iterator(long fromElement) { +- return null; +- } +- +- @Override +- public int size() { +- return back.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return back.isEmpty(); +- } +- +- @Override +- public LongBidirectionalIterator iterator() { +- return null; +- } +- +- @NotNull +- @Override +- public Object[] toArray() { +- return back.toArray(); +- } +- +- @NotNull +- @Override +- public T[] toArray(@NotNull T[] ts) { +- return null; +- } +- +- @Override +- public boolean containsAll(@NotNull Collection collection) { +- return back.containsAll(collection); +- } +- +- @Override +- public boolean addAll(@NotNull Collection collection) { +- return back.addAll(collection); +- } +- +- @Override +- public boolean removeAll(@NotNull Collection collection) { +- return back.removeAll(collection); +- } +- +- @Override +- public boolean retainAll(@NotNull Collection collection) { +- return back.retainAll(collection); +- } +- +- @Override +- public void clear() { +- back.clear(); +- } +- +- @Override +- public boolean add(long key) { +- return back.add(key); +- } +- +- @Override +- public boolean contains(long key) { +- return back.contains(key); +- } +- +- @Override +- public long[] toLongArray() { +- return new long[0]; +- } +- +- @Override +- public long[] toArray(long[] a) { +- return new long[0]; +- } +- +- @Override +- public boolean addAll(LongCollection c) { +- return back.addAll(c); +- } +- +- @Override +- public boolean containsAll(LongCollection c) { +- return back.containsAll(c); +- } +- +- @Override +- public boolean removeAll(LongCollection c) { +- return back.removeAll(c); +- } +- +- @Override +- public boolean retainAll(LongCollection c) { +- return back.retainAll(c); +- } +- +- @Override +- public boolean remove(long k) { +- return back.remove(k); +- } +- +- @Override +- public LongSortedSet subSet(long fromElement, long toElement) { +- return new LongAVLTreeSet(back.subSet(fromElement,toElement)); +- } +- +- @Override +- public LongSortedSet headSet(long toElement) { +- return new LongAVLTreeSet(back.headSet(toElement)); +- } +- +- @Override +- public LongSortedSet tailSet(long fromElement) { +- return new LongAVLTreeSet(back.tailSet(fromElement)); +- } +- +- @Override +- public LongComparator comparator() { +- return null; +- } +- +- @Override +- public long firstLong() { +- return back.first(); +- } +- +- @Override +- public long lastLong() { +- return back.last(); +- } +-} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java +deleted file mode 100644 +index ff1a4f87356459d3bc990a77c3081932046da5b1..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java ++++ /dev/null +@@ -1,112 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised.fastutil; +- +-import it.unimi.dsi.fastutil.shorts.ShortCollection; +-import it.unimi.dsi.fastutil.shorts.ShortIterator; +-import it.unimi.dsi.fastutil.shorts.ShortSet; +-import org.jetbrains.annotations.NotNull; +- +-import java.util.Collection; +-import java.util.concurrent.ConcurrentHashMap; +- +-public class ConcurrentShortHashSet implements ShortSet { +- +- ConcurrentHashMap.KeySetView backing = ConcurrentHashMap.newKeySet(); +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public ShortIterator iterator() { +- return new FastUtilHackUtil.WrappingShortIterator(backing.iterator()); +- } +- +- @NotNull +- @Override +- public Object[] toArray() { +- return backing.toArray(); +- } +- +- @NotNull +- @Override +- public T[] toArray(@NotNull T[] ts) { +- return (T[]) backing.toArray(); +- } +- +- @Override +- public boolean containsAll(@NotNull Collection collection) { +- return backing.containsAll(collection); +- } +- +- @Override +- public boolean addAll(@NotNull Collection collection) { +- return backing.addAll(collection); +- } +- +- @Override +- public boolean removeAll(@NotNull Collection collection) { +- return backing.removeAll(collection); +- } +- +- @Override +- public boolean retainAll(@NotNull Collection collection) { +- return backing.retainAll(collection); +- } +- +- @Override +- public void clear() { +- backing.clear(); +- +- } +- +- @Override +- public boolean add(short key) { +- return backing.add(key); +- } +- +- @Override +- public boolean contains(short key) { +- return backing.contains(key); +- } +- +- @Override +- public short[] toShortArray() { +- return new short[0]; +- } +- +- @Override +- public short[] toArray(short[] a) { +- return new short[0]; +- } +- +- @Override +- public boolean addAll(ShortCollection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean containsAll(ShortCollection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean removeAll(ShortCollection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(ShortCollection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public boolean remove(short k) { +- return backing.remove(k); +- } +-} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java +deleted file mode 100644 +index a14ecb2ca64316fb85e6ecb65df50d98d337aff9..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java ++++ /dev/null +@@ -1,1685 +0,0 @@ +-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 implements ObjectSet { +- +- Set backing; +- Function forward; +- Function back; +- +- public ConvertingObjectSet(Set backing, Function forward, Function back) { +- this.backing = backing; +- this.forward = forward; +- this.back = back; +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean contains(Object o) { +- try { +- return backing.contains(back.apply((T) o)); +- } catch (ClassCastException cce) { +- return false; +- } +- } +- +- @Override +- public Object[] toArray() { +- return backing.stream().map(forward).toArray(); +- } +- +- @Override +- public R[] toArray(R[] a) { +- return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); +- } +- +- @Override +- public boolean add(T e) { +- return backing.add(back.apply(e)); +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean remove(Object o) { +- try { +- return backing.remove(back.apply((T) o)); +- } catch (ClassCastException cce) { +- return false; +- } +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean containsAll(Collection c) { +- try { +- return backing.containsAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); +- } catch (ClassCastException cce) { +- return false; +- } +- +- } +- +- @Override +- public boolean addAll(Collection c) { +- return backing.addAll(c.stream().map(i -> back.apply(i)).collect(Collectors.toSet())); +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean removeAll(Collection c) { +- try { +- return backing.removeAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); +- } catch (ClassCastException cce) { +- return false; +- } +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean retainAll(Collection c) { +- try { +- return backing.retainAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); +- } catch (ClassCastException cce) { +- return false; +- } +- } +- +- @Override +- public void clear() { +- backing.clear(); +- +- } +- +- @Override +- public ObjectIterator iterator() { +- final Iterator backg = backing.iterator(); +- return new ObjectIterator() { +- +- @Override +- public boolean hasNext() { +- return backg.hasNext(); +- } +- +- @Override +- public T next() { +- return forward.apply(backg.next()); +- } +- +- @Override +- public void remove() { +- backg.remove(); +- } +- }; +- } +- +- +- } +- +- public static class ConvertingObjectSetFast implements Long2ObjectMap.FastEntrySet { +- +- Set backing; +- Function> forward; +- Function, E> back; +- +- public ConvertingObjectSetFast(Set backing, +- Function> forward, +- Function, E> back) { +- this.backing = backing; +- this.forward = forward; +- this.back = back; +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean contains(Object o) { +- try { +- return backing.contains(back.apply((Long2ObjectMap.Entry)o)); +- } catch (ClassCastException cce) { +- return false; +- } +- } +- +- @Override +- public Object[] toArray() { +- return backing.stream().map(forward).toArray(); +- } +- +- @Override +- public R[] toArray(R[] a) { +- return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean remove(Object o) { +- try { +- return backing.remove(back.apply((Long2ObjectMap.Entry)o)); +- } catch (ClassCastException cce) { +- return false; +- } +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean containsAll(Collection c) { +- try { +- return backing.containsAll(c.stream() +- .map(i -> back.apply((Long2ObjectMap.Entry) i)) +- .collect(Collectors.toSet())); +- } catch (ClassCastException cce) { +- return false; +- } +- +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean removeAll(Collection c) { +- try { +- return backing.removeAll(c.stream().map(i -> back +- .apply((Long2ObjectMap.Entry) i)) +- .collect(Collectors.toSet())); +- } catch (ClassCastException cce) { +- return false; +- } +- } +- +- @SuppressWarnings("unchecked") +- @Override +- public boolean retainAll(Collection c) { +- try { +- return backing.retainAll(c.stream() +- .map(i -> back.apply((Long2ObjectMap.Entry) i)) +- .collect(Collectors.toSet())); +- } catch (ClassCastException cce) { +- return false; +- } +- } +- +- @Override +- public void clear() { +- backing.clear(); +- +- } +- +- @Override +- public ObjectIterator> iterator() { +- final Iterator backg = backing.iterator(); +- return new ObjectIterator>() { +- +- @Override +- public boolean hasNext() { +- return backg.hasNext(); +- } +- +- @Override +- public Long2ObjectMap.Entry next() { +- return forward.apply(backg.next()); +- } +- +- @Override +- public void remove() { +- backg.remove(); +- } +- }; +- } +- +- @Override +- public boolean add(Long2ObjectMap.Entry e) { +- return backing.add(back.apply(e)); +- } +- +- @Override +- public boolean addAll(Collection> c) { +- return backing.addAll(c.stream().map(back).collect(Collectors.toList())); +- } +- +- @Override +- public ObjectIterator> fastIterator() { +- return iterator(); +- } +- +- +- } +- +- private static Entry intEntryForwards(Map.Entry entry) { +- return new Entry() { +- +- @Override +- public T getValue() { +- return entry.getValue(); +- } +- +- @Override +- public T setValue(T value) { +- return entry.setValue(value); +- } +- +- @Override +- public int getIntKey() { +- return entry.getKey(); +- } +- +- @Override +- public boolean equals(Object obj) { +- if (obj == entry) { +- return true; +- } +- return super.equals(obj); +- } +- +- @Override +- public int hashCode() { +- return entry.hashCode(); +- } +- }; +- } +- +- private static Map.Entry intEntryBackwards(Entry entry) { +- return entry; +- } +- +- private static Long2ObjectMap.Entry longEntryForwards(Map.Entry entry) { +- return new Long2ObjectMap.Entry() { +- +- @Override +- public T getValue() { +- return entry.getValue(); +- } +- +- @Override +- public T setValue(T value) { +- return entry.setValue(value); +- } +- +- @Override +- public long getLongKey() { +- return entry.getKey(); +- } +- +- @Override +- public boolean equals(Object obj) { +- if (obj == entry) { +- return true; +- } +- return super.equals(obj); +- } +- +- @Override +- public int hashCode() { +- return entry.hashCode(); +- } +- }; +- } +- +- private static Map.Entry longEntryBackwards(Long2ObjectMap.Entry entry) { +- return entry; +- } +- +- private static Long2ByteMap.Entry longByteEntryForwards(Map.Entry entry) { +- return new Long2ByteMap.Entry() { +- +- @Override +- public Byte getValue() { +- return entry.getValue(); +- } +- +- @Override +- public byte setValue(byte value) { +- return entry.setValue(value); +- } +- +- @Override +- public byte getByteValue() { +- return entry.getValue(); +- } +- +- @Override +- public long getLongKey() { +- return entry.getKey(); +- } +- +- @Override +- public boolean equals(Object obj) { +- if (obj == entry) { +- return true; +- } +- return super.equals(obj); +- } +- +- @Override +- public int hashCode() { +- return entry.hashCode(); +- } +- +- }; +- } +- +- private static Map.Entry longByteEntryBackwards(Long2ByteMap.Entry entry) { +- return entry; +- } +- +- private static Long2LongMap.Entry longLongEntryForwards(Map.Entry entry) { +- return new Long2LongMap.Entry() { +- +- @Override +- public Long getValue() { +- return entry.getValue(); +- } +- +- @Override +- public long setValue(long value) { +- return entry.setValue(value); +- } +- +- @Override +- public long getLongValue() { +- return entry.getValue(); +- } +- +- @Override +- public long getLongKey() { +- return entry.getKey(); +- } +- +- @Override +- public boolean equals(Object obj) { +- if (obj == entry) { +- return true; +- } +- return super.equals(obj); +- } +- +- @Override +- public int hashCode() { +- return entry.hashCode(); +- } +- +- }; +- } +- +- private static Map.Entry longLongEntryBackwards(Long2LongMap.Entry entry) { +- return entry; +- } +- +- public static ObjectSet> entrySetIntWrap(Map map) { +- return new ConvertingObjectSet, Entry>(map.entrySet(), FastUtilHackUtil::intEntryForwards, FastUtilHackUtil::intEntryBackwards); +- } +- +- public static ObjectSet> entrySetLongWrap(Map map) { +- return new ConvertingObjectSet, Long2ObjectMap.Entry>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); +- } +- +- public static Long2ObjectMap.FastEntrySet entrySetLongWrapFast(Map map) { +- return new ConvertingObjectSetFast, T>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); +- } +- +- public static ObjectSet entrySetLongByteWrap(Map map) { +- return new ConvertingObjectSet, Long2ByteMap.Entry>(map.entrySet(), FastUtilHackUtil::longByteEntryForwards, FastUtilHackUtil::longByteEntryBackwards); +- } +- +- public static ObjectSet entrySetLongLongWrap(Map map) { +- return new ConvertingObjectSet, Long2LongMap.Entry>(map.entrySet(), FastUtilHackUtil::longLongEntryForwards, FastUtilHackUtil::longLongEntryBackwards); +- } +- +- +- static class WrappingIntIterator implements IntIterator { +- +- Iterator backing; +- +- public WrappingIntIterator(Iterator backing) { +- this.backing = backing; +- } +- +- @Override +- public boolean hasNext() { +- return backing.hasNext(); +- } +- +- @Override +- public int nextInt() { +- return backing.next(); +- } +- +- @Override +- public Integer next() { +- return backing.next(); +- } +- +- @Override +- public void remove() { +- backing.remove(); +- } +- +- } +- +- static class WrappingLongIterator implements LongIterator { +- +- Iterator backing; +- +- public WrappingLongIterator(Iterator backing) { +- this.backing = backing; +- } +- +- @Override +- public boolean hasNext() { +- return backing.hasNext(); +- } +- +- @Override +- public long nextLong() { +- return backing.next(); +- } +- +- @Override +- public Long next() { +- return backing.next(); +- } +- +- @Override +- public void remove() { +- backing.remove(); +- } +- +- } +- +- static class WrappingShortIterator implements ShortIterator { +- +- Iterator backing; +- +- public WrappingShortIterator(Iterator backing) { +- this.backing = backing; +- } +- +- @Override +- public boolean hasNext() { +- return backing.hasNext(); +- } +- +- @Override +- public short nextShort() { +- return backing.next(); +- } +- +- @Override +- public Short next() { +- return backing.next(); +- } +- +- @Override +- public void remove() { +- backing.remove(); +- } +- +- } +- +- public static class WrappingIntSet implements IntSet { +- +- Set backing; +- +- public WrappingIntSet(Set backing) { +- this.backing = backing; +- } +- +- @Override +- public boolean add(int key) { +- return backing.add(key); +- } +- +- @Override +- public boolean contains(int key) { +- return backing.contains(key); +- } +- +- @Override +- public int[] toIntArray() { +- return backing.stream().mapToInt(i -> i).toArray(); +- } +- +- @Override +- public int[] toIntArray(int[] a) { +- if (a.length >= size()) { +- return null; +- } else { +- return toIntArray(); +- } +- } +- +- @Override +- public int[] toArray(int[] a) { +- return toIntArray(a); +- } +- +- @Override +- public boolean addAll(IntCollection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean containsAll(IntCollection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean removeAll(IntCollection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(IntCollection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public Object[] toArray() { +- return backing.toArray(); +- } +- +- @Override +- public T[] toArray(T[] a) { +- return backing.toArray(a); +- } +- +- @Override +- public boolean containsAll(Collection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean addAll(Collection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean removeAll(Collection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(Collection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public IntIterator iterator() { +- return new WrappingIntIterator(backing.iterator()); +- } +- +- @Override +- public boolean remove(int k) { +- return backing.remove(k); +- } +- +- } +- +- public static LongSet wrapLongSet(Set longset) { +- return new WrappingLongSet(longset); +- } +- +- public static class WrappingLongSet implements LongSet { +- +- Set backing; +- +- public WrappingLongSet(Set backing) { +- this.backing = backing; +- } +- +- @Override +- public boolean add(long key) { +- return backing.add(key); +- } +- +- @Override +- public boolean contains(long key) { +- return backing.contains(key); +- } +- +- @Override +- public long[] toLongArray() { +- return backing.stream().mapToLong(i -> i).toArray(); +- } +- +- @Override +- public long[] toLongArray(long[] a) { +- if (a.length >= size()) { +- return null; +- } else { +- return toLongArray(); +- } +- } +- +- @Override +- public long[] toArray(long[] a) { +- return toLongArray(a); +- } +- +- @Override +- public boolean addAll(LongCollection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean containsAll(LongCollection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean removeAll(LongCollection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(LongCollection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public Object[] toArray() { +- return backing.toArray(); +- } +- +- @Override +- public T[] toArray(T[] a) { +- return backing.toArray(a); +- } +- +- @Override +- public boolean containsAll(Collection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean addAll(Collection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean removeAll(Collection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(Collection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public LongIterator iterator() { +- return new WrappingLongIterator(backing.iterator()); +- } +- +- @Override +- public boolean remove(long k) { +- return backing.remove(k); +- } +- +- } +- +- public static LongSortedSet wrapLongSortedSet(Set longset) { +- return new WrappingLongSortedSet(longset); +- } +- +- public static class WrappingLongSortedSet implements LongSortedSet { +- +- Set backing; +- +- public WrappingLongSortedSet(Set backing) { +- this.backing = backing; +- } +- +- @Override +- public boolean add(long key) { +- return backing.add(key); +- } +- +- @Override +- public boolean contains(long key) { +- return backing.contains(key); +- } +- +- @Override +- public long[] toLongArray() { +- return backing.stream().mapToLong(i -> i).toArray(); +- } +- +- @Override +- public long[] toLongArray(long[] a) { +- if (a.length >= size()) { +- return null; +- } else { +- return toLongArray(); +- } +- } +- +- @Override +- public long[] toArray(long[] a) { +- return toLongArray(a); +- } +- +- @Override +- public boolean addAll(LongCollection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean containsAll(LongCollection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean removeAll(LongCollection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(LongCollection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public Object[] toArray() { +- return backing.toArray(); +- } +- +- @Override +- public T[] toArray(T[] a) { +- return backing.toArray(a); +- } +- +- @Override +- public boolean containsAll(Collection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean addAll(Collection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean removeAll(Collection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(Collection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public boolean remove(long k) { +- return backing.remove(k); +- } +- +- @Override +- public LongBidirectionalIterator iterator(long fromElement) { +- throw new UnsupportedOperationException(); +- //return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); +- } +- +- @Override +- public LongBidirectionalIterator iterator() { +- return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); +- } +- +- @Override +- public LongSortedSet subSet(long fromElement, long toElement) { +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public LongSortedSet headSet(long toElement) { +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public LongSortedSet tailSet(long fromElement) { +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public LongComparator comparator() { +- return null; +- } +- +- @Override +- public long firstLong() { +- return backing.stream().findAny().get(); +- } +- +- @Override +- public long lastLong() { +- return backing.stream().findAny().get(); +- } +- +- } +- +- public static IntSet wrapIntSet(Set intset) { +- return new WrappingIntSet(intset); +- } +- +- public static class WrappingObjectCollection implements ObjectCollection { +- +- Collection backing; +- +- public WrappingObjectCollection(Collection backing) { +- this.backing = backing; +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public boolean contains(Object o) { +- return backing.contains(o); +- } +- +- @Override +- public Object[] toArray() { +- return backing.toArray(); +- } +- +- @Override +- public T[] toArray(T[] a) { +- return backing.toArray(a); +- } +- +- @Override +- public boolean add(V e) { +- return backing.add(e); +- } +- +- @Override +- public boolean remove(Object o) { +- return backing.remove(o); +- } +- +- @Override +- public boolean containsAll(Collection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean addAll(Collection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean removeAll(Collection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(Collection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public ObjectIterator iterator() { +- return FastUtilHackUtil.itrWrap(backing); +- } +- +- } +- +- public static ObjectCollection wrap(Collection c) { +- return new WrappingObjectCollection(c); +- } +- +- public static class WrappingByteCollection implements ByteCollection { +- +- Collection backing; +- +- public WrappingByteCollection(Collection backing) { +- this.backing = backing; +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public boolean contains(byte o) { +- return backing.contains(o); +- } +- +- @Override +- public Object[] toArray() { +- return backing.toArray(); +- } +- +- @Override +- public T[] toArray(T[] a) { +- return backing.toArray(a); +- } +- +- @Override +- public boolean add(byte e) { +- return backing.add(e); +- } +- +- @Override +- public boolean remove(Object o) { +- return backing.remove(o); +- } +- +- @Override +- public boolean containsAll(Collection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean addAll(Collection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean removeAll(Collection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(Collection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public ByteIterator iterator() { +- return FastUtilHackUtil.itrByteWrap(backing); +- } +- +- @Override +- public boolean rem(byte key) { +- return this.remove(key); +- } +- +- @Override +- public byte[] toByteArray() { +- return null; +- } +- +- @Override +- public byte[] toByteArray(byte[] a) { +- return toArray(a); +- } +- +- @Override +- public byte[] toArray(byte[] a) { +- return ArrayUtils.toPrimitive(backing.toArray(new Byte[0])); +- } +- +- @Override +- public boolean addAll(ByteCollection c) { +- return addAll((Collection) c); +- } +- +- @Override +- public boolean containsAll(ByteCollection c) { +- return containsAll((Collection) c); +- } +- +- @Override +- public boolean removeAll(ByteCollection c) { +- return removeAll((Collection) c); +- } +- +- @Override +- public boolean retainAll(ByteCollection c) { +- return retainAll((Collection) c); +- } +- +- } +- +- public static ByteCollection wrapBytes(Collection c) { +- return new WrappingByteCollection(c); +- } +- +- public static class WrappingIntCollection implements IntCollection { +- +- Collection backing; +- +- public WrappingIntCollection(Collection backing) { +- this.backing = backing; +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public boolean contains(int o) { +- return backing.contains(o); +- } +- +- @Override +- public Object[] toArray() { +- return backing.toArray(); +- } +- +- @Override +- public T[] toArray(T[] a) { +- return backing.toArray(a); +- } +- +- @Override +- public boolean add(int e) { +- return backing.add(e); +- } +- +- @Override +- public boolean remove(Object o) { +- return backing.remove(o); +- } +- +- @Override +- public boolean containsAll(Collection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean addAll(Collection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean removeAll(Collection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(Collection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public IntIterator iterator() { +- return FastUtilHackUtil.itrIntWrap(backing); +- } +- +- @Override +- public boolean rem(int key) { +- return this.remove(key); +- } +- +- @Override +- public int[] toIntArray() { +- return null; +- } +- +- @Override +- public int[] toIntArray(int[] a) { +- return toArray(a); +- } +- +- @Override +- public int[] toArray(int[] a) { +- return ArrayUtils.toPrimitive(backing.toArray(new Integer[0])); +- } +- +- @Override +- public boolean addAll(IntCollection c) { +- return addAll((Collection) c); +- } +- +- @Override +- public boolean containsAll(IntCollection c) { +- return containsAll((Collection) c); +- } +- +- @Override +- public boolean removeAll(IntCollection c) { +- return removeAll((Collection) c); +- } +- +- @Override +- public boolean retainAll(IntCollection c) { +- return retainAll((Collection) c); +- } +- +- } +- +- public static IntCollection wrapInts(Collection c) { +- return new WrappingIntCollection(c); +- } +- +- public static class WrappingLongCollection implements LongCollection { +- +- Collection backing; +- +- public WrappingLongCollection(Collection backing) { +- this.backing = backing; +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public boolean contains(long o) { +- return backing.contains(o); +- } +- +- @Override +- public Object[] toArray() { +- return backing.toArray(); +- } +- +- @Override +- public T[] toArray(T[] a) { +- return backing.toArray(a); +- } +- +- @Override +- public boolean add(long e) { +- return backing.add(e); +- } +- +- @Override +- public boolean remove(Object o) { +- return backing.remove(o); +- } +- +- @Override +- public boolean containsAll(Collection c) { +- return backing.containsAll(c); +- } +- +- @Override +- public boolean addAll(Collection c) { +- return backing.addAll(c); +- } +- +- @Override +- public boolean removeAll(Collection c) { +- return backing.removeAll(c); +- } +- +- @Override +- public boolean retainAll(Collection c) { +- return backing.retainAll(c); +- } +- +- @Override +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public LongIterator iterator() { +- return FastUtilHackUtil.itrLongWrap(backing); +- } +- +- @Override +- public boolean rem(long key) { +- return this.remove(key); +- } +- +- @Override +- public long[] toLongArray() { +- return null; +- } +- +- @Override +- public long[] toLongArray(long[] a) { +- return toArray(a); +- } +- +- @Override +- public long[] toArray(long[] a) { +- return ArrayUtils.toPrimitive(backing.toArray(new Long[0])); +- } +- +- @Override +- public boolean addAll(LongCollection c) { +- return addAll((Collection) c); +- } +- +- @Override +- public boolean containsAll(LongCollection c) { +- return containsAll((Collection) c); +- } +- +- @Override +- public boolean removeAll(LongCollection c) { +- return removeAll((Collection) c); +- } +- +- @Override +- public boolean retainAll(LongCollection c) { +- return retainAll((Collection) c); +- } +- +- } +- +- public static LongCollection wrapLongs(Collection c) { +- return new WrappingLongCollection(c); +- } +- +- +- public static class WrappingLongListIterator implements LongListIterator { +- +- ListIterator backing; +- +- public WrappingLongListIterator(ListIterator backing) { +- this.backing = backing; +- } +- +- @Override +- public long previousLong() { +- return backing.previous(); +- } +- +- @Override +- public long nextLong() { +- return backing.next(); +- } +- +- @Override +- public boolean hasNext() { +- return backing.hasNext(); +- } +- +- @Override +- public boolean hasPrevious() { +- return backing.hasPrevious(); +- } +- +- @Override +- public int nextIndex() { +- return backing.nextIndex(); +- } +- +- @Override +- public int previousIndex() { +- return backing.previousIndex(); +- } +- +- @Override +- public void add(long k) { +- backing.add(k); +- } +- +- @Override +- public void remove() { +- backing.remove(); +- } +- +- @Override +- public void set(long k) { +- backing.set(k); +- } +- } +- +- public static class SlimWrappingLongListIterator implements LongListIterator { +- +- Iterator backing; +- +- public SlimWrappingLongListIterator(Iterator backing) { +- this.backing = backing; +- } +- +- @Override +- public long previousLong() { +- throw new IllegalStateException(); +- } +- +- @Override +- public long nextLong() { +- return backing.next(); +- } +- +- @Override +- public boolean hasNext() { +- return backing.hasNext(); +- } +- +- @Override +- public boolean hasPrevious() { +- throw new IllegalStateException(); +- } +- +- @Override +- public int nextIndex() { +- throw new IllegalStateException(); +- } +- +- @Override +- public int previousIndex() { +- throw new IllegalStateException(); +- } +- +- @Override +- public void add(long k) { +- throw new IllegalStateException(); +- } +- +- @Override +- public void remove() { +- backing.remove(); +- } +- +- @Override +- public void set(long k) { +- throw new IllegalStateException(); +- } +- } +- +- public static LongListIterator wrap(ListIterator c) { +- return new WrappingLongListIterator(c); +- } +- +- public static LongListIterator wrap(Iterator c) { +- return new SlimWrappingLongListIterator(c); +- } +- +- public static class WrappingByteIterator implements ByteIterator { +- +- Iterator parent; +- +- public WrappingByteIterator(Iterator parent) { +- this.parent = parent; +- } +- +- @Override +- public boolean hasNext() { +- return parent.hasNext(); +- } +- +- @Override +- public Byte next() { +- return parent.next(); +- } +- +- @Override +- public void remove() { +- parent.remove(); +- } +- +- @Override +- public byte nextByte() { +- return next(); +- } +- +- } +- +- public static ByteIterator itrByteWrap(Iterator backing) { +- return new WrappingByteIterator(backing); +- } +- +- public static ByteIterator itrByteWrap(Iterable backing) { +- return new WrappingByteIterator(backing.iterator()); +- } +- +- public static IntIterator itrIntWrap(Iterator backing) { +- return new WrappingIntIterator(backing); +- } +- +- public static IntIterator itrIntWrap(Iterable backing) { +- return new WrappingIntIterator(backing.iterator()); +- } +- +- public static LongIterator itrLongWrap(Iterator backing) { +- return new WrappingLongIterator(backing); +- } +- +- public static LongIterator itrLongWrap(Iterable backing) { +- return new WrappingLongIterator(backing.iterator()); +- } +- +- public static ShortIterator itrShortWrap(Iterator backing) { +- return new WrappingShortIterator(backing); +- } +- +- public static ShortIterator itrShortWrap(Iterable backing) { +- return new WrappingShortIterator(backing.iterator()); +- } +- +- public static class WrapperObjectIterator implements ObjectIterator { +- +- Iterator parent; +- +- public WrapperObjectIterator(Iterator parent) { +- this.parent = parent; +- } +- +- @Override +- public boolean hasNext() { +- return parent.hasNext(); +- } +- +- @Override +- public T next() { +- return parent.next(); +- } +- +- @Override +- public void remove() { +- parent.remove(); +- } +- +- } +- +- public static class IntWrapperEntry implements Entry { +- +- Map.Entry parent; +- +- public IntWrapperEntry(Map.Entry parent) { +- this.parent = parent; +- } +- +- @Override +- public T getValue() { +- return parent.getValue(); +- } +- +- @Override +- public T setValue(T value) { +- return parent.setValue(value); +- } +- +- @Override +- public int getIntKey() { +- return parent.getKey(); +- } +- +- @Override +- public Integer getKey() { +- return parent.getKey(); +- } +- +- } +- +- public static class Long2IntWrapperEntry implements Long2IntMap.Entry { +- +- Map.Entry parent; +- +- public Long2IntWrapperEntry(Map.Entry parent) { +- this.parent = parent; +- } +- +- @Override +- public long getLongKey() { +- return parent.getKey(); +- } +- +- @Override +- public int getIntValue() { +- return parent.getValue(); +- } +- +- @Override +- public int setValue(int value) { +- return parent.setValue(value); +- } +- +- +- } +- +- public static class WrapperIntEntryObjectIterator implements ObjectIterator> { +- +- Iterator> parent; +- +- public WrapperIntEntryObjectIterator(Iterator> parent) { +- this.parent = parent; +- } +- +- @Override +- public boolean hasNext() { +- return parent.hasNext(); +- } +- +- @Override +- public Entry next() { +- Map.Entry val = parent.next(); +- if (val == null) return null; +- return new IntWrapperEntry(val); +- } +- +- @Override +- public void remove() { +- parent.remove(); +- } +- +- } +- +- public static ObjectIterator> intMapItrFake(Map in) { +- return new WrapperIntEntryObjectIterator(in.entrySet().iterator()); +- } +- +- public static ObjectIterator itrWrap(Iterator in) { +- return new WrapperObjectIterator(in); +- } +- +- public static ObjectIterator itrWrap(Iterable in) { +- return new WrapperObjectIterator(in.iterator()); +- } +-} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java +deleted file mode 100644 +index bb6c592fd34423fdd910feae83a058d288da537a..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java ++++ /dev/null +@@ -1,93 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised.fastutil; +- +-import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +-import it.unimi.dsi.fastutil.ints.IntSet; +-import it.unimi.dsi.fastutil.objects.ObjectCollection; +-import it.unimi.dsi.fastutil.objects.ObjectSet; +-import org.apache.commons.lang3.NotImplementedException; +- +-import java.util.Map; +-import java.util.concurrent.ConcurrentHashMap; +- +-public class Int2ObjectConcurrentHashMap implements Int2ObjectMap { +- +- Map backing; +- +- public Int2ObjectConcurrentHashMap() { +- backing = new ConcurrentHashMap(); +- } +- +- @Override +- public V get(int key) { +- return backing.get(key); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public boolean containsValue(Object value) { +- return backing.containsValue(value); +- } +- +- @Override +- public void putAll(Map m) { +- backing.putAll(m); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public void defaultReturnValue(V rv) { +- throw new NotImplementedException("MCMT - Not implemented"); +- } +- +- @Override +- public V defaultReturnValue() { +- return null; +- } +- +- @Override +- public ObjectSet> int2ObjectEntrySet() { +- return FastUtilHackUtil.entrySetIntWrap(backing); +- } +- +- +- @Override +- public IntSet keySet() { +- return FastUtilHackUtil.wrapIntSet(backing.keySet()); +- } +- +- @Override +- public ObjectCollection values() { +- return FastUtilHackUtil.wrap(backing.values()); +- } +- +- @Override +- public boolean containsKey(int key) { +- return backing.containsKey(key); +- } +- +- @Override +- public V put(int key, V value) { +- return backing.put(key, value); +- } +- +- @Override +- public V put(Integer key, V value) { +- return backing.put(key, value); +- } +- +- @Override +- public V remove(int key) { +- return backing.remove(key); +- } +- +- @Override +- public void clear() { backing.clear(); } +-} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java +deleted file mode 100644 +index e0fab16860e1be817fd10dbec684f295f2e291dd..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java ++++ /dev/null +@@ -1,99 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised.fastutil; +- +-import it.unimi.dsi.fastutil.bytes.ByteCollection; +-import it.unimi.dsi.fastutil.longs.Long2ByteMap; +-import it.unimi.dsi.fastutil.longs.LongSet; +-import it.unimi.dsi.fastutil.objects.ObjectSet; +- +-import java.util.Map; +-import java.util.concurrent.ConcurrentHashMap; +- +-public class Long2ByteConcurrentHashMap implements Long2ByteMap { +- +- Map backing; +- byte defaultReturn = 0; +- byte nullKey = 0; +- +- public Long2ByteConcurrentHashMap() { +- backing = new ConcurrentHashMap<>(); +- } +- +- public Long2ByteConcurrentHashMap(int initialCapacity, float loadFactor) { +- backing = new ConcurrentHashMap<>(initialCapacity, loadFactor); +- } +- +- @Override +- public byte get(long key) { +- Byte out = backing.get(key); +- return out == null ? defaultReturn : out; +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public boolean containsValue(byte value) { +- return backing.containsValue(value); +- } +- +- @Override +- public void putAll(Map m) { +- backing.putAll(m); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public void defaultReturnValue(byte rv) { +- defaultReturn = rv; +- } +- +- @Override +- public byte defaultReturnValue() { +- return defaultReturn; +- } +- +- @Override +- public ObjectSet long2ByteEntrySet() { +- return FastUtilHackUtil.entrySetLongByteWrap(backing); +- } +- +- @Override +- public LongSet keySet() { +- return FastUtilHackUtil.wrapLongSet(backing.keySet()); +- } +- +- @Override +- public ByteCollection values() { +- return FastUtilHackUtil.wrapBytes(backing.values()); +- } +- +- @Override +- public boolean containsKey(long key) { +- return backing.containsKey(key); +- } +- +- @Override +- public byte put(long key, byte value) { +- return put((Long) key, (Byte) value); +- } +- +- @Override +- public Byte put(Long key, Byte value) { +- Byte out = backing.put(key, value); +- return out == null ? Byte.valueOf(defaultReturn) : out; +- } +- +- @Override +- public byte remove(long key) { +- Byte out = backing.remove(key); +- return out == null ? defaultReturn : out; +- } +- +- +-} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java +deleted file mode 100644 +index 261f06a88021a95b6a0500444665547aeb4ae2c1..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java ++++ /dev/null +@@ -1,74 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised.fastutil; +- +-import it.unimi.dsi.fastutil.ints.IntCollection; +-import it.unimi.dsi.fastutil.longs.Long2IntMap; +-import it.unimi.dsi.fastutil.longs.LongSet; +-import it.unimi.dsi.fastutil.objects.ObjectSet; +- +-import java.util.Map; +-import java.util.concurrent.ConcurrentHashMap; +- +-public class Long2IntConcurrentHashMap implements Long2IntMap { +- +- public Map backing = new ConcurrentHashMap(); +- int defaultRV = 0; +- +- @Override +- public int get(long key) { +- if (backing.containsKey(key)) { +- return backing.get(key); +- } else return defaultRV; +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public void putAll(Map m) { +- backing.putAll(m); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public void defaultReturnValue(int rv) { +- defaultRV = rv; +- } +- +- @Override +- public int defaultReturnValue() { +- return defaultRV; +- } +- +- @Override +- public ObjectSet long2IntEntrySet() { +- return null; +- } +- +- @Override +- public LongSet keySet() { +- return FastUtilHackUtil.wrapLongSet(backing.keySet()); +- } +- +- @Override +- public IntCollection values() { +- return FastUtilHackUtil.wrapInts(backing.values()); +- } +- +- @Override +- public boolean containsKey(long key) { +- return backing.containsKey(key); +- } +- +- @Override +- public boolean containsValue(int value) { +- return backing.containsValue(value); +- } +- +- +-} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java +deleted file mode 100644 +index dc2342486318721d399c7c60a0a859befb4d1c9f..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java ++++ /dev/null +@@ -1,375 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised.fastutil; +- +-import it.unimi.dsi.fastutil.Hash; +-import it.unimi.dsi.fastutil.ints.IntCollection; +-import it.unimi.dsi.fastutil.longs.*; +- +-import java.util.Map; +-import java.util.concurrent.ConcurrentHashMap; +- +-@SuppressWarnings("deprecation") +-public class Long2IntConcurrentNonLinkedOpenMap extends Long2IntLinkedOpenHashMap { +- +- /** +- * +- */ +- private static final long serialVersionUID = -2082212127278131631L; +- +- public Map backing = new ConcurrentHashMap(); +- +- public Long2IntConcurrentNonLinkedOpenMap(final int expected, final float f) { +- +- } +- +- /** +- * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. +- * +- * @param expected the expected number of elements in the hash map. +- */ +- public Long2IntConcurrentNonLinkedOpenMap(final int expected) { +- } +- +- /** +- * Creates a new hash map with initial expected +- * {@link Hash#DEFAULT_INITIAL_SIZE} entries and +- * {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. +- */ +- public Long2IntConcurrentNonLinkedOpenMap() { +- } +- +- /** +- * Creates a new hash map copying a given one. +- * +- * @param m a {@link Map} to be copied into the new hash map. +- * @param f the load factor. +- */ +- public Long2IntConcurrentNonLinkedOpenMap(final Map m, final float f) { +- putAll(m); +- } +- +- /** +- * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor +- * copying a given one. +- * +- * @param m a {@link Map} to be copied into the new hash map. +- */ +- public Long2IntConcurrentNonLinkedOpenMap(final Map m) { +- this(m, DEFAULT_LOAD_FACTOR); +- } +- +- /** +- * Creates a new hash map copying a given type-specific one. +- * +- * @param m a type-specific map to be copied into the new hash map. +- * @param f the load factor. +- */ +- public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m, final float f) { +- this(m.size(), f); +- putAll(m); +- } +- +- /** +- * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor +- * copying a given type-specific one. +- * +- * @param m a type-specific map to be copied into the new hash map. +- */ +- public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m) { +- this(m, DEFAULT_LOAD_FACTOR); +- } +- +- /** +- * Creates a new hash map using the elements of two parallel arrays. +- * +- * @param k the array of keys of the new hash map. +- * @param v the array of corresponding values in the new hash map. +- * @param f the load factor. +- * @throws IllegalArgumentException if {@code k} and {@code v} have different +- * lengths. +- */ +- public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v, final float f) { +- if (k.length != v.length) +- throw new IllegalArgumentException( +- "The key array and the value array have different lengths (" + k.length + " and " + v.length + ")"); +- for (int i = 0; i < k.length; i++) +- this.put(k[i], v[i]); +- } +- +- /** +- * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor +- * using the elements of two parallel arrays. +- * +- * @param k the array of keys of the new hash map. +- * @param v the array of corresponding values in the new hash map. +- * @throws IllegalArgumentException if {@code k} and {@code v} have different +- * lengths. +- */ +- public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v) { +- this(k, v, DEFAULT_LOAD_FACTOR); +- } +- +- public void putAll(Map m) { +- backing.putAll(m); +- } +- +- public int put(final long k, final int v) { +- Integer out = backing.put(k, v); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public int addTo(final long k, final int incr) { +- Integer out = backing.put(k, this.get(k)+incr); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public int remove(final long k) { +- Integer out = backing.remove(k); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public int removeFirstInt() { +- Integer out = this.remove(backing.keySet().stream().findAny().get()); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public int removeLastInt() { +- Integer out = this.remove(backing.keySet().stream().findAny().get()); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- +- public int getAndMoveToFirst(final long k) { +- Integer out = backing.get(k); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public int getAndMoveToLast(final long k) { +- Integer out = backing.get(k); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public int putAndMoveToFirst(final long k, final int v) { +- Integer out = backing.put(k, v); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public int putAndMoveToLast(final long k, final int v) { +- Integer out = backing.put(k, v); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public int get(final long k) { +- Integer out = backing.get(k); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public boolean containsKey(final long k) { +- return backing.containsKey(k); +- } +- +- public boolean containsValue(final int v) { +- return backing.containsValue(v); +- } +- +- public int getOrDefault(final long k, final int defaultValue) { +- Integer out = backing.getOrDefault(k, defaultValue); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- public int putIfAbsent(final long k, final int v) { +- Integer out = backing.putIfAbsent(k, v); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- +- public boolean remove(final long k, final int v) { +- return backing.remove(k, v); +- } +- +- +- public boolean replace(final long k, final int oldValue, final int v) { +- return backing.replace(k, oldValue, v); +- } +- +- +- public int replace(final long k, final int v) { +- Integer out = backing.replace(k, v); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- +- public int computeIfAbsent(final long k, final java.util.function.LongToIntFunction mappingFunction) { +- Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.applyAsInt(l)); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- +- public int computeIfAbsentNullable(final long k, +- final java.util.function.LongFunction mappingFunction) { +- Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.apply(l)); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- +- public int computeIfPresent(final long k, +- final java.util.function.BiFunction remappingFunction) { +- if (this.containsKey(k)) { +- Integer out = backing.put(k, remappingFunction.apply(k, backing.get(k))); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- return defaultReturnValue(); +- +- } +- +- @Override +- public int compute(final long k, +- final java.util.function.BiFunction remappingFunction) { +- Integer out = backing.compute(k, remappingFunction); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- @Override +- public int merge(final long k, final int v, +- final java.util.function.BiFunction remappingFunction) { +- Integer out = backing.merge(k, v, remappingFunction); +- if (out == null) { +- return defRetValue; +- } +- return out; +- } +- +- @Override +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public long firstLongKey() { +- return backing.keySet().stream().findAny().get(); +- } +- +- @Override +- public long lastLongKey() { +- return backing.keySet().stream().findAny().get(); +- } +- +- @Override +- public Long2IntSortedMap tailMap(long from) { +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public Long2IntSortedMap headMap(long to) { +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public Long2IntSortedMap subMap(long from, long to) { +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public LongComparator comparator() { +- return null; +- } +- +- +- @Override +- public FastSortedEntrySet long2IntEntrySet() { +- //TODO implement +- throw new UnsupportedOperationException(); +- } +- +- @Override +- public LongSortedSet keySet() { +- return FastUtilHackUtil.wrapLongSortedSet(backing.keySet()); +- } +- +- +- @Override +- public IntCollection values() { +- return FastUtilHackUtil.wrapInts(backing.values()); +- } +- +- public boolean trim() { +- return true; +- } +- +- public boolean trim(final int n) { +- return true; +- } +- +- +- @Override +- public Long2IntConcurrentNonLinkedOpenMap clone() { +- return new Long2IntConcurrentNonLinkedOpenMap(backing); +- } +- +- @Override +- public int hashCode() { +- return backing.hashCode(); +- } +- +- +-} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java +deleted file mode 100644 +index 3205d30a03f99caf7dfa05237b2bc31182b2db20..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java ++++ /dev/null +@@ -1,97 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised.fastutil; +- +-import it.unimi.dsi.fastutil.longs.Long2LongMap; +-import it.unimi.dsi.fastutil.longs.LongCollection; +-import it.unimi.dsi.fastutil.longs.LongSet; +-import it.unimi.dsi.fastutil.objects.ObjectSet; +- +-import java.util.Map; +-import java.util.concurrent.ConcurrentHashMap; +- +- +-public class Long2LongConcurrentHashMap implements Long2LongMap { +- +- public Map backing = new ConcurrentHashMap(); +- long defaultRV = 0; +- +- public Long2LongConcurrentHashMap(long defaultRV) { +- this.defaultRV = defaultRV; +- } +- +- @Override +- public long get(long key) { +- if (backing.containsKey(key)) { +- return backing.get(key); +- } else return defaultRV; +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public long put(final long key, final long val) { +- backing.put(key,val); +- return val; +- } +- +- @Override +- public Long put(final Long key, final Long val) { +- backing.put(key,val); +- return val; +- } +- +- @Override +- public long remove(final long key) { +- return backing.remove(key); +- } +- +- @Override +- public void putAll(Map m) { +- backing.putAll(m); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public void defaultReturnValue(long rv) { +- defaultRV = rv; +- } +- +- @Override +- public long defaultReturnValue() { +- return defaultRV; +- } +- +- @Override +- public ObjectSet long2LongEntrySet() { +- return FastUtilHackUtil.entrySetLongLongWrap(backing); +- } +- +- +- @Override +- public LongSet keySet() { +- return FastUtilHackUtil.wrapLongSet(backing.keySet()); +- } +- +- @Override +- public LongCollection values() { +- return FastUtilHackUtil.wrapLongs(backing.values()); +- } +- +- @Override +- public boolean containsKey(long key) { +- return backing.containsKey(key); +- } +- +- @Override +- public boolean containsValue(long value) { +- return backing.containsValue(value); +- } +- +- +-} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java +deleted file mode 100644 +index a5ed71564d4c9a986f77cbc0397130aa38f97a91..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java ++++ /dev/null +@@ -1,93 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised.fastutil; +- +-import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +-import it.unimi.dsi.fastutil.longs.LongSet; +-import it.unimi.dsi.fastutil.objects.ObjectCollection; +-import it.unimi.dsi.fastutil.objects.ObjectSet; +- +-import java.util.Map; +-import java.util.concurrent.ConcurrentHashMap; +- +-public class Long2ObjectConcurrentHashMap implements Long2ObjectMap { +- +- Map backing; +- V defaultReturn = null; +- +- public Long2ObjectConcurrentHashMap() { +- backing = new ConcurrentHashMap(); +- } +- +- @Override +- public V get(long key) { +- V out = backing.get(key); +- return (out == null && !backing.containsKey(key)) ? defaultReturn : out; +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public boolean containsValue(Object value) { +- return backing.containsValue(value); +- } +- +- @Override +- public void putAll(Map m) { +- backing.putAll(m); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public void defaultReturnValue(V rv) { +- defaultReturn = rv; +- } +- +- @Override +- public V defaultReturnValue() { +- return defaultReturn; +- } +- +- @Override +- public ObjectSet> long2ObjectEntrySet() { +- return FastUtilHackUtil.entrySetLongWrap(backing); +- } +- +- +- @Override +- public LongSet keySet() { +- return FastUtilHackUtil.wrapLongSet(backing.keySet()); +- } +- +- @Override +- public ObjectCollection values() { +- return FastUtilHackUtil.wrap(backing.values()); +- } +- +- @Override +- public boolean containsKey(long key) { +- return backing.containsKey(key); +- } +- +- @Override +- public V put(long key, V value) { +- return put((Long)key, value); +- } +- +- @Override +- public V put(Long key, V value) { +- V out = backing.put(key, value); +- return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); +- } +- +- @Override +- public V remove(long key) { +- V out = backing.remove(key); +- return (out == null && !backing.containsKey(key)) ? defaultReturn : out; +- } +-} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java +deleted file mode 100644 +index a7d6be048ab3b8bd38231fce16eca0ac78e24690..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java ++++ /dev/null +@@ -1,233 +0,0 @@ +-package net.himeki.mcmtfabric.parallelised.fastutil; +- +-import it.unimi.dsi.fastutil.longs.Long2ObjectFunction; +-import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +-import it.unimi.dsi.fastutil.longs.LongSet; +-import it.unimi.dsi.fastutil.objects.ObjectCollection; +-import it.unimi.dsi.fastutil.objects.ObjectSet; +- +-import java.util.Map; +-import java.util.concurrent.ConcurrentHashMap; +-import java.util.function.Function; +- +-public class Long2ObjectOpenConcurrentHashMap extends Long2ObjectOpenHashMap { +- +- /** +- * +- */ +- private static final long serialVersionUID = -121514116954680057L; +- +- Map backing; +- V defaultReturn = null; +- +- public Long2ObjectOpenConcurrentHashMap() { +- backing = new ConcurrentHashMap(); +- } +- +- @Override +- public V get(long key) { +- V out = backing.get(key); +- return (out == null && !backing.containsKey(key)) ? defaultReturn : out; +- } +- +- @Override +- public V get(Object key) { +- V out = backing.get(key); +- return (out == null && !backing.containsKey(key)) ? defaultReturn : out; +- } +- +- @Override +- public boolean isEmpty() { +- return backing.isEmpty(); +- } +- +- @Override +- public boolean containsValue(Object value) { +- return backing.containsValue(value); +- } +- +- @Override +- public void putAll(Map m) { +- backing.putAll(m); +- } +- +- @Override +- public int size() { +- return backing.size(); +- } +- +- @Override +- public void defaultReturnValue(V rv) { +- defaultReturn = rv; +- } +- +- @Override +- public V defaultReturnValue() { +- return defaultReturn; +- } +- +- @Override +- public FastEntrySet long2ObjectEntrySet() { +- return FastUtilHackUtil.entrySetLongWrapFast(backing); +- } +- +- +- @Override +- public LongSet keySet() { +- return FastUtilHackUtil.wrapLongSet(backing.keySet()); +- } +- +- @Override +- public ObjectCollection values() { +- return FastUtilHackUtil.wrap(backing.values()); +- } +- +- @Override +- public boolean containsKey(long key) { +- return backing.containsKey(key); +- } +- +- @Override +- public V put(long key, V value) { +- return put((Long)key, value); +- } +- +- @Override +- public V put(Long key, V value) { +- V out = backing.put(key, value); +- return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); +- } +- +- @Override +- public V remove(long key) { +- V out = backing.remove(key); +- return (out == null && !backing.containsKey(key)) ? defaultReturn : out; +- } +- +- @Override +- public boolean trim() { return true; } +- +- @Override +- public boolean trim(final int n) { return true; } +- +- @Override +- public boolean replace(final long k, final V oldValue, final V v) { +- return backing.replace(k, oldValue, v); +- } +- +- @Override +- public V replace(final long k, final V v) { +- return backing.replace(k, v); +- } +- +- @Override +- public boolean replace(final Long k, final V oldValue, final V v) { +- return backing.replace(k, oldValue, v); +- } +- +- @Override +- public V replace(final Long k, final V v) { +- return backing.replace(k, v); +- } +- +- @Override +- public boolean remove(final long k, final Object v) { +- return backing.remove(k, v); +- } +- +- @Override +- public V putIfAbsent(final long k, final V v) { +- return backing.putIfAbsent(k, v); +- } +- +- @Override +- public V putIfAbsent(final Long k, final V v) { +- return backing.putIfAbsent(k, v); +- } +- +- @Override +- public V merge(final long k, final V v, final java.util.function.BiFunction remappingFunction) { +- return backing.merge(k, v, remappingFunction); +- } +- +- @Override +- public V merge(Long k, final V v, final java.util.function.BiFunction remappingFunction) { +- return backing.merge(k, v, remappingFunction); +- } +- +- @Override +- public int hashCode() { +- return backing.hashCode(); +- } +- +- @Override +- public V getOrDefault(final long k, final V defaultValue) { +- return backing.getOrDefault(k, defaultValue); +- } +- +- @Override +- public V getOrDefault(Object k, final V defaultValue) { +- return backing.getOrDefault(k, defaultValue); +- } +- +- @Override +- public V computeIfPresent(final long k, final java.util.function.BiFunction remappingFunction) { +- return backing.computeIfPresent(k, remappingFunction); +- } +- +- @Override +- public V computeIfPresent(final Long k, final java.util.function.BiFunction remappingFunction) { +- return backing.computeIfPresent(k, remappingFunction); +- } +- +- @Override +- public V computeIfAbsent(final long k, final java.util.function.LongFunction mappingFunction) { +- return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); +- } +- +- public V computeIfAbsent(final Long k, final java.util.function.LongFunction mappingFunction) { +- return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); +- } +- +- @Override +- public V computeIfAbsentPartial(final long key, final Long2ObjectFunction mappingFunction) { +- if (!mappingFunction.containsKey(key)) +- return defaultReturn; +- return backing.computeIfAbsent(key, (llong) -> mappingFunction.apply(llong)); +- } +- +- @Override +- public V compute(final long k, final java.util.function.BiFunction remappingFunction) { +- return backing.compute(k, remappingFunction); +- } +- +- @Override +- public V compute(final Long k, final java.util.function.BiFunction remappingFunction) { +- return backing.compute(k, remappingFunction); +- } +- +- @Override +- public Long2ObjectOpenHashMap clone() { +- throw new IllegalArgumentException(); +- } +- +- public void clear() { +- backing.clear(); +- } +- +- @Override +- public ObjectSet> entrySet() { +- return new FastUtilHackUtil.ConvertingObjectSet, Map.Entry>(backing.entrySet(), Function.identity(), Function.identity()); +- } +- +- @Override +- public V remove(Object key) { +- return backing.remove(key); +- } +- +- @Override +- public boolean remove(Object key, Object value) { +- return backing.remove(key, value); +- } +- +-} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java +deleted file mode 100644 +index 565dc74cb65ef0ce9df36a72d9cd5fa440a535e6..0000000000000000000000000000000000000000 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java ++++ /dev/null +@@ -1,197 +0,0 @@ +-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 c) { +- return super.addAll(c); +- } +- +- @Override +- public synchronized boolean addAndMoveToFirst(final long k) { +- return super.addAndMoveToFirst(k); +- } +- +- @Override +- public synchronized boolean addAndMoveToLast(final long k) { +- return super.addAndMoveToFirst(k); +- } +- +- @Override +- public synchronized void clear() { +- super.clear(); +- } +- +- @Override +- public synchronized LongLinkedOpenHashSet clone() { +- return new SyncLongLinkedOpenHashSet(this); +- } +- +- @Override +- public synchronized LongComparator comparator() { +- return super.comparator(); +- } +- +- @Override +- public synchronized boolean contains(final long k) { +- return super.contains(k); +- } +- +- @Override +- public synchronized long firstLong() { +- return super.firstLong(); +- } +- +- @Override +- public synchronized int hashCode() { +- return super.hashCode(); +- } +- +- @Override +- public synchronized LongSortedSet headSet(long to) { +- return super.headSet(to); +- } +- +- @Override +- public synchronized boolean isEmpty() { +- return super.isEmpty(); +- } +- +- @Override +- public synchronized LongListIterator iterator() { +- return super.iterator(); +- } +- +- @Override +- public synchronized LongListIterator iterator(long from) { +- return super.iterator(from); +- } +- +- @Override +- public synchronized long lastLong() { +- return super.lastLong(); +- } +- +- @Override +- public synchronized boolean remove(final long k) { +- return super.remove(k); +- } +- +- @Override +- public synchronized long removeFirstLong() { +- return super.removeFirstLong(); +- } +- +- @Override +- public synchronized long removeLastLong() { +- return super.removeLastLong(); +- } +- +- @Override +- public synchronized int size() { +- return super.size(); +- } +- +- @Override +- public synchronized LongSortedSet subSet(long from, long to) { +- return super.subSet(from, to); +- } +- +- @Override +- public synchronized LongSortedSet tailSet(long from) { +- return super.tailSet(from); +- } +- +- @Override +- public synchronized boolean trim() { +- return super.trim(); +- } +- +- @Override +- public synchronized boolean trim(final int n) { +- return super.trim(n); +- } +-} diff --git a/patches/server/0041-Hearse-Change-paper-s-code.patch b/patches/server/0041-Hearse-Change-paper-s-code.patch new file mode 100644 index 00000000..b032eadf --- /dev/null +++ b/patches/server/0041-Hearse-Change-paper-s-code.patch @@ -0,0 +1,1839 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Sun, 8 Jan 2023 21:08:14 +0800 +Subject: [PATCH] Hearse: Change paper's code + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java b/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java +index 0133ea6feb1ab88f021f66855669f58367e7420b..a1ac254c71eb5559b88ed1a6bd5128539d3b38b5 100644 +--- a/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java ++++ b/src/main/java/com/destroystokyo/paper/util/maplist/EntityList.java +@@ -1,128 +1,50 @@ + package com.destroystokyo.paper.util.maplist; + +-import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; ++import it.unimi.dsi.fastutil.objects.ObjectArrayList; ++import it.unimi.dsi.fastutil.objects.ObjectList; ++import it.unimi.dsi.fastutil.objects.ObjectLists; + import net.minecraft.world.entity.Entity; +-import java.util.Arrays; ++import org.jetbrains.annotations.NotNull; ++ + import java.util.Iterator; +-import java.util.NoSuchElementException; + +-// list with O(1) remove & contains +-/** +- * @author Spottedleaf +- */ + public final class EntityList implements Iterable { +- +- protected final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f); +- { +- this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE); +- } +- +- protected static final Entity[] EMPTY_LIST = new Entity[0]; +- +- protected Entity[] entities = EMPTY_LIST; +- protected int count; ++ private final ObjectList entities = ObjectLists.synchronize(new ObjectArrayList<>()); + + public int size() { +- return this.count; ++ return this.entities.size(); + } + + public boolean contains(final Entity entity) { +- return this.entityToIndex.containsKey(entity.getId()); ++ return this.entities.contains(entity); + } + + public boolean remove(final Entity entity) { +- final int index = this.entityToIndex.remove(entity.getId()); +- if (index == Integer.MIN_VALUE) { +- return false; +- } +- +- // move the entity at the end to this index +- final int endIndex = --this.count; +- final Entity end = this.entities[endIndex]; +- if (index != endIndex) { +- // not empty after this call +- this.entityToIndex.put(end.getId(), index); // update index +- } +- this.entities[index] = end; +- this.entities[endIndex] = null; +- +- return true; ++ return this.entities.remove(entity); + } + + public boolean add(final Entity entity) { +- final int count = this.count; +- final int currIndex = this.entityToIndex.putIfAbsent(entity.getId(), count); +- +- if (currIndex != Integer.MIN_VALUE) { +- return false; // already in this list +- } +- +- Entity[] list = this.entities; +- +- if (list.length == count) { +- // resize required +- list = this.entities = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative +- } +- +- list[count] = entity; +- this.count = count + 1; +- +- return true; ++ return this.entities.add(entity); + } + + public Entity getChecked(final int index) { +- if (index < 0 || index >= this.count) { +- throw new IndexOutOfBoundsException("Index: " + index + " is out of bounds, size: " + this.count); +- } +- return this.entities[index]; ++ return this.entities.get(index); + } + + public Entity getUnchecked(final int index) { +- return this.entities[index]; ++ return this.entities.get(index); + } + + public Entity[] getRawData() { +- return this.entities; ++ return this.entities.toArray(Entity[]::new); + } + + public void clear() { +- this.entityToIndex.clear(); +- Arrays.fill(this.entities, 0, this.count, null); +- this.count = 0; ++ this.entities.clear(); + } + + @Override +- public Iterator iterator() { +- return new Iterator() { +- +- Entity lastRet; +- int current; +- +- @Override +- public boolean hasNext() { +- return this.current < EntityList.this.count; +- } +- +- @Override +- public Entity next() { +- if (this.current >= EntityList.this.count) { +- throw new NoSuchElementException(); +- } +- return this.lastRet = EntityList.this.entities[this.current++]; +- } +- +- @Override +- public void remove() { +- final Entity lastRet = this.lastRet; +- +- if (lastRet == null) { +- throw new IllegalStateException(); +- } +- this.lastRet = null; +- +- EntityList.this.remove(lastRet); +- --this.current; +- } +- }; ++ public @NotNull Iterator iterator() { ++ return this.entities.iterator(); + } + } +diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +index 0b060183429f4c72ec767075538477b4302bbf0d..f72d99bea1c78c8882793fa8ee3dc3642be1b8c7 100644 +--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java ++++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +@@ -22,11 +22,11 @@ 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.ConcurrentSkipListSet; + import java.util.concurrent.atomic.AtomicInteger; ++import java.util.concurrent.locks.StampedLock; + + public final class PlayerChunkLoader { + +@@ -75,6 +75,9 @@ public final class PlayerChunkLoader { + return data.getTargetSendViewDistance(); + } + ++ private final StampedLock sendLock = new StampedLock(); ++ private final StampedLock loadLock = new StampedLock(); ++ + protected final ChunkMap chunkMap; + protected final Reference2ObjectLinkedOpenHashMap playerMap = new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f); + protected final ReferenceLinkedOpenHashSet chunkSendQueue = new ReferenceLinkedOpenHashSet<>(512, 0.7f); +@@ -109,7 +112,7 @@ public final class PlayerChunkLoader { + return Integer.compare(System.identityHashCode(p1), System.identityHashCode(p2)); + }); + +- protected final TreeSet chunkSendWaitQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> { ++ protected final NavigableSet chunkSendWaitQueue = new ConcurrentSkipListSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> { + if (p1 == p2) { + return 0; + } +@@ -481,8 +484,21 @@ public final class PlayerChunkLoader { + return; + } + loaderData.remove(); +- this.chunkLoadQueue.remove(loaderData); +- this.chunkSendQueue.remove(loaderData); ++ ++ long id1 = this.loadLock.writeLock(); ++ try { ++ this.chunkLoadQueue.remove(loaderData); ++ }finally { ++ this.loadLock.unlockWrite(id1); ++ } ++ ++ final long id = this.sendLock.writeLock(); ++ try { ++ this.chunkSendQueue.remove(loaderData); ++ }finally { ++ this.sendLock.unlockWrite(id); ++ } ++ + this.chunkSendWaitQueue.remove(loaderData); + synchronized (this.sendingChunkCounts) { + final int count = this.sendingChunkCounts.removeInt(loaderData); +@@ -523,29 +539,46 @@ public final class PlayerChunkLoader { + if (time < nextChunkSend) { + return; + } +- // drain entries from wait queue +- while (!this.chunkSendWaitQueue.isEmpty()) { +- final PlayerLoaderData data = this.chunkSendWaitQueue.first(); ++ final long id = this.sendLock.writeLock(); ++ try { ++ // drain entries from wait queue ++ while (!this.chunkSendWaitQueue.isEmpty()) { ++ final PlayerLoaderData data = this.chunkSendWaitQueue.first(); + +- if (data.nextChunkSendTarget > time) { +- break; +- } ++ if (data.nextChunkSendTarget > time) { ++ break; ++ } + +- this.chunkSendWaitQueue.pollFirst(); ++ this.chunkSendWaitQueue.pollFirst(); + +- this.chunkSendQueue.add(data); ++ this.chunkSendQueue.add(data); ++ } ++ }finally { ++ this.sendLock.unlockWrite(id); + } + +- if (this.chunkSendQueue.isEmpty()) { +- return; ++ long id2 = this.sendLock.readLock(); ++ try { ++ if (this.chunkSendQueue.isEmpty()) { ++ return; ++ } ++ }finally { ++ this.sendLock.unlockRead(id2); + } + + final int maxSends = this.getMaxConcurrentChunkSends(); + final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time; + for (;;) { +- if (this.chunkSendQueue.isEmpty()) { +- break; ++ ++ long id3 = this.sendLock.readLock(); ++ try { ++ if (this.chunkSendQueue.isEmpty()) { ++ break; ++ } ++ }finally { ++ this.sendLock.unlockRead(id3); + } ++ + final int currSends = concurrentChunkSends.get(); + if (currSends >= maxSends) { + break; +@@ -557,17 +590,29 @@ public final class PlayerChunkLoader { + + // send chunk + +- final PlayerLoaderData data = this.chunkSendQueue.removeFirst(); ++ PlayerLoaderData data; ++ ++ final long id4 = this.sendLock.writeLock(); ++ try { ++ data = this.chunkSendQueue.removeFirst(); ++ }finally { ++ this.sendLock.unlockWrite(id4); ++ } + + 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; ++ final long id5 = this.sendLock.readLock(); ++ try { ++ if (this.chunkSendQueue.isEmpty()) { ++ // nothing left ++ break; ++ } ++ continue; ++ }finally { ++ this.sendLock.unlockRead(id5); + } +- continue; + } + + if (!this.isChunkPlayerLoaded(queuedSend.chunkX, queuedSend.chunkZ)) { +@@ -575,7 +620,7 @@ public final class PlayerChunkLoader { + } + + data.nextChunkSendTarget = nextPlayerDeadline; +- this.chunkSendWaitQueue.add(data); ++ this.sendLock.writeLock(); + + synchronized (this.sendingChunkCounts) { + this.sendingChunkCounts.addTo(data, 1); +@@ -611,22 +656,39 @@ public final class PlayerChunkLoader { + // this interval ensures the rate is kept between ticks correctly + protected static final IntervalledCounter TICKET_ADDITION_COUNTER_LONG = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms + private void tryLoadChunks() { +- if (this.chunkLoadQueue.isEmpty()) { +- return; ++ final long id = this.loadLock.writeLock(); ++ try { ++ if (this.chunkLoadQueue.isEmpty()) { ++ return; ++ } ++ }finally { ++ this.loadLock.unlockWrite(id); + } + + final int maxLoads = this.getMaxChunkLoads(); + final long time = System.nanoTime(); + boolean updatedCounters = false; + for (;;) { +- final PlayerLoaderData data = this.chunkLoadQueue.pollFirst(); ++ PlayerLoaderData data; ++ ++ final long id1 = this.loadLock.writeLock(); ++ try { ++ data = this.chunkLoadQueue.pollFirst(); ++ }finally { ++ this.loadLock.unlock(id1); ++ } + + data.lastChunkLoad = time; + + final ChunkPriorityHolder queuedLoad = data.loadQueue.peekFirst(); + if (queuedLoad == null) { +- if (this.chunkLoadQueue.isEmpty()) { +- break; ++ long id2 = this.loadLock.writeLock(); ++ try { ++ if (this.chunkLoadQueue.isEmpty()) { ++ break; ++ } ++ }finally { ++ this.loadLock.unlockWrite(id2); + } + continue; + } +@@ -642,8 +704,12 @@ public final class PlayerChunkLoader { + if (this.isChunkPlayerLoaded(queuedLoad.chunkX, queuedLoad.chunkZ)) { + // already loaded! + data.loadQueue.pollFirst(); // already loaded so we just skip +- this.chunkLoadQueue.add(data); +- ++ final long id3 = this.loadLock.writeLock(); ++ try { ++ this.chunkLoadQueue.add(data); ++ }finally { ++ this.loadLock.unlockWrite(id3); ++ } + // ensure the chunk is queued to send + this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ); + continue; +@@ -675,7 +741,12 @@ public final class PlayerChunkLoader { + if (currentChunkLoads >= maxLoads || (GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate > 0 && (TICKET_ADDITION_COUNTER_SHORT.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate || TICKET_ADDITION_COUNTER_LONG.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate)) + || (GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate > 0.0 && (data.ticketAdditionCounterShort.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate || data.ticketAdditionCounterLong.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate))) { + // don't poll, we didn't load it +- this.chunkLoadQueue.add(data); ++ final long id4 = this.loadLock.writeLock(); ++ try { ++ this.chunkLoadQueue.add(data); ++ }finally { ++ this.loadLock.unlockWrite(id4); ++ } + break; + } + } +@@ -684,7 +755,12 @@ public final class PlayerChunkLoader { + data.loadQueue.pollFirst(); + + // now that we've polled we can re-add to load queue +- this.chunkLoadQueue.add(data); ++ final long id4 = this.loadLock.writeLock(); ++ try { ++ this.chunkLoadQueue.add(data); ++ }finally { ++ this.loadLock.unlockWrite(id4); ++ } + + // add necessary tickets to load chunk up to send-ready + for (int dz = -1; dz <= 1; ++dz) { +@@ -1110,13 +1186,23 @@ public final class PlayerChunkLoader { + }); + + // we're modifying loadQueue, must remove +- this.loader.chunkLoadQueue.remove(this); ++ final long id3 = this.loader.loadLock.writeLock(); ++ try { ++ this.loader.chunkLoadQueue.remove(this); ++ }finally { ++ this.loader.loadLock.unlockWrite(id3); ++ } + + this.loadQueue.clear(); + this.loadQueue.addAll(loadQueue); + +- // must re-add +- this.loader.chunkLoadQueue.add(this); ++ final long id4 = this.loader.loadLock.writeLock(); ++ try { ++ // must re-add ++ this.loader.chunkLoadQueue.add(this); ++ }finally { ++ this.loader.loadLock.unlockWrite(id4); ++ } + + // update the chunk center + // this must be done last so that the client does not ignore any of our unload chunk packets +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..36d3b0d0e1e716cb0e5a2e98c37a834776e26c20 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 +@@ -31,6 +31,8 @@ import java.util.Iterator; + import java.util.List; + import java.util.NoSuchElementException; + import java.util.UUID; ++import java.util.concurrent.locks.ReadWriteLock; ++import java.util.concurrent.locks.ReentrantReadWriteLock; + import java.util.concurrent.locks.StampedLock; + import java.util.function.Consumer; + import java.util.function.Predicate; +@@ -45,14 +47,17 @@ public final class EntityLookup implements LevelEntityGetter { + + public final ServerLevel world; + ++ private final StampedLock entityByLock = new StampedLock(); + private final StampedLock stateLock = new StampedLock(); ++ ++ private final ReadWriteLock paraEntityLock = new ReentrantReadWriteLock(false); //Hearse -- Just for multithreaded entity ticking ++ + protected final Long2ObjectOpenHashMap regions = new Long2ObjectOpenHashMap<>(128, 0.5f); + + private final int minSection; // inclusive + private final int maxSection; // inclusive + private final LevelCallback worldCallback; + +- private final StampedLock entityByLock = new StampedLock(); + private final Int2ReferenceOpenHashMap entityById = new Int2ReferenceOpenHashMap<>(); + private final Object2ReferenceOpenHashMap entityByUUID = new Object2ReferenceOpenHashMap<>(); + private final EntityList accessibleEntities = new EntityList(); +@@ -75,52 +80,61 @@ public final class EntityLookup implements LevelEntityGetter { + @Nullable + @Override + public Entity get(final int id) { +- final long attempt = this.entityByLock.tryOptimisticRead(); +- if (attempt != 0L) { +- try { +- final Entity ret = this.entityById.get(id); ++ this.paraEntityLock.readLock().lock();//Hearse ++ try { ++ final long attempt = this.entityByLock.tryOptimisticRead(); ++ if (attempt != 0L) { ++ try { ++ final Entity ret = this.entityById.get(id); + +- if (this.entityByLock.validate(attempt)) { +- return maskNonAccessible(ret); ++ if (this.entityByLock.validate(attempt)) { ++ return maskNonAccessible(ret); ++ } ++ } catch (final Error error) { ++ throw error; ++ } catch (final Throwable thr) { ++ // ignore + } +- } catch (final Error error) { +- throw error; +- } catch (final Throwable thr) { +- // ignore + } +- } + +- this.entityByLock.readLock(); +- try { +- return maskNonAccessible(this.entityById.get(id)); +- } finally { +- this.entityByLock.tryUnlockRead(); ++ this.entityByLock.readLock(); ++ try { ++ return maskNonAccessible(this.entityById.get(id)); ++ } finally { ++ this.entityByLock.tryUnlockRead(); ++ } ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse + } + } + + @Nullable + @Override + public Entity get(final UUID id) { +- final long attempt = this.entityByLock.tryOptimisticRead(); +- if (attempt != 0L) { +- try { +- final Entity ret = this.entityByUUID.get(id); +- +- if (this.entityByLock.validate(attempt)) { +- return maskNonAccessible(ret); ++ this.paraEntityLock.readLock().lock();//Hearse ++ try { ++ final long attempt = this.entityByLock.tryOptimisticRead(); ++ if (attempt != 0L) { ++ try { ++ final Entity ret = this.entityByUUID.get(id); ++ if (this.entityByLock.validate(attempt)) { ++ return maskNonAccessible(ret); ++ } ++ } catch (final Error error) { ++ throw error; ++ } catch (final Throwable thr) { ++ // ignore + } +- } catch (final Error error) { +- throw error; +- } catch (final Throwable thr) { +- // ignore + } +- } + +- this.entityByLock.readLock(); +- try { +- return maskNonAccessible(this.entityByUUID.get(id)); +- } finally { +- this.entityByLock.tryUnlockRead(); ++ this.entityByLock.readLock(); ++ try { ++ return maskNonAccessible(this.entityByUUID.get(id)); ++ } finally { ++ this.entityByLock.tryUnlockRead(); ++ } ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -129,7 +143,12 @@ public final class EntityLookup implements LevelEntityGetter { + } + + public String getDebugInfo() { +- return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + this.regions.size(); ++ this.paraEntityLock.readLock();//Hearse ++ try { ++ return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + this.regions.size(); ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse ++ } + } + + static final class ArrayIterable implements Iterable { +@@ -166,12 +185,12 @@ public final class EntityLookup implements LevelEntityGetter { + } + + @Override +- public boolean hasNext() { ++ public synchronized boolean hasNext() { + return this.off < this.length; + } + + @Override +- public T next() { ++ public synchronized T next() { + if (this.off >= this.length) { + throw new NoSuchElementException(); + } +@@ -179,7 +198,7 @@ public final class EntityLookup implements LevelEntityGetter { + } + + @Override +- public void remove() { ++ public synchronized void remove() { + throw new UnsupportedOperationException(); + } + } +@@ -187,20 +206,30 @@ public final class EntityLookup implements LevelEntityGetter { + + @Override + public Iterable getAll() { +- return new ArrayIterable<>(this.accessibleEntities.getRawData(), 0, this.accessibleEntities.size()); ++ this.paraEntityLock.readLock().lock();//Hearse ++ try { ++ return new ArrayIterable<>(this.accessibleEntities.getRawData(), 0, this.accessibleEntities.size()); ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse ++ } + } + + @Override + public void get(final EntityTypeTest filter, final AbortableIterationConsumer action) { +- for (final Entity entity : this.entityById.values()) { +- final Visibility visibility = EntityLookup.getEntityStatus(entity); +- if (!visibility.isAccessible()) { +- continue; +- } +- final U casted = filter.tryCast(entity); +- if (casted != null && action.accept(casted).shouldAbort()) { +- break; ++ this.paraEntityLock.readLock().lock();//Hearse ++ try { ++ for (final Entity entity : this.entityById.values()) { ++ final Visibility visibility = EntityLookup.getEntityStatus(entity); ++ if (!visibility.isAccessible()) { ++ continue; ++ } ++ final U casted = filter.tryCast(entity); ++ if (casted != null && action.accept(casted).shouldAbort()) { ++ break; ++ } + } ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -208,8 +237,8 @@ public final class EntityLookup implements LevelEntityGetter { + public void get(final AABB box, final Consumer action) { + List entities = new ArrayList<>(); + this.getEntitiesWithoutDragonParts(null, box, entities, null); +- for (int i = 0, len = entities.size(); i < len; ++i) { +- action.accept(entities.get(i)); ++ for (Entity entity : entities) { ++ action.accept(entity); + } + } + +@@ -217,8 +246,8 @@ public final class EntityLookup implements LevelEntityGetter { + public void get(final EntityTypeTest filter, final AABB box, final AbortableIterationConsumer action) { + List entities = new ArrayList<>(); + this.getEntitiesWithoutDragonParts(null, box, entities, null); +- for (int i = 0, len = entities.size(); i < len; ++i) { +- final U casted = filter.tryCast(entities.get(i)); ++ for (Entity entity : entities) { ++ final U casted = filter.tryCast(entity); + if (casted != null && action.accept(casted).shouldAbort()) { + break; + } +@@ -228,75 +257,55 @@ public final class EntityLookup implements LevelEntityGetter { + public void entityStatusChange(final Entity entity, final ChunkEntitySlices slices, final Visibility oldVisibility, final Visibility newVisibility, final boolean moved, + final boolean created, final boolean destroyed) { + TickThread.ensureTickThread(entity, "Entity status change must only happen on the main thread"); +- +- if (entity.updatingSectionStatus) { +- // recursive status update +- LOGGER.error("Cannot recursively update entity chunk status for entity " + entity, new Throwable()); +- return; +- } +- +- final boolean entityStatusUpdateBefore = slices == null ? false : slices.startPreventingStatusUpdates(); +- +- if (entityStatusUpdateBefore) { +- LOGGER.error("Cannot update chunk status for entity " + entity + " since entity chunk (" + slices.chunkX + "," + slices.chunkZ + ") is receiving update", new Throwable()); +- return; +- } +- ++ this.paraEntityLock.writeLock().lock();//Hearse + try { + final Boolean ticketBlockBefore = this.world.chunkTaskScheduler.chunkHolderManager.blockTicketUpdates(); + try { +- entity.updatingSectionStatus = true; +- try { +- if (created) { +- EntityLookup.this.worldCallback.onCreated(entity); +- } ++ if (created) { ++ EntityLookup.this.worldCallback.onCreated(entity); ++ } + +- if (oldVisibility == newVisibility) { +- if (moved && newVisibility.isAccessible()) { +- EntityLookup.this.worldCallback.onSectionChange(entity); +- } +- return; ++ if (oldVisibility == newVisibility) { ++ if (moved && newVisibility.isAccessible()) { ++ EntityLookup.this.worldCallback.onSectionChange(entity); + } ++ return; ++ } + +- if (newVisibility.ordinal() > oldVisibility.ordinal()) { +- // status upgrade +- if (!oldVisibility.isAccessible() && newVisibility.isAccessible()) { +- this.accessibleEntities.add(entity); +- EntityLookup.this.worldCallback.onTrackingStart(entity); +- } +- +- if (!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); + } + } finally { +- if (slices != null) { +- slices.stopPreventingStatusUpdates(false); +- } ++ this.paraEntityLock.writeLock().lock();//Hearse + } + } + +@@ -341,53 +350,53 @@ public final class EntityLookup implements LevelEntityGetter { + final int sectionZ = pos.getZ() >> 4; + TickThread.ensureTickThread(this.world, sectionX, sectionZ, "Cannot add entity off-main thread"); + +- if (entity.isRemoved()) { +- LOGGER.warn("Refusing to add removed entity: " + 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); ++ this.paraEntityLock.writeLock().lock();//Hearse ++ try { + if (entity.isRemoved()) { +- // removed from checkDupeUUID call ++ LOGGER.warn("Refusing to add removed entity: " + entity); + return false; + } +- } + +- this.entityByLock.writeLock(); +- try { +- if (this.entityById.containsKey(entity.getId())) { +- LOGGER.warn("Entity id already exists: " + entity.getId() + ", mapped to " + this.entityById.get(entity.getId()) + ", can't add " + entity); +- return false; ++ if (fromDisk) { ++ ChunkSystem.onEntityPreAdd(this.world, entity); ++ if (entity.isRemoved()) { ++ // removed from checkDupeUUID call ++ return false; ++ } + } +- if (this.entityByUUID.containsKey(entity.getUUID())) { +- LOGGER.warn("Entity uuid already exists: " + entity.getUUID() + ", mapped to " + this.entityByUUID.get(entity.getUUID()) + ", can't add " + entity); +- return false; ++ ++ this.entityByLock.writeLock(); ++ try { ++ if (this.entityById.containsKey(entity.getId())) { ++ LOGGER.warn("Entity id already exists: " + entity.getId() + ", mapped to " + this.entityById.get(entity.getId()) + ", can't add " + entity); ++ return false; ++ } ++ if (this.entityByUUID.containsKey(entity.getUUID())) { ++ LOGGER.warn("Entity uuid already exists: " + entity.getUUID() + ", mapped to " + this.entityByUUID.get(entity.getUUID()) + ", can't add " + entity); ++ return false; ++ } ++ this.entityById.put(entity.getId(), entity); ++ this.entityByUUID.put(entity.getUUID(), entity); ++ } finally { ++ this.entityByLock.tryUnlockWrite(); + } +- this.entityById.put(entity.getId(), entity); +- this.entityByUUID.put(entity.getUUID(), entity); +- } finally { +- this.entityByLock.tryUnlockWrite(); +- } + +- entity.sectionX = sectionX; +- entity.sectionY = sectionY; +- entity.sectionZ = sectionZ; +- final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ); +- if (!slices.addEntity(entity, sectionY)) { +- LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")"); +- } ++ entity.sectionX = sectionX; ++ entity.sectionY = sectionY; ++ entity.sectionZ = sectionZ; ++ final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ); ++ if (!slices.addEntity(entity, sectionY)) { ++ LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")"); ++ } + +- entity.setLevelCallback(new EntityCallback(entity)); ++ entity.setLevelCallback(new EntityCallback(entity)); + +- this.entityStatusChange(entity, slices, Visibility.HIDDEN, getEntityStatus(entity), false, !fromDisk, false); ++ this.entityStatusChange(entity, slices, Visibility.HIDDEN, getEntityStatus(entity), false, !fromDisk, false); + +- return true; ++ return true; ++ }finally { ++ this.paraEntityLock.writeLock().unlock();//Hearse ++ } + } + + private void removeEntity(final Entity entity) { +@@ -398,27 +407,32 @@ public final class EntityLookup implements LevelEntityGetter { + if (!entity.isRemoved()) { + throw new IllegalStateException("Only call Entity#setRemoved to remove an entity"); + } +- final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ); +- // all entities should be in a chunk +- if (slices == null) { +- LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")"); +- } else { +- if (!slices.removeEntity(entity, sectionY)) { +- LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")"); +- } +- } +- entity.sectionX = entity.sectionY = entity.sectionZ = Integer.MIN_VALUE; +- +- this.entityByLock.writeLock(); ++ this.paraEntityLock.writeLock().lock();//Hearse + try { +- if (!this.entityById.remove(entity.getId(), entity)) { +- LOGGER.warn("Failed to remove entity " + entity + " by id, current entity mapped: " + this.entityById.get(entity.getId())); ++ final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ); ++ // all entities should be in a chunk ++ if (slices == null) { ++ LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")"); ++ } else { ++ if (!slices.removeEntity(entity, sectionY)) { ++ LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")"); ++ } + } +- if (!this.entityByUUID.remove(entity.getUUID(), entity)) { +- LOGGER.warn("Failed to remove entity " + entity + " by uuid, current entity mapped: " + this.entityByUUID.get(entity.getUUID())); ++ entity.sectionX = entity.sectionY = entity.sectionZ = Integer.MIN_VALUE; ++ ++ this.entityByLock.writeLock(); ++ try { ++ if (!this.entityById.remove(entity.getId(), entity)) { ++ LOGGER.warn("Failed to remove entity " + entity + " by id, current entity mapped: " + this.entityById.get(entity.getId())); ++ } ++ if (!this.entityByUUID.remove(entity.getUUID(), entity)) { ++ LOGGER.warn("Failed to remove entity " + entity + " by uuid, current entity mapped: " + this.entityByUUID.get(entity.getUUID())); ++ } ++ } finally { ++ this.entityByLock.tryUnlockWrite(); + } +- } finally { +- this.entityByLock.tryUnlockWrite(); ++ }finally { ++ this.paraEntityLock.writeLock().unlock();//Hearse + } + } + +@@ -441,22 +455,27 @@ public final class EntityLookup implements LevelEntityGetter { + // ensure the old section is owned by this tick thread + TickThread.ensureTickThread(this.world, entity.sectionX, entity.sectionZ, "Cannot move entity off-main"); + +- final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ); +- final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ); ++ this.paraEntityLock.writeLock().lock();//Hearse ++ try { ++ final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ); ++ final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ); + +- if (!old.removeEntity(entity, entity.sectionY)) { +- LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section"); +- } ++ if (!old.removeEntity(entity, entity.sectionY)) { ++ LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section"); ++ } + +- if (!slices.addEntity(entity, newSectionY)) { +- LOGGER.warn("Could not add entity " + entity + " to its new chunk section (" + newSectionX + "," + newSectionY + "," + newSectionZ + ") as it is already contained in the section"); +- } ++ if (!slices.addEntity(entity, newSectionY)) { ++ LOGGER.warn("Could not add entity " + entity + " to its new chunk section (" + newSectionX + "," + newSectionY + "," + newSectionZ + ") as it is already contained in the section"); ++ } + +- entity.sectionX = newSectionX; +- entity.sectionY = newSectionY; +- entity.sectionZ = newSectionZ; ++ entity.sectionX = newSectionX; ++ entity.sectionY = newSectionY; ++ entity.sectionZ = newSectionZ; + +- return slices; ++ return slices; ++ }finally { ++ this.paraEntityLock.writeLock().unlock();//Hearse ++ } + } + + public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List into, final Predicate predicate) { +@@ -470,31 +489,36 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ this.paraEntityLock.readLock().lock();//Hearse ++ try { ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- if (region == null) { +- continue; +- } ++ if (region == null) { ++ continue; ++ } + +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; ++ } + +- chunk.getEntitiesWithoutDragonParts(except, box, into, predicate); ++ chunk.getEntitiesWithoutDragonParts(except, box, into, predicate); ++ } + } + } + } ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -509,31 +533,36 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ this.paraEntityLock.readLock().lock();//Hearse ++ try { ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- if (region == null) { +- continue; +- } ++ if (region == null) { ++ continue; ++ } + +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; ++ } + +- chunk.getEntities(except, box, into, predicate); ++ chunk.getEntities(except, box, into, predicate); ++ } + } + } + } ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -548,31 +577,36 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ this.paraEntityLock.readLock().lock();//Hearse ++ try { ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- if (region == null) { +- continue; +- } ++ if (region == null) { ++ continue; ++ } + +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; ++ } + +- chunk.getHardCollidingEntities(except, box, into, predicate); ++ chunk.getHardCollidingEntities(except, box, into, predicate); ++ } + } + } + } ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -588,31 +622,36 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ this.paraEntityLock.readLock().lock();//Hearse ++ try { ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- if (region == null) { +- continue; +- } ++ if (region == null) { ++ continue; ++ } + +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; ++ } + +- chunk.getEntities(type, box, (List)into, (Predicate)predicate); ++ chunk.getEntities(type, box, (List)into, (Predicate)predicate); ++ } + } + } + } ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -628,31 +667,36 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ this.paraEntityLock.readLock().unlock();//Hearse ++ try { ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- if (region == null) { +- continue; +- } ++ if (region == null) { ++ continue; ++ } + +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; ++ } + +- chunk.getEntities(clazz, except, box, into, predicate); ++ chunk.getEntities(clazz, except, box, into, predicate); ++ } + } + } + } ++ }finally { ++ this.paraEntityLock.readLock().unlock();//Hearse + } + } + +diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java +index 830d863cd9665d58875bfa5ca2bcd22f89ab2d49..15eeea40bb7a44470f6f3f0e2473cb451812eec1 100644 +--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java ++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java +@@ -13,14 +13,10 @@ import io.papermc.paper.util.CoordinateUtils; + import io.papermc.paper.util.TickThread; + import io.papermc.paper.util.misc.Delayed8WayDistancePropagator2D; + import io.papermc.paper.world.ChunkEntitySlices; +-import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap; +-import it.unimi.dsi.fastutil.longs.Long2IntMap; +-import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +-import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +-import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +-import it.unimi.dsi.fastutil.longs.LongArrayList; +-import it.unimi.dsi.fastutil.longs.LongIterator; ++import it.unimi.dsi.fastutil.longs.*; + import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; ++import it.unimi.dsi.fastutil.objects.ObjectSortedSet; ++import it.unimi.dsi.fastutil.objects.ObjectSortedSets; + import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; + import net.minecraft.nbt.CompoundTag; + import io.papermc.paper.chunk.system.ChunkSystem; +@@ -39,13 +35,8 @@ import org.bukkit.plugin.Plugin; + import org.slf4j.Logger; + import java.io.IOException; + import java.text.DecimalFormat; +-import java.util.ArrayDeque; +-import java.util.ArrayList; +-import java.util.Collection; +-import java.util.Collections; +-import java.util.Iterator; +-import java.util.List; +-import java.util.Objects; ++import java.util.*; ++import java.util.concurrent.ConcurrentLinkedDeque; + import java.util.concurrent.TimeUnit; + import java.util.concurrent.atomic.AtomicBoolean; + import java.util.concurrent.atomic.AtomicReference; +@@ -67,16 +58,16 @@ public final class ChunkHolderManager { + final ReentrantLock ticketLock = new ReentrantLock(); + + private final SWMRLong2ObjectHashTable chunkHolders = new SWMRLong2ObjectHashTable<>(16384, 0.25f); +- private final Long2ObjectOpenHashMap>> tickets = new Long2ObjectOpenHashMap<>(8192, 0.25f); ++ private final Long2ObjectMap>> tickets = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(8192, 0.25f)); + // what a disaster of a name + // this is a map of removal tick to a map of chunks and the number of tickets a chunk has that are to expire that tick +- private final Long2ObjectOpenHashMap removeTickToChunkExpireTicketCount = new Long2ObjectOpenHashMap<>(); ++ private final Long2ObjectMap removeTickToChunkExpireTicketCount = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); + private final ServerLevel world; + private final ChunkTaskScheduler taskScheduler; + private long currentTick; + +- private final ArrayDeque pendingFullLoadUpdate = new ArrayDeque<>(); +- private final ObjectRBTreeSet autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> { ++ private final Deque pendingFullLoadUpdate = new ConcurrentLinkedDeque<>(); ++ private final ObjectSortedSet autoSaveQueue = ObjectSortedSets.synchronize(new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> { + if (c1 == c2) { + return 0; + } +@@ -95,7 +86,7 @@ public final class ChunkHolderManager { + } + + return Long.compare(coord1, coord2); +- }); ++ })); + + public ChunkHolderManager(final ServerLevel world, final ChunkTaskScheduler taskScheduler) { + this.world = world; +@@ -311,7 +302,7 @@ public final class ChunkHolderManager { + public Long2ObjectOpenHashMap>> getTicketsCopy() { + this.ticketLock.lock(); + try { +- return this.tickets.clone(); ++ return new Long2ObjectOpenHashMap<>(this.tickets); + } finally { + this.ticketLock.unlock(); + } +@@ -784,7 +775,7 @@ public final class ChunkHolderManager { + } + if (!TickThread.isTickThread()) { + this.taskScheduler.scheduleChunkTask(() -> { +- final ArrayDeque pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate; ++ final Deque pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate; + for (int i = 0, len = changedFullStatus.size(); i < len; ++i) { + pendingFullLoadUpdate.add(changedFullStatus.get(i)); + } +@@ -792,7 +783,7 @@ public final class ChunkHolderManager { + ChunkHolderManager.this.processPendingFullUpdate(); + }, PrioritisedExecutor.Priority.HIGHEST); + } else { +- final ArrayDeque pendingFullLoadUpdate = this.pendingFullLoadUpdate; ++ final Deque pendingFullLoadUpdate = this.pendingFullLoadUpdate; + for (int i = 0, len = changedFullStatus.size(); i < len; ++i) { + pendingFullLoadUpdate.add(changedFullStatus.get(i)); + } +@@ -1039,7 +1030,7 @@ public final class ChunkHolderManager { + + // only call on tick thread + protected final boolean processPendingFullUpdate() { +- final ArrayDeque pendingFullLoadUpdate = this.pendingFullLoadUpdate; ++ final Deque pendingFullLoadUpdate = this.pendingFullLoadUpdate; + + boolean ret = false; + +diff --git a/src/main/java/io/papermc/paper/util/CachedLists.java b/src/main/java/io/papermc/paper/util/CachedLists.java +index e08f4e39db4ee3fed62e37364d17dcc5c5683504..f543462240e744e3424d302eb62be46c70ef9377 100644 +--- a/src/main/java/io/papermc/paper/util/CachedLists.java ++++ b/src/main/java/io/papermc/paper/util/CachedLists.java +@@ -1,57 +1,22 @@ + package io.papermc.paper.util; + ++import com.google.common.collect.Lists; + import net.minecraft.world.entity.Entity; + import net.minecraft.world.phys.AABB; +-import org.bukkit.Bukkit; +-import org.bukkit.craftbukkit.util.UnsafeList; + import java.util.List; + + public final class CachedLists { +- +- // Paper start - optimise collisions +- static final UnsafeList TEMP_COLLISION_LIST = new UnsafeList<>(1024); +- static boolean tempCollisionListInUse; +- +- public static UnsafeList getTempCollisionList() { +- if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) { +- return new UnsafeList<>(16); +- } +- tempCollisionListInUse = true; +- return TEMP_COLLISION_LIST; +- } +- +- public static void returnTempCollisionList(List list) { +- if (list != TEMP_COLLISION_LIST) { +- return; +- } +- ((UnsafeList)list).setSize(0); +- tempCollisionListInUse = false; ++ public static List getTempCollisionList() { ++ return Lists.newCopyOnWriteArrayList(); + } + +- static final UnsafeList TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024); +- static boolean tempGetEntitiesListInUse; ++ public static void returnTempCollisionList(List list) {} + +- public static UnsafeList getTempGetEntitiesList() { +- if (!Bukkit.isPrimaryThread() || tempGetEntitiesListInUse) { +- return new UnsafeList<>(16); +- } +- tempGetEntitiesListInUse = true; +- return TEMP_GET_ENTITIES_LIST; ++ public static List getTempGetEntitiesList() { ++ return Lists.newCopyOnWriteArrayList(); + } + +- public static void returnTempGetEntitiesList(List list) { +- if (list != TEMP_GET_ENTITIES_LIST) { +- return; +- } +- ((UnsafeList)list).setSize(0); +- tempGetEntitiesListInUse = false; +- } +- // Paper end - optimise collisions ++ public static void returnTempGetEntitiesList(List list) {} + +- public static void reset() { +- // Paper start - optimise collisions +- TEMP_COLLISION_LIST.completeReset(); +- TEMP_GET_ENTITIES_LIST.completeReset(); +- // Paper end - optimise collisions +- } ++ public static void reset() {} + } +diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +index f597d65d56964297eeeed6c7e77703764178fee0..f6c8dccb7f7cd287f1ebdf46481365b952baa891 100644 +--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java ++++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +@@ -22,6 +22,7 @@ import java.util.ArrayList; + import java.util.Arrays; + import java.util.Iterator; + import java.util.List; ++import java.util.concurrent.locks.StampedLock; + import java.util.function.Predicate; + + public final class ChunkEntitySlices { +@@ -168,17 +169,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; + +@@ -378,6 +368,7 @@ public final class ChunkEntitySlices { + protected final long[] nonEmptyBitset; + protected final BasicEntityList[] entitiesBySection; + protected int count; ++ private final StampedLock listLock = new StampedLock();//Hearse + + public EntityCollectionBySection(final ChunkEntitySlices manager) { + this.manager = manager; +@@ -389,212 +380,242 @@ public final class ChunkEntitySlices { + } + + public void addEntity(final Entity entity, final int sectionIndex) { +- BasicEntityList list = this.entitiesBySection[sectionIndex]; ++ final long id = this.listLock.writeLock(); ++ try { ++ BasicEntityList list = this.entitiesBySection[sectionIndex]; + +- if (list != null && list.has(entity)) { +- return; +- } ++ if (list != null && list.has(entity)) { ++ return; ++ } + +- if (list == null) { +- this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>(); +- this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1))); +- } ++ if (list == null) { ++ this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>(); ++ this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1))); ++ } + +- list.add(entity); +- ++this.count; ++ list.add(entity); ++ ++this.count; ++ }finally { ++ this.listLock.unlockWrite(id); ++ } + } + + public void removeEntity(final Entity entity, final int sectionIndex) { +- final BasicEntityList list = this.entitiesBySection[sectionIndex]; ++ final long id = this.listLock.writeLock(); ++ try { ++ final BasicEntityList list = this.entitiesBySection[sectionIndex]; + +- if (list == null || !list.remove(entity)) { +- return; +- } ++ if (list == null || !list.remove(entity)) { ++ return; ++ } + +- --this.count; ++ --this.count; + +- if (list.isEmpty()) { +- this.entitiesBySection[sectionIndex] = null; +- this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1))); ++ if (list.isEmpty()) { ++ this.entitiesBySection[sectionIndex] = null; ++ this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1))); ++ } ++ }finally { ++ this.listLock.unlockWrite(id); + } + } + + public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { +- if (this.count == 0) { +- return; +- } ++ final long id = this.listLock.readLock(); ++ try { ++ if (this.count == 0) { ++ return; ++ } + +- final int minSection = this.manager.minSection; +- final int maxSection = this.manager.maxSection; ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; + +- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); +- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + +- final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; + +- for (int section = min; section <= max; ++section) { +- final BasicEntityList list = entitiesBySection[section - minSection]; ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; + +- if (list == null) { +- continue; +- } ++ if (list == null) { ++ continue; ++ } + +- final Entity[] storage = list.storage; ++ final Entity[] storage = list.storage; + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; + +- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { +- continue; +- } ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- if (predicate != null && !predicate.test(entity)) { +- continue; +- } ++ if (predicate != null && !predicate.test(entity)) { ++ continue; ++ } + +- into.add(entity); ++ into.add(entity); ++ } + } ++ }finally { ++ this.listLock.unlockRead(id); + } + } + + public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List into, + final Predicate predicate) { +- if (this.count == 0) { +- return; +- } ++ final long id = this.listLock.readLock(); ++ try { ++ if (this.count == 0) { ++ return; ++ } + +- final int minSection = this.manager.minSection; +- final int maxSection = this.manager.maxSection; ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; + +- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); +- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + +- final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; + +- for (int section = min; section <= max; ++section) { +- final BasicEntityList list = entitiesBySection[section - minSection]; ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; + +- if (list == null) { +- continue; +- } ++ if (list == null) { ++ continue; ++ } + +- final Entity[] storage = list.storage; ++ final Entity[] storage = list.storage; + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; + +- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { +- continue; +- } ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- if (predicate == null || predicate.test(entity)) { +- into.add(entity); +- } // else: continue to test the ender dragon parts ++ if (predicate == null || predicate.test(entity)) { ++ into.add(entity); ++ } // else: continue to test the ender dragon parts + +- if (entity instanceof EnderDragon) { +- for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) { +- if (part == except || !part.getBoundingBox().intersects(box)) { +- continue; +- } ++ if (entity instanceof EnderDragon) { ++ for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) { ++ if (part == except || !part.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- if (predicate != null && !predicate.test(part)) { +- continue; +- } ++ if (predicate != null && !predicate.test(part)) { ++ continue; ++ } + +- into.add(part); +- } +- } +- } +- } ++ into.add(part); ++ } ++ } ++ } ++ } ++ }finally { ++ this.listLock.unlockRead(id); ++ } + } + + public void getEntitiesWithEnderDragonParts(final Entity except, final Class clazz, final AABB box, final List into, + final Predicate predicate) { +- if (this.count == 0) { +- return; +- } ++ final long id = this.listLock.readLock(); ++ try { ++ if (this.count == 0) { ++ return; ++ } + +- final int minSection = this.manager.minSection; +- final int maxSection = this.manager.maxSection; ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; + +- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); +- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + +- final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; + +- for (int section = min; section <= max; ++section) { +- final BasicEntityList list = entitiesBySection[section - minSection]; ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; + +- if (list == null) { +- continue; +- } ++ if (list == null) { ++ continue; ++ } + +- final Entity[] storage = list.storage; ++ final Entity[] storage = list.storage; + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; + +- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { +- continue; +- } ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- if (predicate == null || predicate.test(entity)) { +- into.add(entity); +- } // else: continue to test the ender dragon parts ++ if (predicate == null || predicate.test(entity)) { ++ into.add(entity); ++ } // else: continue to test the ender dragon parts + +- if (entity instanceof EnderDragon) { +- for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) { +- if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) { +- continue; +- } ++ if (entity instanceof EnderDragon) { ++ for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) { ++ if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) { ++ continue; ++ } + +- if (predicate != null && !predicate.test(part)) { +- continue; +- } ++ if (predicate != null && !predicate.test(part)) { ++ continue; ++ } + +- into.add(part); ++ into.add(part); ++ } + } + } + } ++ }finally { ++ this.listLock.unlockRead(id); + } + } + + public void getEntities(final EntityType type, final AABB box, final List into, + final Predicate predicate) { +- if (this.count == 0) { +- return; +- } ++ final long id = this.listLock.readLock(); ++ try { ++ if (this.count == 0) { ++ return; ++ } + +- final int minSection = this.manager.minSection; +- final int maxSection = this.manager.maxSection; ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; + +- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); +- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + +- final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; + +- for (int section = min; section <= max; ++section) { +- final BasicEntityList list = entitiesBySection[section - minSection]; ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; + +- if (list == null) { +- continue; +- } ++ if (list == null) { ++ continue; ++ } + +- final Entity[] storage = list.storage; ++ final Entity[] storage = list.storage; + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; + +- if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) { +- continue; +- } ++ if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- if (predicate != null && !predicate.test((T)entity)) { +- continue; +- } ++ if (predicate != null && !predicate.test((T)entity)) { ++ continue; ++ } + +- into.add((T)entity); ++ into.add((T)entity); ++ } + } ++ }finally { ++ this.listLock.unlockRead(id); + } + } + } +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 8f3f7a4c2429dd3c9060a1d2f412e41a5448bf4c..99912ae883227e1c0a0adc1edbb4c4baee4fe2bc 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -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/patches/server/0041-Hearse-Patches.patch b/patches/server/0041-Hearse-Patches.patch deleted file mode 100644 index d78f6742..00000000 --- a/patches/server/0041-Hearse-Patches.patch +++ /dev/null @@ -1,8150 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BuildTools -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 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 { - -- protected final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f); -- { -- this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE); -- } -- -- protected static final Entity[] EMPTY_LIST = new Entity[0]; -- -- protected Entity[] entities = EMPTY_LIST; -- protected int count; -+ private final ObjectList objectList = ObjectLists.synchronize(new ObjectArrayList<>()); - - public int size() { -- return this.count; -+ return this.objectList.size(); -+ } -+ -+ public boolean 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 iterator() { -- return new Iterator() { -- -- Entity lastRet; -- int current; -- -- @Override -- public boolean hasNext() { -- return this.current < EntityList.this.count; -- } -- -- @Override -- public Entity next() { -- if (this.current >= EntityList.this.count) { -- throw new NoSuchElementException(); -- } -- return this.lastRet = EntityList.this.entities[this.current++]; -- } -- -- @Override -- public void remove() { -- final Entity lastRet = this.lastRet; -- -- if (lastRet == null) { -- throw new IllegalStateException(); -- } -- this.lastRet = null; -- -- EntityList.this.remove(lastRet); -- --this.current; -- } -- }; -+ return this.objectList.iterator(); - } - } -diff --git a/src/main/java/io/papermc/paper/chunk/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 playerMap = new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f); -- protected final ReferenceLinkedOpenHashSet chunkSendQueue = new ReferenceLinkedOpenHashSet<>(512, 0.7f); -+ protected final Reference2ObjectMap playerMap = Reference2ObjectMaps.synchronize(new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f)); -+ protected final Deque chunkSendQueue = new ConcurrentLinkedDeque<>(); - -- protected final TreeSet chunkLoadQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> { -+ protected final NavigableSet 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 chunkSendWaitQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> { -+ protected final NavigableSet 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 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 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 loadQueue = new ArrayDeque<>(); -- protected final LongOpenHashSet sentChunks = new LongOpenHashSet(); -- protected final LongOpenHashSet chunksToBeSent = new LongOpenHashSet(); -+ protected final Deque loadQueue = new ConcurrentLinkedDeque<>(); -+ protected final LongSet sentChunks = new ConcurrentLongLinkedOpenHashSet(); -+ protected final LongSet chunksToBeSent = new ConcurrentLongLinkedOpenHashSet(); - -- protected final TreeSet sendQueue = new TreeSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> { -+ protected final NavigableSet 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 { - public final ServerLevel world; - - private final StampedLock stateLock = new StampedLock(); -- protected final Long2ObjectOpenHashMap regions = new Long2ObjectOpenHashMap<>(128, 0.5f); -+ protected final Long2ObjectMap regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f)); - - private final int minSection; // inclusive - private final int maxSection; // inclusive - private final LevelCallback worldCallback; - - private final StampedLock entityByLock = new StampedLock(); -- private final Int2ReferenceOpenHashMap entityById = new Int2ReferenceOpenHashMap<>(); -- private final Object2ReferenceOpenHashMap entityByUUID = new Object2ReferenceOpenHashMap<>(); -+ private final Map entityById = Int2ReferenceMaps.synchronize(new Int2ReferenceOpenHashMap<>()); -+ private final Object2ReferenceMap entityByUUID = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenHashMap<>()); - private final EntityList accessibleEntities = new EntityList(); - - public EntityLookup(final ServerLevel world, final LevelCallback worldCallback) { -@@ -208,8 +211,8 @@ public final class EntityLookup implements LevelEntityGetter { - public void get(final AABB box, final Consumer action) { - List entities = new ArrayList<>(); - this.getEntitiesWithoutDragonParts(null, box, entities, null); -- for (int i = 0, len = entities.size(); i < len; ++i) { -- action.accept(entities.get(i)); -+ for (Entity entity : entities) { -+ action.accept(entity); - } - } - -@@ -217,8 +220,8 @@ public final class EntityLookup implements LevelEntityGetter { - public void get(final EntityTypeTest filter, final AABB box, final AbortableIterationConsumer action) { - List entities = new ArrayList<>(); - this.getEntitiesWithoutDragonParts(null, box, entities, null); -- for (int i = 0, len = entities.size(); i < len; ++i) { -- final U casted = filter.tryCast(entities.get(i)); -+ for (Entity entity : entities) { -+ final U casted = filter.tryCast(entity); - if (casted != null && action.accept(casted).shouldAbort()) { - break; - } -@@ -231,72 +234,59 @@ public final class EntityLookup implements LevelEntityGetter { - - if (entity.updatingSectionStatus) { - // recursive status update -- LOGGER.error("Cannot recursively update entity chunk status for entity " + entity, new Throwable()); -- 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 { - } - - public void addLegacyChunkEntities(final List 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 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 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 { - 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 TEMP_COLLISION_LIST = new UnsafeList<>(1024); -- static boolean tempCollisionListInUse; -- -- public static UnsafeList getTempCollisionList() { -- if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) { -- return new UnsafeList<>(16); -- } -- tempCollisionListInUse = true; -- return TEMP_COLLISION_LIST; -- } -- -- public static void returnTempCollisionList(List list) { -- if (list != TEMP_COLLISION_LIST) { -- return; -- } -- ((UnsafeList)list).setSize(0); -- tempCollisionListInUse = false; -+ public static List getTempCollisionList() { -+ return Lists.newCopyOnWriteArrayList(); - } - -- static final UnsafeList TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024); -- static boolean tempGetEntitiesListInUse; -+ public static void returnTempCollisionList(List list) {} - -- public static UnsafeList getTempGetEntitiesList() { -- if (!Bukkit.isPrimaryThread() || tempGetEntitiesListInUse) { -- return new UnsafeList<>(16); -- } -- tempGetEntitiesListInUse = true; -- return TEMP_GET_ENTITIES_LIST; -+ public static List getTempGetEntitiesList() { -+ return Lists.newCopyOnWriteArrayList(); - } - -- public static void returnTempGetEntitiesList(List list) { -- if (list != TEMP_GET_ENTITIES_LIST) { -- return; -- } -- ((UnsafeList)list).setSize(0); -- tempGetEntitiesListInUse = false; -- } -+ public static void returnTempGetEntitiesList(List list) {} - // Paper end - optimise collisions - -- public static void reset() { -- // Paper start - optimise collisions -- TEMP_COLLISION_LIST.completeReset(); -- TEMP_GET_ENTITIES_LIST.completeReset(); -- // Paper end - optimise collisions -- } -+ public static void reset() {} - } -diff --git a/src/main/java/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 { - return 1.0 - ((double)this.indexMap.size() / (double)this.listSize); - } - -- public int createRawIterator() { -+ public synchronized int createRawIterator() { - if (this.allowSafeIteration()) { - ++this.iteratorCount; - } -@@ -105,7 +105,7 @@ public final class IteratorSafeOrderedReferenceSet { - } - } - -- public int advanceRawIterator(final int index) { -+ public synchronized int advanceRawIterator(final int index) { - final E[] elements = this.listElements; - int ret = index + 1; - for (int len = this.listSize; ret < len; ++ret) { -@@ -117,7 +117,7 @@ public final class IteratorSafeOrderedReferenceSet { - return -1; - } - -- public void finishRawIterator() { -+ public synchronized void finishRawIterator() { - if (this.allowSafeIteration() && --this.iteratorCount == 0) { - if (this.getFragFactor() >= this.maxFragFactor) { - this.defrag(); -@@ -125,7 +125,7 @@ public final class IteratorSafeOrderedReferenceSet { - } - } - -- public boolean remove(final E element) { -+ public synchronized boolean remove(final E element) { - final int index = this.indexMap.removeInt(element); - if (index >= 0) { - if (this.firstInvalidIndex < 0 || index < this.firstInvalidIndex) { -@@ -144,11 +144,11 @@ public final class IteratorSafeOrderedReferenceSet { - return false; - } - -- public boolean contains(final E element) { -+ public synchronized boolean contains(final E element) { - return this.indexMap.containsKey(element); - } - -- public boolean add(final E element) { -+ public synchronized boolean add(final E element) { - final int listSize = this.listSize; - - final int previous = this.indexMap.putIfAbsent(element, listSize); -@@ -223,30 +223,30 @@ public final class IteratorSafeOrderedReferenceSet { - //this.check(); - } - -- public E rawGet(final int index) { -+ public synchronized E rawGet(final int index) { - return this.listElements[index]; - } - -- public int size() { -+ public synchronized int size() { - // always returns the correct amount - listSize can be different - return this.indexMap.size(); - } - -- public IteratorSafeOrderedReferenceSet.Iterator iterator() { -+ public synchronized IteratorSafeOrderedReferenceSet.Iterator iterator() { - return this.iterator(0); - } - -- public IteratorSafeOrderedReferenceSet.Iterator iterator(final int flags) { -+ public synchronized IteratorSafeOrderedReferenceSet.Iterator iterator(final int flags) { - if (this.allowSafeIteration()) { - ++this.iteratorCount; - } - return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize); - } - -- public java.util.Iterator unsafeIterator() { -+ public synchronized java.util.Iterator unsafeIterator() { - return this.unsafeIterator(0); - } -- public java.util.Iterator unsafeIterator(final int flags) { -+ public synchronized java.util.Iterator unsafeIterator(final int flags) { - return new BaseIterator<>(this, false, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize); - } - -@@ -273,7 +273,7 @@ public final class IteratorSafeOrderedReferenceSet { - } - - @Override -- public boolean hasNext() { -+ public synchronized boolean hasNext() { - if (this.finished) { - return false; - } -@@ -297,7 +297,7 @@ public final class IteratorSafeOrderedReferenceSet { - } - - @Override -- public E next() { -+ public synchronized E next() { - if (!this.hasNext()) { - throw new NoSuchElementException(); - } -@@ -310,7 +310,7 @@ public final class IteratorSafeOrderedReferenceSet { - } - - @Override -- public void remove() { -+ public synchronized void remove() { - final E lastReturned = this.lastReturned; - if (lastReturned == null) { - throw new IllegalStateException(); -@@ -320,7 +320,7 @@ public final class IteratorSafeOrderedReferenceSet { - } - - @Override -- public void finishedIterating() { -+ public synchronized void finishedIterating() { - if (this.finished || !this.canFinish) { - throw new IllegalStateException(); - } -diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -index f597d65d56964297eeeed6c7e77703764178fee0..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, EntityCollectionBySection> entitiesByClass; -+ protected final Reference2ObjectMap, EntityCollectionBySection> entitiesByClass; - protected final EntityList entities = new EntityList(); - - public ChunkHolder.FullChunkStatus status; -@@ -61,7 +62,7 @@ public final class ChunkEntitySlices { - - this.allEntities = new EntityCollectionBySection(this); - this.hardCollidingEntities = new EntityCollectionBySection(this); -- this.entitiesByClass = new Reference2ObjectOpenHashMap<>(); -+ this.entitiesByClass = Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>()); - - this.status = status; - } -@@ -140,9 +141,7 @@ public final class ChunkEntitySlices { - - final Entity[] rawData = this.entities.getRawData(); - final List collectedEntities = new ArrayList<>(len); -- for (int i = 0; i < len; ++i) { -- collectedEntities.add(rawData[i]); -- } -+ collectedEntities.addAll(Arrays.asList(rawData).subList(0, len)); - - return collectedEntities; - } -@@ -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, EntityCollectionBySection>> iterator = -- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { -+ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { - final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); - - if (entry.getKey().isInstance(entity)) { -@@ -234,7 +222,7 @@ public final class ChunkEntitySlices { - } - - for (final Iterator, EntityCollectionBySection>> iterator = -- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { -+ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { - final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); - - if (entry.getKey().isInstance(entity)) { -@@ -312,11 +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[] 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 Set newHashSet() { -+ //LOGGER.info("Concurrent hash set created"); -+ return Collections.newSetFromMap(new ConcurrentHashMap()); -+ } -+ -+ public static Map newHashMap() { -+ //LOGGER.info("Concurrent hash map created"); -+ return new ConcurrentHashMap(); -+ } -+ -+ public static List newLinkedList() { -+ LOGGER.info("Concurrent \"linked\" list created"); -+ return new CopyOnWriteArrayList(); -+ } -+ -+ public static Collector> toList() { -+ return Collectors.toCollection(CopyOnWriteArrayList::new); -+ } -+ -+ public static Queue newArrayDeque() { -+ LOGGER.info("Concurrent \"array\" deque created"); -+ return new ConcurrentLinkedDeque(); -+ } -+ -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java -new file mode 100644 -index 0000000000000000000000000000000000000000..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 -+ */ -+ -+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 weakly consistent, returning -+ * elements reflecting the state of the deque at some point at or since the -+ * creation of the iterator. They do not throw -+ * {@link ConcurrentModificationException}, and may proceed concurrently with -+ * other operations. -+ * -+ *

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

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

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

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

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

-+ * -+ * If the deque fits in the specified array with room to spare (i.e., the array -+ * has more elements than the deque), the element in the array immediately -+ * following the end of the collection is set to null. This is useful in -+ * determining the length of the deque only if the caller knows that the -+ * deque does not contain any null elements. -+ * -+ * @param a the array into which the elements of the deque are to be stored, if -+ * it is big enough; otherwise, a new array of the same runtime type is -+ * allocated for this purpose. -+ * @return an array containing the elements of the deque. -+ * @throws ArrayStoreException if the runtime type of a is not a supertype of -+ * the runtime type of every element in this deque. -+ * @throws NullPointerException if the specified array is null. -+ */ -+ public T[] toArray(T[] a) { -+ return toArrayList().toArray(a); -+ } -+ -+ /** -+ * Returns a weakly consistent iterator over the elements in this deque, in -+ * first-to-last order. The next method returns elements reflecting the -+ * state of the deque at some point at or since the creation of the iterator. -+ * The method does not throw {@link ConcurrentModificationException}, -+ * and may proceed concurrently with other operations. -+ * -+ * @return an iterator over the elements in this deque -+ */ -+ public Iterator iterator() { -+ return new CLDIterator(); -+ } -+ -+ final class CLDIterator implements Iterator { -+ Node last; -+ -+ Node next = header.forward(); -+ -+ public boolean hasNext() { -+ return next != null; -+ } -+ -+ public E next() { -+ Node l = last = next; -+ if (l == null) -+ throw new NoSuchElementException(); -+ next = next.forward(); -+ return l.element; -+ } -+ -+ public void remove() { -+ Node l = last; -+ if (l == null) -+ throw new IllegalStateException(); -+ while (!l.delete() && !l.isDeleted()) -+ ; -+ } -+ } -+ -+ @Override -+ public boolean addAll(int index, Collection c) { -+ throw new NotImplementedException("TODO"); -+ } -+ -+ @Override -+ public E get(int index) { -+ Node current = header.successor(); -+ if (current == null) { -+ throw new IndexOutOfBoundsException(); -+ } -+ for (; index > 0; index --) { -+ current = current.successor(); -+ if (current == null) { -+ throw new IndexOutOfBoundsException(); -+ } -+ } -+ return current.element; -+ } -+ -+ @Override -+ public E set(int index, E element) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public void add(int index, E element) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public E remove(int index) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public int indexOf(Object o) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public int lastIndexOf(Object o) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public ListIterator listIterator() { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public ListIterator listIterator(int index) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+ @Override -+ public List subList(int fromIndex, int toIndex) { -+ throw new NotImplementedException("INVALID"); -+ } -+ -+} -+ -+/** -+ * Linked Nodes. As a minor efficiency hack, this class opportunistically -+ * inherits from AtomicReference, with the atomic ref used as the "next" link. -+ * -+ * Nodes are in doubly-linked lists. There are three kinds of special nodes, -+ * distinguished by: * The list header has a null prev link * The list trailer -+ * has a null next link * A deletion marker has a prev link pointing to itself. -+ * All three kinds of special nodes have null element fields. -+ * -+ * Regular nodes have non-null element, next, and prev fields. To avoid visible -+ * inconsistencies when deletions overlap element replacement, replacements are -+ * done by replacing the node, not just setting the element. -+ * -+ * Nodes can be traversed by read-only ConcurrentLinkedDeque class operations -+ * just by following raw next pointers, so long as they ignore any special nodes -+ * seen along the way. (This is automated in method forward.) However, traversal -+ * using prev pointers is not guaranteed to see all live nodes since a prev -+ * pointer of a deleted node can become unrecoverably stale. -+ */ -+ -+class Node extends AtomicReference> { -+ -+ private static final long serialVersionUID = 6640557564507962862L; -+ -+ private volatile Node prev; -+ -+ final E element; -+ -+ /** Creates a node with given contents */ -+ Node(E element, Node next, Node prev) { -+ super(next); -+ this.prev = prev; -+ this.element = element; -+ } -+ -+ /** Creates a marker node with given successor */ -+ Node(Node next) { -+ super(next); -+ this.prev = this; -+ this.element = null; -+ } -+ -+ /** -+ * Gets next link (which is actually the value held as atomic reference). -+ */ -+ private Node getNext() { -+ return get(); -+ } -+ -+ /** -+ * Sets next link -+ * -+ * @param n the next node -+ */ -+ void setNext(Node n) { -+ set(n); -+ } -+ -+ /** -+ * compareAndSet next link -+ */ -+ private boolean casNext(Node cmp, Node val) { -+ return compareAndSet(cmp, val); -+ } -+ -+ /** -+ * Gets prev link -+ */ -+ private Node getPrev() { -+ return prev; -+ } -+ -+ /** -+ * Sets prev link -+ * -+ * @param b the previous node -+ */ -+ void setPrev(Node b) { -+ prev = b; -+ } -+ -+ /** -+ * Returns true if this is a header, trailer, or marker node -+ */ -+ boolean isSpecial() { -+ return element == null; -+ } -+ -+ /** -+ * Returns true if this is a trailer node -+ */ -+ boolean isTrailer() { -+ return getNext() == null; -+ } -+ -+ /** -+ * Returns true if this is a header node -+ */ -+ boolean isHeader() { -+ return getPrev() == null; -+ } -+ -+ /** -+ * Returns true if this is a marker node -+ */ -+ boolean isMarker() { -+ return getPrev() == this; -+ } -+ -+ /** -+ * Returns true if this node is followed by a marker, meaning that it is -+ * deleted. -+ * -+ * @return true if this node is deleted -+ */ -+ boolean isDeleted() { -+ Node f = getNext(); -+ return f != null && f.isMarker(); -+ } -+ -+ /** -+ * Returns next node, ignoring deletion marker -+ */ -+ private Node nextNonmarker() { -+ Node f = getNext(); -+ return (f == null || !f.isMarker()) ? f : f.getNext(); -+ } -+ -+ /** -+ * Returns the next non-deleted node, swinging next pointer around any -+ * encountered deleted nodes, and also patching up successor''s prev link to -+ * point back to this. Returns null if this node is trailer so has no successor. -+ * -+ * @return successor, or null if no such -+ */ -+ Node successor() { -+ Node f = nextNonmarker(); -+ for (;;) { -+ if (f == null) -+ return null; -+ if (!f.isDeleted()) { -+ if (f.getPrev() != this && !isDeleted()) -+ f.setPrev(this); // relink f's prev -+ return f; -+ } -+ Node s = f.nextNonmarker(); -+ if (f == getNext()) -+ casNext(f, s); // unlink f -+ f = s; -+ } -+ } -+ -+ /** -+ * Returns the apparent predecessor of target by searching forward for it -+ * starting at this node, patching up pointers while traversing. Used by -+ * predecessor(). -+ * -+ * @return target's predecessor, or null if not found -+ */ -+ private Node findPredecessorOf(Node target) { -+ Node n = this; -+ for (;;) { -+ Node f = n.successor(); -+ if (f == target) -+ return n; -+ if (f == null) -+ return null; -+ n = f; -+ } -+ } -+ -+ /** -+ * Returns the previous non-deleted node, patching up pointers as needed. -+ * Returns null if this node is header so has no successor. May also return null -+ * if this node is deleted, so doesn't have a distinct predecessor. -+ * -+ * @return predecessor or null if not found -+ */ -+ Node predecessor() { -+ Node n = this; -+ for (;;) { -+ Node b = n.getPrev(); -+ if (b == null) -+ return n.findPredecessorOf(this); -+ Node s = b.getNext(); -+ if (s == this) -+ return b; -+ if (s == null || !s.isMarker()) { -+ Node p = b.findPredecessorOf(this); -+ if (p != null) -+ return p; -+ } -+ n = b; -+ } -+ } -+ -+ /** -+ * Returns the next node containing a nondeleted user element. Use for forward -+ * list traversal. -+ * -+ * @return successor, or null if no such -+ */ -+ Node forward() { -+ Node f = successor(); -+ return (f == null || f.isSpecial()) ? null : f; -+ } -+ -+ /** -+ * Returns previous node containing a nondeleted user element, if possible. Use -+ * for backward list traversal, but beware that if this method is called from a -+ * deleted node, it might not be able to determine a usable predecessor. -+ * -+ * @return predecessor, or null if no such could be found -+ */ -+ Node back() { -+ Node f = predecessor(); -+ return (f == null || f.isSpecial()) ? null : f; -+ } -+ -+ /** -+ * Tries to insert a node holding element as successor, failing if this node is -+ * deleted. -+ * -+ * @param element the element -+ * @return the new node, or null on failure. -+ */ -+ Node append(E element) { -+ for (;;) { -+ Node f = getNext(); -+ if (f == null || f.isMarker()) -+ return null; -+ Node x = new Node(element, f, this); -+ if (casNext(f, x)) { -+ f.setPrev(x); // optimistically link -+ return x; -+ } -+ } -+ } -+ -+ /** -+ * Tries to insert a node holding element as predecessor, failing if no live -+ * predecessor can be found to link to. -+ * -+ * @param element the element -+ * @return the new node, or null on failure. -+ */ -+ Node prepend(E element) { -+ for (;;) { -+ Node b = predecessor(); -+ if (b == null) -+ return null; -+ Node x = new Node(element, this, b); -+ if (b.casNext(this, x)) { -+ setPrev(x); // optimistically link -+ return x; -+ } -+ } -+ } -+ -+ /** -+ * Tries to mark this node as deleted, failing if already deleted or if this -+ * node is header or trailer -+ * -+ * @return true if successful -+ */ -+ boolean delete() { -+ Node b = getPrev(); -+ Node f = getNext(); -+ if (b != null && f != null && !f.isMarker() && casNext(f, new Node(f))) { -+ if (b.casNext(this, f)) -+ f.setPrev(b); -+ return true; -+ } -+ return false; -+ } -+ -+ /** -+ * Tries to insert a node holding element to replace this node. failing if -+ * already deleted. -+ * -+ * @param newElement the new element -+ * @return the new node, or null on failure. -+ */ -+ Node replace(E newElement) { -+ for (;;) { -+ Node b = getPrev(); -+ Node f = getNext(); -+ if (b == null || f == null || f.isMarker()) -+ return null; -+ Node x = new Node(newElement, f, b); -+ if (casNext(f, new Node(x))) { -+ b.successor(); // to relink b -+ x.successor(); // to relink f -+ return x; -+ } -+ } -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java -new file mode 100644 -index 0000000000000000000000000000000000000000..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 backing; -+ -+ public ConcurrentLongLinkedOpenHashSet() { -+ //backing = new ConcurrentLinkedDeque(); -+ backing = new ConcurrentSkipListSet(); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final int initial) { -+ //backing = new ConcurrentLinkedDeque(); -+ backing = new ConcurrentSkipListSet(); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final int initial, final float dnc) { -+ this(initial); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final LongCollection c) { -+ this(c.size()); -+ addAll(c); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final LongCollection c, final float f) { -+ this(c.size(), f); -+ addAll(c); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final LongIterator i, final float f) { -+ this(16, f); -+ while (i.hasNext()) -+ add(i.nextLong()); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final LongIterator i) { -+ this(i, -1); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final Iterator i, final float f) { -+ this(LongIterators.asLongIterator(i), f); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final Iterator i) { -+ this(LongIterators.asLongIterator(i)); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length, final float f) { -+ this(length < 0 ? 0 : length, f); -+ LongArrays.ensureOffsetLength(a, offset, length); -+ for (int i = 0; i < length; i++) -+ add(a[offset + i]); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length) { -+ this(a, offset, length, DEFAULT_LOAD_FACTOR); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final long[] a, final float f) { -+ this(a, 0, a.length, f); -+ } -+ -+ public ConcurrentLongLinkedOpenHashSet(final long[] a) { -+ this(a, -1); -+ } -+ -+ @Override -+ public boolean add(final long k) { -+ boolean out = backing.add(k); -+ /* -+ if (!firstDef) { -+ first = k; -+ firstDef = true; -+ } -+ last = k; -+ */ -+ return out; -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return addAll((Collection) c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean addAndMoveToFirst(final long k) { -+ boolean out = backing.add(k); -+ //first = k; -+ return out; -+ } -+ -+ @Override -+ public boolean addAndMoveToLast(final long k) { -+ boolean out = backing.add(k); -+ //last = k; -+ return out; -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public LongLinkedOpenHashSet clone() { -+ return new ConcurrentLongLinkedOpenHashSet(backing.iterator()); -+ } -+ -+ @Override -+ public LongComparator comparator() { -+ return null; -+ } -+ -+ @Override -+ public boolean contains(final long k) { -+ return backing.contains(k); -+ } -+ -+ @Override -+ public long firstLong() { -+ /* -+ if (backing.size() == 0) throw new NoSuchElementException(); -+ return first; -+ */ -+ return backing.first(); -+ } -+ -+ @Override -+ public int hashCode() { -+ return backing.hashCode(); -+ } -+ -+ @Override -+ public LongSortedSet headSet(long to) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public LongListIterator iterator() { -+ return FastUtilHackUtil.wrap(backing.iterator()); -+ } -+ -+ @Override -+ public LongListIterator iterator(long from) { -+ throw new IllegalStateException(); -+ //return FastUtilHackUtil.wrap(backing.iterator()); -+ } -+ -+ @Override -+ public long lastLong() { -+ /* -+ if (backing.size() == 0) throw new NoSuchElementException(); -+ return last; -+ */ -+ return backing.last(); -+ } -+ -+ @Override -+ public boolean remove(final long k) { -+ /* -+ if (k == first) { -+ first = backing.iterator().next(); -+ } -+ if (k == last) { -+ last = backing.iterator().next(); -+ } -+ */ -+ return backing.remove(k); -+ } -+ -+ @Override -+ public long removeFirstLong() { -+ long fl = this.firstLong(); -+ this.remove(fl); -+ //first = backing.iterator().next(); -+ return fl; -+ } -+ -+ @Override -+ public long removeLastLong() { -+ long fl = this.lastLong(); -+ this.remove(fl); -+ //last = backing.iterator().next(); -+ return fl; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public LongSortedSet subSet(long from, long to) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongSortedSet tailSet(long from) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public boolean trim() { -+ return true; -+ } -+ -+ @Override -+ public boolean trim(final int n) { -+ return true; -+ } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java -new file mode 100644 -index 0000000000000000000000000000000000000000..93bd066ec2013e42a85fcf21344fe41f3ad69598 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java -@@ -0,0 +1,144 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.longs.*; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.Collection; -+import java.util.concurrent.ConcurrentSkipListSet; -+ -+public class ConcurrentLongSortedSet implements LongSortedSet { -+ -+ ConcurrentSkipListSet back = new ConcurrentSkipListSet<>(); -+ -+ @Override -+ public LongBidirectionalIterator iterator(long fromElement) { -+ return null; -+ } -+ -+ @Override -+ public int size() { -+ return back.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return back.isEmpty(); -+ } -+ -+ @Override -+ public LongBidirectionalIterator iterator() { -+ return null; -+ } -+ -+ @NotNull -+ @Override -+ public Object[] toArray() { -+ return back.toArray(); -+ } -+ -+ @NotNull -+ @Override -+ public T[] toArray(@NotNull T[] ts) { -+ return null; -+ } -+ -+ @Override -+ public boolean containsAll(@NotNull Collection collection) { -+ return back.containsAll(collection); -+ } -+ -+ @Override -+ public boolean addAll(@NotNull Collection collection) { -+ return back.addAll(collection); -+ } -+ -+ @Override -+ public boolean removeAll(@NotNull Collection collection) { -+ return back.removeAll(collection); -+ } -+ -+ @Override -+ public boolean retainAll(@NotNull Collection collection) { -+ return back.retainAll(collection); -+ } -+ -+ @Override -+ public void clear() { -+ back.clear(); -+ } -+ -+ @Override -+ public boolean add(long key) { -+ return back.add(key); -+ } -+ -+ @Override -+ public boolean contains(long key) { -+ return back.contains(key); -+ } -+ -+ @Override -+ public long[] toLongArray() { -+ return new long[0]; -+ } -+ -+ @Override -+ public long[] toArray(long[] a) { -+ return new long[0]; -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return back.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(LongCollection c) { -+ return back.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(LongCollection c) { -+ return back.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(LongCollection c) { -+ return back.retainAll(c); -+ } -+ -+ @Override -+ public boolean remove(long k) { -+ return back.remove(k); -+ } -+ -+ @Override -+ public LongSortedSet subSet(long fromElement, long toElement) { -+ return new LongAVLTreeSet(back.subSet(fromElement,toElement)); -+ } -+ -+ @Override -+ public LongSortedSet headSet(long toElement) { -+ return new LongAVLTreeSet(back.headSet(toElement)); -+ } -+ -+ @Override -+ public LongSortedSet tailSet(long fromElement) { -+ return new LongAVLTreeSet(back.tailSet(fromElement)); -+ } -+ -+ @Override -+ public LongComparator comparator() { -+ return null; -+ } -+ -+ @Override -+ public long firstLong() { -+ return back.first(); -+ } -+ -+ @Override -+ public long lastLong() { -+ return back.last(); -+ } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ff1a4f87356459d3bc990a77c3081932046da5b1 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java -@@ -0,0 +1,112 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.shorts.ShortCollection; -+import it.unimi.dsi.fastutil.shorts.ShortIterator; -+import it.unimi.dsi.fastutil.shorts.ShortSet; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.Collection; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class ConcurrentShortHashSet implements ShortSet { -+ -+ ConcurrentHashMap.KeySetView backing = ConcurrentHashMap.newKeySet(); -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public ShortIterator iterator() { -+ return new FastUtilHackUtil.WrappingShortIterator(backing.iterator()); -+ } -+ -+ @NotNull -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @NotNull -+ @Override -+ public T[] toArray(@NotNull T[] ts) { -+ return (T[]) backing.toArray(); -+ } -+ -+ @Override -+ public boolean containsAll(@NotNull Collection collection) { -+ return backing.containsAll(collection); -+ } -+ -+ @Override -+ public boolean addAll(@NotNull Collection collection) { -+ return backing.addAll(collection); -+ } -+ -+ @Override -+ public boolean removeAll(@NotNull Collection collection) { -+ return backing.removeAll(collection); -+ } -+ -+ @Override -+ public boolean retainAll(@NotNull Collection collection) { -+ return backing.retainAll(collection); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ -+ } -+ -+ @Override -+ public boolean add(short key) { -+ return backing.add(key); -+ } -+ -+ @Override -+ public boolean contains(short key) { -+ return backing.contains(key); -+ } -+ -+ @Override -+ public short[] toShortArray() { -+ return new short[0]; -+ } -+ -+ @Override -+ public short[] toArray(short[] a) { -+ return new short[0]; -+ } -+ -+ @Override -+ public boolean addAll(ShortCollection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(ShortCollection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(ShortCollection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(ShortCollection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public boolean remove(short k) { -+ return backing.remove(k); -+ } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java -new file mode 100644 -index 0000000000000000000000000000000000000000..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 implements ObjectSet { -+ -+ Set backing; -+ Function forward; -+ Function back; -+ -+ public ConvertingObjectSet(Set backing, Function forward, Function back) { -+ this.backing = backing; -+ this.forward = forward; -+ this.back = back; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean contains(Object o) { -+ try { -+ return backing.contains(back.apply((T) o)); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.stream().map(forward).toArray(); -+ } -+ -+ @Override -+ public R[] toArray(R[] a) { -+ return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); -+ } -+ -+ @Override -+ public boolean add(T e) { -+ return backing.add(back.apply(e)); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean remove(Object o) { -+ try { -+ return backing.remove(back.apply((T) o)); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean containsAll(Collection c) { -+ try { -+ return backing.containsAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c.stream().map(i -> back.apply(i)).collect(Collectors.toSet())); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean removeAll(Collection c) { -+ try { -+ return backing.removeAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean retainAll(Collection c) { -+ try { -+ return backing.retainAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ -+ } -+ -+ @Override -+ public ObjectIterator iterator() { -+ final Iterator backg = backing.iterator(); -+ return new ObjectIterator() { -+ -+ @Override -+ public boolean hasNext() { -+ return backg.hasNext(); -+ } -+ -+ @Override -+ public T next() { -+ return forward.apply(backg.next()); -+ } -+ -+ @Override -+ public void remove() { -+ backg.remove(); -+ } -+ }; -+ } -+ -+ -+ } -+ -+ public static class ConvertingObjectSetFast implements Long2ObjectMap.FastEntrySet { -+ -+ Set backing; -+ Function> forward; -+ Function, E> back; -+ -+ public ConvertingObjectSetFast(Set backing, -+ Function> forward, -+ Function, E> back) { -+ this.backing = backing; -+ this.forward = forward; -+ this.back = back; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean contains(Object o) { -+ try { -+ return backing.contains(back.apply((Long2ObjectMap.Entry)o)); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.stream().map(forward).toArray(); -+ } -+ -+ @Override -+ public R[] toArray(R[] a) { -+ return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean remove(Object o) { -+ try { -+ return backing.remove(back.apply((Long2ObjectMap.Entry)o)); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean containsAll(Collection c) { -+ try { -+ return backing.containsAll(c.stream() -+ .map(i -> back.apply((Long2ObjectMap.Entry) i)) -+ .collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean removeAll(Collection c) { -+ try { -+ return backing.removeAll(c.stream().map(i -> back -+ .apply((Long2ObjectMap.Entry) i)) -+ .collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @SuppressWarnings("unchecked") -+ @Override -+ public boolean retainAll(Collection c) { -+ try { -+ return backing.retainAll(c.stream() -+ .map(i -> back.apply((Long2ObjectMap.Entry) i)) -+ .collect(Collectors.toSet())); -+ } catch (ClassCastException cce) { -+ return false; -+ } -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ -+ } -+ -+ @Override -+ public ObjectIterator> iterator() { -+ final Iterator backg = backing.iterator(); -+ return new ObjectIterator>() { -+ -+ @Override -+ public boolean hasNext() { -+ return backg.hasNext(); -+ } -+ -+ @Override -+ public Long2ObjectMap.Entry next() { -+ return forward.apply(backg.next()); -+ } -+ -+ @Override -+ public void remove() { -+ backg.remove(); -+ } -+ }; -+ } -+ -+ @Override -+ public boolean add(Long2ObjectMap.Entry e) { -+ return backing.add(back.apply(e)); -+ } -+ -+ @Override -+ public boolean addAll(Collection> c) { -+ return backing.addAll(c.stream().map(back).collect(Collectors.toList())); -+ } -+ -+ @Override -+ public ObjectIterator> fastIterator() { -+ return iterator(); -+ } -+ -+ -+ } -+ -+ private static Entry intEntryForwards(Map.Entry entry) { -+ return new Entry() { -+ -+ @Override -+ public T getValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public T setValue(T value) { -+ return entry.setValue(value); -+ } -+ -+ @Override -+ public int getIntKey() { -+ return entry.getKey(); -+ } -+ -+ @Override -+ public boolean equals(Object obj) { -+ if (obj == entry) { -+ return true; -+ } -+ return super.equals(obj); -+ } -+ -+ @Override -+ public int hashCode() { -+ return entry.hashCode(); -+ } -+ }; -+ } -+ -+ private static Map.Entry intEntryBackwards(Entry entry) { -+ return entry; -+ } -+ -+ private static Long2ObjectMap.Entry longEntryForwards(Map.Entry entry) { -+ return new Long2ObjectMap.Entry() { -+ -+ @Override -+ public T getValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public T setValue(T value) { -+ return entry.setValue(value); -+ } -+ -+ @Override -+ public long getLongKey() { -+ return entry.getKey(); -+ } -+ -+ @Override -+ public boolean equals(Object obj) { -+ if (obj == entry) { -+ return true; -+ } -+ return super.equals(obj); -+ } -+ -+ @Override -+ public int hashCode() { -+ return entry.hashCode(); -+ } -+ }; -+ } -+ -+ private static Map.Entry longEntryBackwards(Long2ObjectMap.Entry entry) { -+ return entry; -+ } -+ -+ private static Long2ByteMap.Entry longByteEntryForwards(Map.Entry entry) { -+ return new Long2ByteMap.Entry() { -+ -+ @Override -+ public Byte getValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public byte setValue(byte value) { -+ return entry.setValue(value); -+ } -+ -+ @Override -+ public byte getByteValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public long getLongKey() { -+ return entry.getKey(); -+ } -+ -+ @Override -+ public boolean equals(Object obj) { -+ if (obj == entry) { -+ return true; -+ } -+ return super.equals(obj); -+ } -+ -+ @Override -+ public int hashCode() { -+ return entry.hashCode(); -+ } -+ -+ }; -+ } -+ -+ private static Map.Entry longByteEntryBackwards(Long2ByteMap.Entry entry) { -+ return entry; -+ } -+ -+ private static Long2LongMap.Entry longLongEntryForwards(Map.Entry entry) { -+ return new Long2LongMap.Entry() { -+ -+ @Override -+ public Long getValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public long setValue(long value) { -+ return entry.setValue(value); -+ } -+ -+ @Override -+ public long getLongValue() { -+ return entry.getValue(); -+ } -+ -+ @Override -+ public long getLongKey() { -+ return entry.getKey(); -+ } -+ -+ @Override -+ public boolean equals(Object obj) { -+ if (obj == entry) { -+ return true; -+ } -+ return super.equals(obj); -+ } -+ -+ @Override -+ public int hashCode() { -+ return entry.hashCode(); -+ } -+ -+ }; -+ } -+ -+ private static Map.Entry longLongEntryBackwards(Long2LongMap.Entry entry) { -+ return entry; -+ } -+ -+ public static ObjectSet> entrySetIntWrap(Map map) { -+ return new ConvertingObjectSet, Entry>(map.entrySet(), FastUtilHackUtil::intEntryForwards, FastUtilHackUtil::intEntryBackwards); -+ } -+ -+ public static ObjectSet> entrySetLongWrap(Map map) { -+ return new ConvertingObjectSet, Long2ObjectMap.Entry>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); -+ } -+ -+ public static Long2ObjectMap.FastEntrySet entrySetLongWrapFast(Map map) { -+ return new ConvertingObjectSetFast, T>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); -+ } -+ -+ public static ObjectSet entrySetLongByteWrap(Map map) { -+ return new ConvertingObjectSet, Long2ByteMap.Entry>(map.entrySet(), FastUtilHackUtil::longByteEntryForwards, FastUtilHackUtil::longByteEntryBackwards); -+ } -+ -+ public static ObjectSet entrySetLongLongWrap(Map map) { -+ return new ConvertingObjectSet, Long2LongMap.Entry>(map.entrySet(), FastUtilHackUtil::longLongEntryForwards, FastUtilHackUtil::longLongEntryBackwards); -+ } -+ -+ -+ static class WrappingIntIterator implements IntIterator { -+ -+ Iterator backing; -+ -+ public WrappingIntIterator(Iterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public int nextInt() { -+ return backing.next(); -+ } -+ -+ @Override -+ public Integer next() { -+ return backing.next(); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ } -+ -+ static class WrappingLongIterator implements LongIterator { -+ -+ Iterator backing; -+ -+ public WrappingLongIterator(Iterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public long nextLong() { -+ return backing.next(); -+ } -+ -+ @Override -+ public Long next() { -+ return backing.next(); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ } -+ -+ static class WrappingShortIterator implements ShortIterator { -+ -+ Iterator backing; -+ -+ public WrappingShortIterator(Iterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public short nextShort() { -+ return backing.next(); -+ } -+ -+ @Override -+ public Short next() { -+ return backing.next(); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ } -+ -+ public static class WrappingIntSet implements IntSet { -+ -+ Set backing; -+ -+ public WrappingIntSet(Set backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean add(int key) { -+ return backing.add(key); -+ } -+ -+ @Override -+ public boolean contains(int key) { -+ return backing.contains(key); -+ } -+ -+ @Override -+ public int[] toIntArray() { -+ return backing.stream().mapToInt(i -> i).toArray(); -+ } -+ -+ @Override -+ public int[] toIntArray(int[] a) { -+ if (a.length >= size()) { -+ return null; -+ } else { -+ return toIntArray(); -+ } -+ } -+ -+ @Override -+ public int[] toArray(int[] a) { -+ return toIntArray(a); -+ } -+ -+ @Override -+ public boolean addAll(IntCollection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(IntCollection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(IntCollection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(IntCollection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public IntIterator iterator() { -+ return new WrappingIntIterator(backing.iterator()); -+ } -+ -+ @Override -+ public boolean remove(int k) { -+ return backing.remove(k); -+ } -+ -+ } -+ -+ public static LongSet wrapLongSet(Set longset) { -+ return new WrappingLongSet(longset); -+ } -+ -+ public static class WrappingLongSet implements LongSet { -+ -+ Set backing; -+ -+ public WrappingLongSet(Set backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean add(long key) { -+ return backing.add(key); -+ } -+ -+ @Override -+ public boolean contains(long key) { -+ return backing.contains(key); -+ } -+ -+ @Override -+ public long[] toLongArray() { -+ return backing.stream().mapToLong(i -> i).toArray(); -+ } -+ -+ @Override -+ public long[] toLongArray(long[] a) { -+ if (a.length >= size()) { -+ return null; -+ } else { -+ return toLongArray(); -+ } -+ } -+ -+ @Override -+ public long[] toArray(long[] a) { -+ return toLongArray(a); -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(LongCollection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(LongCollection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(LongCollection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public LongIterator iterator() { -+ return new WrappingLongIterator(backing.iterator()); -+ } -+ -+ @Override -+ public boolean remove(long k) { -+ return backing.remove(k); -+ } -+ -+ } -+ -+ public static LongSortedSet wrapLongSortedSet(Set longset) { -+ return new WrappingLongSortedSet(longset); -+ } -+ -+ public static class WrappingLongSortedSet implements LongSortedSet { -+ -+ Set backing; -+ -+ public WrappingLongSortedSet(Set backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public boolean add(long key) { -+ return backing.add(key); -+ } -+ -+ @Override -+ public boolean contains(long key) { -+ return backing.contains(key); -+ } -+ -+ @Override -+ public long[] toLongArray() { -+ return backing.stream().mapToLong(i -> i).toArray(); -+ } -+ -+ @Override -+ public long[] toLongArray(long[] a) { -+ if (a.length >= size()) { -+ return null; -+ } else { -+ return toLongArray(); -+ } -+ } -+ -+ @Override -+ public long[] toArray(long[] a) { -+ return toLongArray(a); -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean containsAll(LongCollection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(LongCollection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(LongCollection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public boolean remove(long k) { -+ return backing.remove(k); -+ } -+ -+ @Override -+ public LongBidirectionalIterator iterator(long fromElement) { -+ throw new UnsupportedOperationException(); -+ //return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); -+ } -+ -+ @Override -+ public LongBidirectionalIterator iterator() { -+ return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); -+ } -+ -+ @Override -+ public LongSortedSet subSet(long fromElement, long toElement) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongSortedSet headSet(long toElement) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongSortedSet tailSet(long fromElement) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongComparator comparator() { -+ return null; -+ } -+ -+ @Override -+ public long firstLong() { -+ return backing.stream().findAny().get(); -+ } -+ -+ @Override -+ public long lastLong() { -+ return backing.stream().findAny().get(); -+ } -+ -+ } -+ -+ public static IntSet wrapIntSet(Set intset) { -+ return new WrappingIntSet(intset); -+ } -+ -+ public static class WrappingObjectCollection implements ObjectCollection { -+ -+ Collection backing; -+ -+ public WrappingObjectCollection(Collection backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(Object o) { -+ return backing.contains(o); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean add(V e) { -+ return backing.add(e); -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ return backing.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public ObjectIterator iterator() { -+ return FastUtilHackUtil.itrWrap(backing); -+ } -+ -+ } -+ -+ public static ObjectCollection wrap(Collection c) { -+ return new WrappingObjectCollection(c); -+ } -+ -+ public static class WrappingByteCollection implements ByteCollection { -+ -+ Collection backing; -+ -+ public WrappingByteCollection(Collection backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(byte o) { -+ return backing.contains(o); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean add(byte e) { -+ return backing.add(e); -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ return backing.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public ByteIterator iterator() { -+ return FastUtilHackUtil.itrByteWrap(backing); -+ } -+ -+ @Override -+ public boolean rem(byte key) { -+ return this.remove(key); -+ } -+ -+ @Override -+ public byte[] toByteArray() { -+ return null; -+ } -+ -+ @Override -+ public byte[] toByteArray(byte[] a) { -+ return toArray(a); -+ } -+ -+ @Override -+ public byte[] toArray(byte[] a) { -+ return ArrayUtils.toPrimitive(backing.toArray(new Byte[0])); -+ } -+ -+ @Override -+ public boolean addAll(ByteCollection c) { -+ return addAll((Collection) c); -+ } -+ -+ @Override -+ public boolean containsAll(ByteCollection c) { -+ return containsAll((Collection) c); -+ } -+ -+ @Override -+ public boolean removeAll(ByteCollection c) { -+ return removeAll((Collection) c); -+ } -+ -+ @Override -+ public boolean retainAll(ByteCollection c) { -+ return retainAll((Collection) c); -+ } -+ -+ } -+ -+ public static ByteCollection wrapBytes(Collection c) { -+ return new WrappingByteCollection(c); -+ } -+ -+ public static class WrappingIntCollection implements IntCollection { -+ -+ Collection backing; -+ -+ public WrappingIntCollection(Collection backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(int o) { -+ return backing.contains(o); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean add(int e) { -+ return backing.add(e); -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ return backing.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public IntIterator iterator() { -+ return FastUtilHackUtil.itrIntWrap(backing); -+ } -+ -+ @Override -+ public boolean rem(int key) { -+ return this.remove(key); -+ } -+ -+ @Override -+ public int[] toIntArray() { -+ return null; -+ } -+ -+ @Override -+ public int[] toIntArray(int[] a) { -+ return toArray(a); -+ } -+ -+ @Override -+ public int[] toArray(int[] a) { -+ return ArrayUtils.toPrimitive(backing.toArray(new Integer[0])); -+ } -+ -+ @Override -+ public boolean addAll(IntCollection c) { -+ return addAll((Collection) c); -+ } -+ -+ @Override -+ public boolean containsAll(IntCollection c) { -+ return containsAll((Collection) c); -+ } -+ -+ @Override -+ public boolean removeAll(IntCollection c) { -+ return removeAll((Collection) c); -+ } -+ -+ @Override -+ public boolean retainAll(IntCollection c) { -+ return retainAll((Collection) c); -+ } -+ -+ } -+ -+ public static IntCollection wrapInts(Collection c) { -+ return new WrappingIntCollection(c); -+ } -+ -+ public static class WrappingLongCollection implements LongCollection { -+ -+ Collection backing; -+ -+ public WrappingLongCollection(Collection backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(long o) { -+ return backing.contains(o); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return backing.toArray(); -+ } -+ -+ @Override -+ public T[] toArray(T[] a) { -+ return backing.toArray(a); -+ } -+ -+ @Override -+ public boolean add(long e) { -+ return backing.add(e); -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ return backing.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ return backing.containsAll(c); -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ return backing.addAll(c); -+ } -+ -+ @Override -+ public boolean removeAll(Collection c) { -+ return backing.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(Collection c) { -+ return backing.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public LongIterator iterator() { -+ return FastUtilHackUtil.itrLongWrap(backing); -+ } -+ -+ @Override -+ public boolean rem(long key) { -+ return this.remove(key); -+ } -+ -+ @Override -+ public long[] toLongArray() { -+ return null; -+ } -+ -+ @Override -+ public long[] toLongArray(long[] a) { -+ return toArray(a); -+ } -+ -+ @Override -+ public long[] toArray(long[] a) { -+ return ArrayUtils.toPrimitive(backing.toArray(new Long[0])); -+ } -+ -+ @Override -+ public boolean addAll(LongCollection c) { -+ return addAll((Collection) c); -+ } -+ -+ @Override -+ public boolean containsAll(LongCollection c) { -+ return containsAll((Collection) c); -+ } -+ -+ @Override -+ public boolean removeAll(LongCollection c) { -+ return removeAll((Collection) c); -+ } -+ -+ @Override -+ public boolean retainAll(LongCollection c) { -+ return retainAll((Collection) c); -+ } -+ -+ } -+ -+ public static LongCollection wrapLongs(Collection c) { -+ return new WrappingLongCollection(c); -+ } -+ -+ -+ public static class WrappingLongListIterator implements LongListIterator { -+ -+ ListIterator backing; -+ -+ public WrappingLongListIterator(ListIterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public long previousLong() { -+ return backing.previous(); -+ } -+ -+ @Override -+ public long nextLong() { -+ return backing.next(); -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public boolean hasPrevious() { -+ return backing.hasPrevious(); -+ } -+ -+ @Override -+ public int nextIndex() { -+ return backing.nextIndex(); -+ } -+ -+ @Override -+ public int previousIndex() { -+ return backing.previousIndex(); -+ } -+ -+ @Override -+ public void add(long k) { -+ backing.add(k); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ @Override -+ public void set(long k) { -+ backing.set(k); -+ } -+ } -+ -+ public static class SlimWrappingLongListIterator implements LongListIterator { -+ -+ Iterator backing; -+ -+ public SlimWrappingLongListIterator(Iterator backing) { -+ this.backing = backing; -+ } -+ -+ @Override -+ public long previousLong() { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public long nextLong() { -+ return backing.next(); -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return backing.hasNext(); -+ } -+ -+ @Override -+ public boolean hasPrevious() { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public int nextIndex() { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public int previousIndex() { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public void add(long k) { -+ throw new IllegalStateException(); -+ } -+ -+ @Override -+ public void remove() { -+ backing.remove(); -+ } -+ -+ @Override -+ public void set(long k) { -+ throw new IllegalStateException(); -+ } -+ } -+ -+ public static LongListIterator wrap(ListIterator c) { -+ return new WrappingLongListIterator(c); -+ } -+ -+ public static LongListIterator wrap(Iterator c) { -+ return new SlimWrappingLongListIterator(c); -+ } -+ -+ public static class WrappingByteIterator implements ByteIterator { -+ -+ Iterator parent; -+ -+ public WrappingByteIterator(Iterator parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return parent.hasNext(); -+ } -+ -+ @Override -+ public Byte next() { -+ return parent.next(); -+ } -+ -+ @Override -+ public void remove() { -+ parent.remove(); -+ } -+ -+ @Override -+ public byte nextByte() { -+ return next(); -+ } -+ -+ } -+ -+ public static ByteIterator itrByteWrap(Iterator backing) { -+ return new WrappingByteIterator(backing); -+ } -+ -+ public static ByteIterator itrByteWrap(Iterable backing) { -+ return new WrappingByteIterator(backing.iterator()); -+ } -+ -+ public static IntIterator itrIntWrap(Iterator backing) { -+ return new WrappingIntIterator(backing); -+ } -+ -+ public static IntIterator itrIntWrap(Iterable backing) { -+ return new WrappingIntIterator(backing.iterator()); -+ } -+ -+ public static LongIterator itrLongWrap(Iterator backing) { -+ return new WrappingLongIterator(backing); -+ } -+ -+ public static LongIterator itrLongWrap(Iterable backing) { -+ return new WrappingLongIterator(backing.iterator()); -+ } -+ -+ public static ShortIterator itrShortWrap(Iterator backing) { -+ return new WrappingShortIterator(backing); -+ } -+ -+ public static ShortIterator itrShortWrap(Iterable backing) { -+ return new WrappingShortIterator(backing.iterator()); -+ } -+ -+ public static class WrapperObjectIterator implements ObjectIterator { -+ -+ Iterator parent; -+ -+ public WrapperObjectIterator(Iterator parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return parent.hasNext(); -+ } -+ -+ @Override -+ public T next() { -+ return parent.next(); -+ } -+ -+ @Override -+ public void remove() { -+ parent.remove(); -+ } -+ -+ } -+ -+ public static class IntWrapperEntry implements Entry { -+ -+ Map.Entry parent; -+ -+ public IntWrapperEntry(Map.Entry parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public T getValue() { -+ return parent.getValue(); -+ } -+ -+ @Override -+ public T setValue(T value) { -+ return parent.setValue(value); -+ } -+ -+ @Override -+ public int getIntKey() { -+ return parent.getKey(); -+ } -+ -+ @Override -+ public Integer getKey() { -+ return parent.getKey(); -+ } -+ -+ } -+ -+ public static class Long2IntWrapperEntry implements Long2IntMap.Entry { -+ -+ Map.Entry parent; -+ -+ public Long2IntWrapperEntry(Map.Entry parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public long getLongKey() { -+ return parent.getKey(); -+ } -+ -+ @Override -+ public int getIntValue() { -+ return parent.getValue(); -+ } -+ -+ @Override -+ public int setValue(int value) { -+ return parent.setValue(value); -+ } -+ -+ -+ } -+ -+ public static class WrapperIntEntryObjectIterator implements ObjectIterator> { -+ -+ Iterator> parent; -+ -+ public WrapperIntEntryObjectIterator(Iterator> parent) { -+ this.parent = parent; -+ } -+ -+ @Override -+ public boolean hasNext() { -+ return parent.hasNext(); -+ } -+ -+ @Override -+ public Entry next() { -+ Map.Entry val = parent.next(); -+ if (val == null) return null; -+ return new IntWrapperEntry(val); -+ } -+ -+ @Override -+ public void remove() { -+ parent.remove(); -+ } -+ -+ } -+ -+ public static ObjectIterator> intMapItrFake(Map in) { -+ return new WrapperIntEntryObjectIterator(in.entrySet().iterator()); -+ } -+ -+ public static ObjectIterator itrWrap(Iterator in) { -+ return new WrapperObjectIterator(in); -+ } -+ -+ public static ObjectIterator itrWrap(Iterable in) { -+ return new WrapperObjectIterator(in.iterator()); -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..bb6c592fd34423fdd910feae83a058d288da537a ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java -@@ -0,0 +1,93 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -+import it.unimi.dsi.fastutil.ints.IntSet; -+import it.unimi.dsi.fastutil.objects.ObjectCollection; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+import org.apache.commons.lang3.NotImplementedException; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class Int2ObjectConcurrentHashMap implements Int2ObjectMap { -+ -+ Map backing; -+ -+ public Int2ObjectConcurrentHashMap() { -+ backing = new ConcurrentHashMap(); -+ } -+ -+ @Override -+ public V get(int key) { -+ return backing.get(key); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean containsValue(Object value) { -+ return backing.containsValue(value); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(V rv) { -+ throw new NotImplementedException("MCMT - Not implemented"); -+ } -+ -+ @Override -+ public V defaultReturnValue() { -+ return null; -+ } -+ -+ @Override -+ public ObjectSet> int2ObjectEntrySet() { -+ return FastUtilHackUtil.entrySetIntWrap(backing); -+ } -+ -+ -+ @Override -+ public IntSet keySet() { -+ return FastUtilHackUtil.wrapIntSet(backing.keySet()); -+ } -+ -+ @Override -+ public ObjectCollection values() { -+ return FastUtilHackUtil.wrap(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(int key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public V put(int key, V value) { -+ return backing.put(key, value); -+ } -+ -+ @Override -+ public V put(Integer key, V value) { -+ return backing.put(key, value); -+ } -+ -+ @Override -+ public V remove(int key) { -+ return backing.remove(key); -+ } -+ -+ @Override -+ public void clear() { backing.clear(); } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e0fab16860e1be817fd10dbec684f295f2e291dd ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java -@@ -0,0 +1,99 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.bytes.ByteCollection; -+import it.unimi.dsi.fastutil.longs.Long2ByteMap; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class Long2ByteConcurrentHashMap implements Long2ByteMap { -+ -+ Map backing; -+ byte defaultReturn = 0; -+ byte nullKey = 0; -+ -+ public Long2ByteConcurrentHashMap() { -+ backing = new ConcurrentHashMap<>(); -+ } -+ -+ public Long2ByteConcurrentHashMap(int initialCapacity, float loadFactor) { -+ backing = new ConcurrentHashMap<>(initialCapacity, loadFactor); -+ } -+ -+ @Override -+ public byte get(long key) { -+ Byte out = backing.get(key); -+ return out == null ? defaultReturn : out; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean containsValue(byte value) { -+ return backing.containsValue(value); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(byte rv) { -+ defaultReturn = rv; -+ } -+ -+ @Override -+ public byte defaultReturnValue() { -+ return defaultReturn; -+ } -+ -+ @Override -+ public ObjectSet long2ByteEntrySet() { -+ return FastUtilHackUtil.entrySetLongByteWrap(backing); -+ } -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public ByteCollection values() { -+ return FastUtilHackUtil.wrapBytes(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public byte put(long key, byte value) { -+ return put((Long) key, (Byte) value); -+ } -+ -+ @Override -+ public Byte put(Long key, Byte value) { -+ Byte out = backing.put(key, value); -+ return out == null ? Byte.valueOf(defaultReturn) : out; -+ } -+ -+ @Override -+ public byte remove(long key) { -+ Byte out = backing.remove(key); -+ return out == null ? defaultReturn : out; -+ } -+ -+ -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..261f06a88021a95b6a0500444665547aeb4ae2c1 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java -@@ -0,0 +1,74 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.ints.IntCollection; -+import it.unimi.dsi.fastutil.longs.Long2IntMap; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class Long2IntConcurrentHashMap implements Long2IntMap { -+ -+ public Map backing = new ConcurrentHashMap(); -+ int defaultRV = 0; -+ -+ @Override -+ public int get(long key) { -+ if (backing.containsKey(key)) { -+ return backing.get(key); -+ } else return defaultRV; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(int rv) { -+ defaultRV = rv; -+ } -+ -+ @Override -+ public int defaultReturnValue() { -+ return defaultRV; -+ } -+ -+ @Override -+ public ObjectSet long2IntEntrySet() { -+ return null; -+ } -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public IntCollection values() { -+ return FastUtilHackUtil.wrapInts(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public boolean containsValue(int value) { -+ return backing.containsValue(value); -+ } -+ -+ -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..dc2342486318721d399c7c60a0a859befb4d1c9f ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java -@@ -0,0 +1,375 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.Hash; -+import it.unimi.dsi.fastutil.ints.IntCollection; -+import it.unimi.dsi.fastutil.longs.*; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+@SuppressWarnings("deprecation") -+public class Long2IntConcurrentNonLinkedOpenMap extends Long2IntLinkedOpenHashMap { -+ -+ /** -+ * -+ */ -+ private static final long serialVersionUID = -2082212127278131631L; -+ -+ public Map backing = new ConcurrentHashMap(); -+ -+ public Long2IntConcurrentNonLinkedOpenMap(final int expected, final float f) { -+ -+ } -+ -+ /** -+ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. -+ * -+ * @param expected the expected number of elements in the hash map. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final int expected) { -+ } -+ -+ /** -+ * Creates a new hash map with initial expected -+ * {@link Hash#DEFAULT_INITIAL_SIZE} entries and -+ * {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap() { -+ } -+ -+ /** -+ * Creates a new hash map copying a given one. -+ * -+ * @param m a {@link Map} to be copied into the new hash map. -+ * @param f the load factor. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final Map m, final float f) { -+ putAll(m); -+ } -+ -+ /** -+ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor -+ * copying a given one. -+ * -+ * @param m a {@link Map} to be copied into the new hash map. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final Map m) { -+ this(m, DEFAULT_LOAD_FACTOR); -+ } -+ -+ /** -+ * Creates a new hash map copying a given type-specific one. -+ * -+ * @param m a type-specific map to be copied into the new hash map. -+ * @param f the load factor. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m, final float f) { -+ this(m.size(), f); -+ putAll(m); -+ } -+ -+ /** -+ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor -+ * copying a given type-specific one. -+ * -+ * @param m a type-specific map to be copied into the new hash map. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m) { -+ this(m, DEFAULT_LOAD_FACTOR); -+ } -+ -+ /** -+ * Creates a new hash map using the elements of two parallel arrays. -+ * -+ * @param k the array of keys of the new hash map. -+ * @param v the array of corresponding values in the new hash map. -+ * @param f the load factor. -+ * @throws IllegalArgumentException if {@code k} and {@code v} have different -+ * lengths. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v, final float f) { -+ if (k.length != v.length) -+ throw new IllegalArgumentException( -+ "The key array and the value array have different lengths (" + k.length + " and " + v.length + ")"); -+ for (int i = 0; i < k.length; i++) -+ this.put(k[i], v[i]); -+ } -+ -+ /** -+ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor -+ * using the elements of two parallel arrays. -+ * -+ * @param k the array of keys of the new hash map. -+ * @param v the array of corresponding values in the new hash map. -+ * @throws IllegalArgumentException if {@code k} and {@code v} have different -+ * lengths. -+ */ -+ public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v) { -+ this(k, v, DEFAULT_LOAD_FACTOR); -+ } -+ -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ public int put(final long k, final int v) { -+ Integer out = backing.put(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int addTo(final long k, final int incr) { -+ Integer out = backing.put(k, this.get(k)+incr); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int remove(final long k) { -+ Integer out = backing.remove(k); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int removeFirstInt() { -+ Integer out = this.remove(backing.keySet().stream().findAny().get()); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int removeLastInt() { -+ Integer out = this.remove(backing.keySet().stream().findAny().get()); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public int getAndMoveToFirst(final long k) { -+ Integer out = backing.get(k); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int getAndMoveToLast(final long k) { -+ Integer out = backing.get(k); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int putAndMoveToFirst(final long k, final int v) { -+ Integer out = backing.put(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int putAndMoveToLast(final long k, final int v) { -+ Integer out = backing.put(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int get(final long k) { -+ Integer out = backing.get(k); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public boolean containsKey(final long k) { -+ return backing.containsKey(k); -+ } -+ -+ public boolean containsValue(final int v) { -+ return backing.containsValue(v); -+ } -+ -+ public int getOrDefault(final long k, final int defaultValue) { -+ Integer out = backing.getOrDefault(k, defaultValue); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ public int putIfAbsent(final long k, final int v) { -+ Integer out = backing.putIfAbsent(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public boolean remove(final long k, final int v) { -+ return backing.remove(k, v); -+ } -+ -+ -+ public boolean replace(final long k, final int oldValue, final int v) { -+ return backing.replace(k, oldValue, v); -+ } -+ -+ -+ public int replace(final long k, final int v) { -+ Integer out = backing.replace(k, v); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public int computeIfAbsent(final long k, final java.util.function.LongToIntFunction mappingFunction) { -+ Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.applyAsInt(l)); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public int computeIfAbsentNullable(final long k, -+ final java.util.function.LongFunction mappingFunction) { -+ Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.apply(l)); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ -+ public int computeIfPresent(final long k, -+ final java.util.function.BiFunction remappingFunction) { -+ if (this.containsKey(k)) { -+ Integer out = backing.put(k, remappingFunction.apply(k, backing.get(k))); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ return defaultReturnValue(); -+ -+ } -+ -+ @Override -+ public int compute(final long k, -+ final java.util.function.BiFunction remappingFunction) { -+ Integer out = backing.compute(k, remappingFunction); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ @Override -+ public int merge(final long k, final int v, -+ final java.util.function.BiFunction remappingFunction) { -+ Integer out = backing.merge(k, v, remappingFunction); -+ if (out == null) { -+ return defRetValue; -+ } -+ return out; -+ } -+ -+ @Override -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public long firstLongKey() { -+ return backing.keySet().stream().findAny().get(); -+ } -+ -+ @Override -+ public long lastLongKey() { -+ return backing.keySet().stream().findAny().get(); -+ } -+ -+ @Override -+ public Long2IntSortedMap tailMap(long from) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public Long2IntSortedMap headMap(long to) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public Long2IntSortedMap subMap(long from, long to) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongComparator comparator() { -+ return null; -+ } -+ -+ -+ @Override -+ public FastSortedEntrySet long2IntEntrySet() { -+ //TODO implement -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public LongSortedSet keySet() { -+ return FastUtilHackUtil.wrapLongSortedSet(backing.keySet()); -+ } -+ -+ -+ @Override -+ public IntCollection values() { -+ return FastUtilHackUtil.wrapInts(backing.values()); -+ } -+ -+ public boolean trim() { -+ return true; -+ } -+ -+ public boolean trim(final int n) { -+ return true; -+ } -+ -+ -+ @Override -+ public Long2IntConcurrentNonLinkedOpenMap clone() { -+ return new Long2IntConcurrentNonLinkedOpenMap(backing); -+ } -+ -+ @Override -+ public int hashCode() { -+ return backing.hashCode(); -+ } -+ -+ -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3205d30a03f99caf7dfa05237b2bc31182b2db20 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java -@@ -0,0 +1,97 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.longs.Long2LongMap; -+import it.unimi.dsi.fastutil.longs.LongCollection; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+ -+public class Long2LongConcurrentHashMap implements Long2LongMap { -+ -+ public Map backing = new ConcurrentHashMap(); -+ long defaultRV = 0; -+ -+ public Long2LongConcurrentHashMap(long defaultRV) { -+ this.defaultRV = defaultRV; -+ } -+ -+ @Override -+ public long get(long key) { -+ if (backing.containsKey(key)) { -+ return backing.get(key); -+ } else return defaultRV; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public long put(final long key, final long val) { -+ backing.put(key,val); -+ return val; -+ } -+ -+ @Override -+ public Long put(final Long key, final Long val) { -+ backing.put(key,val); -+ return val; -+ } -+ -+ @Override -+ public long remove(final long key) { -+ return backing.remove(key); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(long rv) { -+ defaultRV = rv; -+ } -+ -+ @Override -+ public long defaultReturnValue() { -+ return defaultRV; -+ } -+ -+ @Override -+ public ObjectSet long2LongEntrySet() { -+ return FastUtilHackUtil.entrySetLongLongWrap(backing); -+ } -+ -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public LongCollection values() { -+ return FastUtilHackUtil.wrapLongs(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public boolean containsValue(long value) { -+ return backing.containsValue(value); -+ } -+ -+ -+} -\ No newline at end of file -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a5ed71564d4c9a986f77cbc0397130aa38f97a91 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java -@@ -0,0 +1,93 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectCollection; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+public class Long2ObjectConcurrentHashMap implements Long2ObjectMap { -+ -+ Map backing; -+ V defaultReturn = null; -+ -+ public Long2ObjectConcurrentHashMap() { -+ backing = new ConcurrentHashMap(); -+ } -+ -+ @Override -+ public V get(long key) { -+ V out = backing.get(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean containsValue(Object value) { -+ return backing.containsValue(value); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(V rv) { -+ defaultReturn = rv; -+ } -+ -+ @Override -+ public V defaultReturnValue() { -+ return defaultReturn; -+ } -+ -+ @Override -+ public ObjectSet> long2ObjectEntrySet() { -+ return FastUtilHackUtil.entrySetLongWrap(backing); -+ } -+ -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public ObjectCollection values() { -+ return FastUtilHackUtil.wrap(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public V put(long key, V value) { -+ return put((Long)key, value); -+ } -+ -+ @Override -+ public V put(Long key, V value) { -+ V out = backing.put(key, value); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); -+ } -+ -+ @Override -+ public V remove(long key) { -+ V out = backing.remove(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+} -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a7d6be048ab3b8bd38231fce16eca0ac78e24690 ---- /dev/null -+++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java -@@ -0,0 +1,233 @@ -+package net.himeki.mcmtfabric.parallelised.fastutil; -+ -+import it.unimi.dsi.fastutil.longs.Long2ObjectFunction; -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -+import it.unimi.dsi.fastutil.longs.LongSet; -+import it.unimi.dsi.fastutil.objects.ObjectCollection; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+import java.util.function.Function; -+ -+public class Long2ObjectOpenConcurrentHashMap extends Long2ObjectOpenHashMap { -+ -+ /** -+ * -+ */ -+ private static final long serialVersionUID = -121514116954680057L; -+ -+ Map backing; -+ V defaultReturn = null; -+ -+ public Long2ObjectOpenConcurrentHashMap() { -+ backing = new ConcurrentHashMap(); -+ } -+ -+ @Override -+ public V get(long key) { -+ V out = backing.get(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+ -+ @Override -+ public V get(Object key) { -+ V out = backing.get(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return backing.isEmpty(); -+ } -+ -+ @Override -+ public boolean containsValue(Object value) { -+ return backing.containsValue(value); -+ } -+ -+ @Override -+ public void putAll(Map m) { -+ backing.putAll(m); -+ } -+ -+ @Override -+ public int size() { -+ return backing.size(); -+ } -+ -+ @Override -+ public void defaultReturnValue(V rv) { -+ defaultReturn = rv; -+ } -+ -+ @Override -+ public V defaultReturnValue() { -+ return defaultReturn; -+ } -+ -+ @Override -+ public FastEntrySet long2ObjectEntrySet() { -+ return FastUtilHackUtil.entrySetLongWrapFast(backing); -+ } -+ -+ -+ @Override -+ public LongSet keySet() { -+ return FastUtilHackUtil.wrapLongSet(backing.keySet()); -+ } -+ -+ @Override -+ public ObjectCollection values() { -+ return FastUtilHackUtil.wrap(backing.values()); -+ } -+ -+ @Override -+ public boolean containsKey(long key) { -+ return backing.containsKey(key); -+ } -+ -+ @Override -+ public V put(long key, V value) { -+ return put((Long)key, value); -+ } -+ -+ @Override -+ public V put(Long key, V value) { -+ V out = backing.put(key, value); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); -+ } -+ -+ @Override -+ public V remove(long key) { -+ V out = backing.remove(key); -+ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; -+ } -+ -+ @Override -+ public boolean trim() { return true; } -+ -+ @Override -+ public boolean trim(final int n) { return true; } -+ -+ @Override -+ public boolean replace(final long k, final V oldValue, final V v) { -+ return backing.replace(k, oldValue, v); -+ } -+ -+ @Override -+ public V replace(final long k, final V v) { -+ return backing.replace(k, v); -+ } -+ -+ @Override -+ public boolean replace(final Long k, final V oldValue, final V v) { -+ return backing.replace(k, oldValue, v); -+ } -+ -+ @Override -+ public V replace(final Long k, final V v) { -+ return backing.replace(k, v); -+ } -+ -+ @Override -+ public boolean remove(final long k, final Object v) { -+ return backing.remove(k, v); -+ } -+ -+ @Override -+ public V putIfAbsent(final long k, final V v) { -+ return backing.putIfAbsent(k, v); -+ } -+ -+ @Override -+ public V putIfAbsent(final Long k, final V v) { -+ return backing.putIfAbsent(k, v); -+ } -+ -+ @Override -+ public V merge(final long k, final V v, final java.util.function.BiFunction remappingFunction) { -+ return backing.merge(k, v, remappingFunction); -+ } -+ -+ @Override -+ public V merge(Long k, final V v, final java.util.function.BiFunction remappingFunction) { -+ return backing.merge(k, v, remappingFunction); -+ } -+ -+ @Override -+ public int hashCode() { -+ return backing.hashCode(); -+ } -+ -+ @Override -+ public V getOrDefault(final long k, final V defaultValue) { -+ return backing.getOrDefault(k, defaultValue); -+ } -+ -+ @Override -+ public V getOrDefault(Object k, final V defaultValue) { -+ return backing.getOrDefault(k, defaultValue); -+ } -+ -+ @Override -+ public V computeIfPresent(final long k, final java.util.function.BiFunction remappingFunction) { -+ return backing.computeIfPresent(k, remappingFunction); -+ } -+ -+ @Override -+ public V computeIfPresent(final Long k, final java.util.function.BiFunction remappingFunction) { -+ return backing.computeIfPresent(k, remappingFunction); -+ } -+ -+ @Override -+ public V computeIfAbsent(final long k, final java.util.function.LongFunction mappingFunction) { -+ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); -+ } -+ -+ public V computeIfAbsent(final Long k, final java.util.function.LongFunction mappingFunction) { -+ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); -+ } -+ -+ @Override -+ public V computeIfAbsentPartial(final long key, final Long2ObjectFunction mappingFunction) { -+ if (!mappingFunction.containsKey(key)) -+ return defaultReturn; -+ return backing.computeIfAbsent(key, (llong) -> mappingFunction.apply(llong)); -+ } -+ -+ @Override -+ public V compute(final long k, final java.util.function.BiFunction remappingFunction) { -+ return backing.compute(k, remappingFunction); -+ } -+ -+ @Override -+ public V compute(final Long k, final java.util.function.BiFunction remappingFunction) { -+ return backing.compute(k, remappingFunction); -+ } -+ -+ @Override -+ public Long2ObjectOpenHashMap clone() { -+ throw new IllegalArgumentException(); -+ } -+ -+ public void clear() { -+ backing.clear(); -+ } -+ -+ @Override -+ public ObjectSet> entrySet() { -+ return new FastUtilHackUtil.ConvertingObjectSet, Map.Entry>(backing.entrySet(), Function.identity(), Function.identity()); -+ } -+ -+ @Override -+ public V remove(Object key) { -+ return backing.remove(key); -+ } -+ -+ @Override -+ public boolean remove(Object key, Object value) { -+ return backing.remove(key, value); -+ } -+ -+} -\ No newline at end of file -diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java -new file mode 100644 -index 0000000000000000000000000000000000000000..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 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 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 implements CommandSource, AutoCloseable { - -@@ -284,6 +216,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { -+ 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 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= 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 entityMap; -+ public final Map entityMap; - private final Long2ByteMap chunkTypeCache; - private final Long2LongMap chunkSaveCooldowns; - private final Queue unloadQueue; - int viewDistance; - public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper -- public final ReferenceOpenHashSet needsChangeBroadcasting = new ReferenceOpenHashSet<>(); -+ public final ReferenceSet needsChangeBroadcasting = ReferenceSets.synchronize(new ReferenceOpenHashSet<>()); - - // Paper - rewrite chunk system - // Paper start - optimise checkDespawn -@@ -295,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 list = Lists.newArrayList(); - List list1 = this.level.players(); -- ObjectIterator objectiterator = this.entityMap.values().iterator(); -+ Iterator objectiterator = this.entityMap.values().iterator(); - //level.timings.tracker1.startTiming(); // Paper // Purpur - - ChunkMap.TrackedEntity playerchunkmap_entitytracker; -diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java -index 52cba8f68d274cce106304aef1249a95474d3238..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> playersPerChunk = new Long2ObjectOpenHashMap(); -+ final Long2ObjectMap> 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 copy = this.chunkMap.needsChangeBroadcasting.clone(); -+ List copy = new ArrayList<>(this.chunkMap.needsChangeBroadcasting); - this.chunkMap.needsChangeBroadcasting.clear(); - for (ChunkHolder holder : copy) { - holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded -@@ -848,12 +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 objectiterator = -- level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); -+ Iterator objectiterator = level.entityTickList.entities.iterator(); - gg.pufferfish.pufferfish.util.IterableWrapper wrappedIterator = - new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator); - lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true); -- objectiterator.finishedIterating(); - _pufferfish_spawnCountsReady.set(true); - }); - } -diff --git a/src/main/java/net/minecraft/server/level/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 navigatingMobs; - volatile boolean isUpdatingNavigations; - protected final Raids raids; -- private final ObjectLinkedOpenHashSet blockEvents; -+ private final Deque blockEvents; - private final List blockEventsToReschedule; - private boolean handlingTick; - private final List customSpawners; -@@ -269,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> 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 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 list = new ObjectArrayList(); -- Iterator iterator = this.navigatingMobs.iterator(); -+ if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) { -+ List list = new ObjectArrayList(); -+ Iterator iterator = this.navigatingMobs.iterator(); - -- while (iterator.hasNext()) { -- // CraftBukkit start - fix SPIGOT-6362 -- Mob entityinsentient; -- try { -- entityinsentient = (Mob) iterator.next(); -- } catch (java.util.ConcurrentModificationException ex) { -- // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register -- // In this case we just run the update again across all the iterators as the chunk will then be loaded -- // As this is a relative edge case it is much faster than copying navigators (on either read or write) -- this.sendBlockUpdated(pos, oldState, newState, flags); -- return; -- } -- // CraftBukkit end -- PathNavigation navigationabstract = entityinsentient.getNavigation(); -+ while (iterator.hasNext()) { -+ // CraftBukkit start - fix SPIGOT-6362 -+ Mob entityinsentient; -+ try { -+ entityinsentient = (Mob) iterator.next(); -+ } catch (java.util.ConcurrentModificationException ex) { -+ // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register -+ // In this case we just run the update again across all the iterators as the chunk will then be loaded -+ // As this is a relative edge case it is much faster than copying navigators (on either read or write) -+ this.sendBlockUpdated(pos, oldState, newState, flags); -+ return; -+ } -+ // CraftBukkit end -+ PathNavigation navigationabstract = entityinsentient.getNavigation(); - -- if (navigationabstract.shouldRecomputePath(pos)) { -- list.add(navigationabstract); -+ if (navigationabstract.shouldRecomputePath(pos)) { -+ list.add(navigationabstract); -+ } - } -- } - -- try { -- this.isUpdatingNavigations = true; -- iterator = list.iterator(); -+ try { -+ this.isUpdatingNavigations = true; -+ iterator = list.iterator(); - -- while (iterator.hasNext()) { -- PathNavigation navigationabstract1 = (PathNavigation) iterator.next(); -+ while (iterator.hasNext()) { -+ PathNavigation navigationabstract1 = (PathNavigation) iterator.next(); - -- navigationabstract1.recomputePath(); -+ 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 extends AbstractCollection { -- private final Map, List> byClass = Maps.newHashMap(); -+ private final Map, List> byClass = Maps.newConcurrentMap(); - private final Class baseClass; -- private final List allInstances = Lists.newArrayList(); -+ private final List allInstances = Lists.newCopyOnWriteArrayList(); - - public ClassInstanceMultiMap(Class elementType) { - this.baseClass = elementType; -@@ -58,22 +60,24 @@ public class ClassInstanceMultiMap extends AbstractCollection { - if (!this.baseClass.isAssignableFrom(type)) { - throw new IllegalArgumentException("Don't know how to search for " + type); - } else { -- List list = this.byClass.computeIfAbsent(type, (typeClass) -> { -- return this.allInstances.stream().filter(typeClass::isInstance).collect(Collectors.toList()); -- }); -- return Collections.unmodifiableCollection(list); -+ List list = this.byClass.computeIfAbsent(type, (typeClass) -> this.allInstances.stream().filter(typeClass::isInstance).collect(toList())); -+ return (Collection) Collections.unmodifiableCollection(list); - } - } - - @Override - public Iterator iterator() { -- return (Iterator)(this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator())); -+ return this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator()); - } - - public List getAllInstances() { - return ImmutableList.copyOf(this.allInstances); - } - -+ public static Collector> 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 implements ProfilerMeasured, ProcessorHandle, Executor { - private final String name; - private static final Logger LOGGER = LogUtils.getLogger(); -- private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); -+ private final Deque pendingRunnables = new ConcurrentLinkedDeque<>(); - private int blockingCount; - - protected BlockableEventLoop(String name) { -@@ -117,13 +120,14 @@ public abstract class BlockableEventLoop implements Profiler - } - - public boolean pollTask() { -- R runnable = this.pendingRunnables.peek(); -+ R runnable = this.pendingRunnables.poll(); - if (runnable == null) { - return false; - } else if (this.blockingCount == 0 && !this.shouldRun(runnable)) { -+ this.pendingRunnables.addFirst(runnable); - return false; - } else { -- this.doRunTask(this.pendingRunnables.remove()); -+ this.doRunTask(runnable); - return true; - } - } -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 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 extends Behavior { - 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 ALLOWED_ANGLES = Lists.newArrayList(65, 70, 75, 80); -+ private static final List 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 jumpCandidates = Lists.newArrayList(); -+ protected List jumpCandidates = Lists.newCopyOnWriteArrayList(); - protected Optional 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 gossips = Maps.newHashMap(); -+ public final Map gossips = Maps.newConcurrentMap(); - - @VisibleForDebug - public Map> getGossipEntries() { -- Map> map = Maps.newHashMap(); -+ Map> map = Maps.newConcurrentMap(); - this.gossips.keySet().forEach((uuid) -> { - GossipContainer.EntityGossips entityGossips = this.gossips.get(uuid); - map.put(uuid, entityGossips.entries); -diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java -index 9f138bc471b5c2a4fa813ff943dbe34018b8df74..5c8a90f8536c9291df5891d8c75de963b75ec4bd 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java -+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java -@@ -7,6 +7,7 @@ import com.mojang.logging.LogUtils; - import com.mojang.serialization.Codec; - import com.mojang.serialization.codecs.RecordCodecBuilder; - import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -+import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps; - import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; - import java.util.List; - import java.util.Map; -@@ -25,8 +26,9 @@ import org.slf4j.Logger; - - public class PoiSection { - private static final Logger LOGGER = LogUtils.getLogger(); -- private final Short2ObjectMap records = new Short2ObjectOpenHashMap<>(); -- private final Map, Set> byType = Maps.newHashMap(); public final Map, Set> getData() { return this.byType; } // Paper - public accessor -+ private final Short2ObjectMap records = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>()); -+ private final Map, Set> byType = Maps.newConcurrentMap(); -+ public final Map, Set> getData() { return this.byType; } // Paper - public accessor - private final Runnable setDirty; - private boolean isValid; - public final Optional noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system -diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -index c58496c84b2b3f86890050813041fa49711f3a01..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 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 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 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> storage = new Long2ObjectOpenHashMap<>(); -- private final LongLinkedOpenHashSet dirty = new LongLinkedOpenHashSet(); -+ private final Long2ObjectMap> storage = new Long2ObjectOpenConcurrentHashMap<>(); -+ private final LongLinkedOpenHashSet dirty = new ConcurrentLongLinkedOpenHashSet(); - private final Function> codec; - private final Function 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 { - private static final Logger LOGGER = LogUtils.getLogger(); -- private final Int2ObjectMap byId = new Int2ObjectLinkedOpenHashMap<>(); -- private final Map byUuid = Maps.newHashMap(); -+ private final Int2ObjectMap byId = Int2ObjectMaps.synchronize(new Int2ObjectLinkedOpenHashMap<>()); -+ private final Map byUuid = Maps.newConcurrentMap(); - - public void getEntities(EntityTypeTest filter, AbortableIterationConsumer consumer) { - for(T entityAccess : this.byId.values()) { -diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -index 57fcf3910f45ce371ac2e237b277b1034caaac4e..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 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 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 action) { - io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist iteration"); // Paper -- // Paper start - replace with better logic, do not delay removals/additions -- // To ensure nothing weird happens with dimension travelling, do not iterate over new entries... -- // (by dfl iterator() is configured to not iterate over new entries) -- io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator iterator = this.entities.iterator(); -- try { -- while (iterator.hasNext()) { -- action.accept(iterator.next()); -- } -- } finally { -- iterator.finishedIterating(); -+ -+ if (MinecraftServer.getServer().asyncEntityEnabled){ -+ final List 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 implements AutoCloseable { - - static final Logger LOGGER = LogUtils.getLogger(); -- final Set knownUuids = Sets.newHashSet(); -+ final Set knownUuids = Sets.newConcurrentHashSet(); - final LevelCallback callbacks; - public final EntityPersistentStorage permanentStorage; - private final EntityLookup visibleEntityStorage = new EntityLookup<>(); - final EntitySectionStorage sectionStorage; - private final LevelEntityGetter entityGetter; -- private final Long2ObjectMap chunkVisibility = new Long2ObjectOpenHashMap(); -- private final Long2ObjectMap chunkLoadStatuses = new Long2ObjectOpenHashMap(); -- private final LongSet chunksToUnload = new LongOpenHashSet(); -+ private final Long2ObjectMap chunkVisibility = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); -+ private final Long2ObjectMap chunkLoadStatuses = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); -+ private final LongSet chunksToUnload = LongSets.synchronize(new LongOpenHashSet()); - private final Queue> loadingInbox = Queues.newConcurrentLinkedQueue(); - - public PersistentEntitySectionManager(Class entityClass, LevelCallback handler, EntityPersistentStorage dataAccess) { -diff --git a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java -index 878a42695ecedf0c3f2e6310e3ce44c6b6c36858..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 listeners = Lists.newArrayList(); -- private final Set listenersToRemove = Sets.newHashSet(); -- private final List listenersToAdd = Lists.newArrayList(); -+ private final List listeners = new ConcurrentDoublyLinkedList<>(); -+ private final Set listenersToRemove = Sets.newConcurrentHashSet(); -+ private final List listenersToAdd = Lists.newCopyOnWriteArrayList(); - private boolean processing; - private final ServerLevel level; - -diff --git a/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java b/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java -index daa03360dd7044f10b20f36023b305dc7e0bb7df..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 pathTypeByPosCache = new Long2ObjectOpenHashMap<>(); -+ private final Long2ObjectMap pathTypeByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); - private static final float SMALL_MOB_INFLATED_START_NODE_BOUNDING_BOX = 1.5F; - private static final int MAX_START_NODE_CANDIDATES = 10; - -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java -index a8a2594b8f5b3ebf6a1f918c7d822ad35b051b17..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 nodes = new Int2ObjectOpenHashMap<>(); -+ protected final Int2ObjectMap 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 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 positions, float followRange, int distance, float rangeMultiplier) { -+ public synchronized Path findPath(PathNavigationRegion world, Mob mob, Set positions, float followRange, int distance, float rangeMultiplier) { - this.openSet.clear(); - this.nodeEvaluator.prepare(world, mob); - Node node = this.nodeEvaluator.getStart(); -@@ -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 entry = positions.get(i1); -+ for (final Map.Entry 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 pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); -+ private final Long2ObjectMap pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); - - public SwimNodeEvaluator(boolean canJumpOutOfWater) { - this.allowBreaching = canJumpOutOfWater; -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -index 894881018c659d874f28f5744f0b8247cfecb1c1..ae06f7ef9c4b8147508984f8b46176de46171285 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -@@ -1,8 +1,10 @@ - package net.minecraft.world.level.pathfinder; - - import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; - import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; - import it.unimi.dsi.fastutil.objects.Object2BooleanMap; -+import it.unimi.dsi.fastutil.objects.Object2BooleanMaps; - import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; - import java.util.EnumSet; - import javax.annotation.Nullable; -@@ -33,8 +35,8 @@ public class WalkNodeEvaluator extends NodeEvaluator { - public static final double SPACE_BETWEEN_WALL_POSTS = 0.5D; - private static final double DEFAULT_MOB_JUMP_HEIGHT = 1.125D; - protected float oldWaterCost; -- private final Long2ObjectMap pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); -- private final Object2BooleanMap collisionCache = new Object2BooleanOpenHashMap<>(); -+ private final Long2ObjectMap pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); -+ private final Object2BooleanMap collisionCache = Object2BooleanMaps.synchronize(new Object2BooleanOpenHashMap<>()); - - @Override - public void prepare(PathNavigationRegion cachedWorld, Mob entity) { -diff --git a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java -index b1c594dc6a6b8a6c737b99272acab9e7dbd0ed63..4aedee56077159aaf613033b688d2be6833f1ad1 100644 ---- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java -+++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java -@@ -3,7 +3,10 @@ package net.minecraft.world.level.redstone; - import com.mojang.logging.LogUtils; - import java.util.ArrayDeque; - import java.util.ArrayList; -+import java.util.Deque; - import java.util.List; -+import java.util.concurrent.ConcurrentLinkedDeque; -+import java.util.concurrent.CopyOnWriteArrayList; - import javax.annotation.Nullable; - import net.minecraft.core.BlockPos; - import net.minecraft.core.Direction; -@@ -16,8 +19,8 @@ public class CollectingNeighborUpdater implements NeighborUpdater { - private static final Logger LOGGER = LogUtils.getLogger(); - private final Level level; - private final int maxChainedNeighborUpdates; -- private final ArrayDeque stack = new ArrayDeque<>(); -- private final List addedThisLayer = new ArrayList<>(); -+ private final Deque stack = new ConcurrentLinkedDeque<>(); -+ private final List addedThisLayer = new CopyOnWriteArrayList<>(); - private int count = 0; - - public CollectingNeighborUpdater(Level world, int maxChainDepth) { -@@ -26,22 +29,22 @@ public class CollectingNeighborUpdater implements NeighborUpdater { - } - - @Override -- public void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) { -+ public synchronized void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) { - this.addAndRun(pos, new CollectingNeighborUpdater.ShapeUpdate(direction, neighborState, pos.immutable(), neighborPos.immutable(), flags)); - } - - @Override -- public void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) { -+ public synchronized void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) { - this.addAndRun(pos, new CollectingNeighborUpdater.SimpleNeighborUpdate(pos, sourceBlock, sourcePos.immutable())); - } - - @Override -- public void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) { -+ public synchronized void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) { - this.addAndRun(pos, new CollectingNeighborUpdater.FullNeighborUpdate(state, pos.immutable(), sourceBlock, sourcePos.immutable(), notify)); - } - - @Override -- public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) { -+ public synchronized void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) { - this.addAndRun(pos, new CollectingNeighborUpdater.MultiNeighborUpdate(pos.immutable(), sourceBlock, except)); - } - -diff --git a/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 implements LevelTickAccess { -- private static final Comparator> CONTAINER_DRAIN_ORDER = (a, b) -> { -- return ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek()); -- }; -+ private static final Comparator> CONTAINER_DRAIN_ORDER = (a, b) -> ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek()); - private final LongPredicate tickCheck; - private final Supplier profiler; -- private final Long2ObjectMap> allContainers = new Long2ObjectOpenHashMap<>(); -- private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), (map) -> { -+ private final Long2ObjectMap> allContainers = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); -+ private final Long2LongMap nextTickForContainer = Long2LongMaps.synchronize(Util.make(new Long2LongOpenHashMap(), (map) -> { - map.defaultReturnValue(Long.MAX_VALUE); -- }); -+ })); - private final Queue> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER); -- private final Queue> toRunThisTick = new ArrayDeque<>(); -- private final List> alreadyRunThisTick = new ArrayList<>(); -- private final Set> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); -+ private final Queue> toRunThisTick = new ConcurrentLinkedQueue<>(); -+ private final List> alreadyRunThisTick = new CopyOnWriteArrayList<>(); -+ private final Set> toRunThisTickSet = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); -+ private static int taskLimit = HearseConfig.getInt("scheduled-task-limit-per-tick",65535); -+ - private final BiConsumer, ScheduledTick> chunkScheduleUpdater = (chunkTickScheduler, tick) -> { - if (tick.equals(chunkTickScheduler.peek())) { - this.updateContainerScheduling(tick); - } -- - }; - - public LevelTicks(LongPredicate tickingFutureReadyPredicate, Supplier profilerGetter) { -@@ -123,7 +123,9 @@ public class LevelTicks implements LevelTickAccess { - entry.setValue(scheduledTick.triggerTick()); - } else if (this.tickCheck.test(l)) { - objectIterator.remove(); -- this.containersToTick.add(levelChunkTicks); -+ synchronized (this.containersToTick){ -+ this.containersToTick.add(levelChunkTicks); -+ } - } - } - } -@@ -133,27 +135,29 @@ public class LevelTicks implements LevelTickAccess { - - private void drainContainers(long time, int maxTicks) { - LevelChunkTicks levelChunkTicks; -- while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { -- ScheduledTick scheduledTick = levelChunkTicks.poll(); -- this.scheduleForThisTick(scheduledTick); -- this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks); -- ScheduledTick scheduledTick2 = levelChunkTicks.peek(); -- if (scheduledTick2 != null) { -- if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) { -- this.containersToTick.add(levelChunkTicks); -- } else { -- this.updateContainerScheduling(scheduledTick2); -+ synchronized (this.containersToTick){ -+ while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { -+ ScheduledTick scheduledTick = levelChunkTicks.poll(); -+ this.scheduleForThisTick(scheduledTick); -+ this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks); -+ ScheduledTick scheduledTick2 = levelChunkTicks.peek(); -+ if (scheduledTick2 != null) { -+ if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) { -+ this.containersToTick.add(levelChunkTicks); -+ } else { -+ this.updateContainerScheduling(scheduledTick2); -+ } - } - } - } -- - } - - private void rescheduleLeftoverContainers() { -- for(LevelChunkTicks levelChunkTicks : this.containersToTick) { -- this.updateContainerScheduling(levelChunkTicks.peek()); -+ synchronized (this.containersToTick){ -+ for(LevelChunkTicks levelChunkTicks : this.containersToTick) { -+ this.updateContainerScheduling(levelChunkTicks.peek()); -+ } - } -- - } - - private void updateContainerScheduling(ScheduledTick tick) { -@@ -186,24 +190,38 @@ public class LevelTicks implements LevelTickAccess { - return this.toRunThisTick.size() < maxTicks; - } - -+ private boolean allRunned; -+ - private void runCollectedTicks(BiConsumer ticker) { -+ int cycleCounter = 0; - while(!this.toRunThisTick.isEmpty()) { -+ if (cycleCounter >= taskLimit){ -+ break; -+ } - ScheduledTick 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 diff --git a/patches/server/0042-Hearse-Pathfiner-changes.patch b/patches/server/0042-Hearse-Pathfiner-changes.patch new file mode 100644 index 00000000..ec12652b --- /dev/null +++ b/patches/server/0042-Hearse-Pathfiner-changes.patch @@ -0,0 +1,203 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Sun, 8 Jan 2023 21:14:07 +0800 +Subject: [PATCH] Hearse: Pathfiner changes + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java b/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java +index 27b9cefc172b391824ead382a712b8b9b1ddfe45..4b65331a9192b7ac75141183493126ee730e697e 100644 +--- a/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java ++++ b/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java +@@ -4,9 +4,9 @@ public class BinaryHeap { + private Node[] heap = new Node[128]; + private int size; + +- public Node insert(Node node) { ++ public synchronized Node insert(Node node) { + if (node.heapIdx >= 0) { +- throw new IllegalStateException("OW KNOWS!"); ++ return node; + } else { + if (this.size == this.heap.length) { + Node[] nodes = new Node[this.size << 1]; +@@ -21,15 +21,15 @@ public class BinaryHeap { + } + } + +- public void clear() { ++ public synchronized void clear() { + this.size = 0; + } + +- public Node peek() { ++ public synchronized Node peek() { + return this.heap[0]; + } + +- public Node pop() { ++ public synchronized Node pop() { + Node node = this.heap[0]; + this.heap[0] = this.heap[--this.size]; + this.heap[this.size] = null; +@@ -41,7 +41,7 @@ public class BinaryHeap { + return node; + } + +- public void remove(Node node) { ++ public synchronized void remove(Node node) { + this.heap[node.heapIdx] = this.heap[--this.size]; + this.heap[this.size] = null; + if (this.size > node.heapIdx) { +@@ -55,7 +55,7 @@ public class BinaryHeap { + node.heapIdx = -1; + } + +- public void changeCost(Node node, float weight) { ++ public synchronized void changeCost(Node node, float weight) { + float f = node.f; + node.f = weight; + if (weight < f) { +@@ -66,11 +66,14 @@ public class BinaryHeap { + + } + +- public int size() { ++ public synchronized int size() { + return this.size; + } + + private void upHeap(int index) { ++ if(index == -1){ ++ return; ++ } + Node node = this.heap[index]; + + int i; +@@ -90,6 +93,9 @@ public class BinaryHeap { + } + + private void downHeap(int index) { ++ if(index == -1){ ++ return; ++ } + Node node = this.heap[index]; + float f = node.f; + +@@ -135,11 +141,11 @@ public class BinaryHeap { + node.heapIdx = index; + } + +- public boolean isEmpty() { ++ public synchronized boolean isEmpty() { + return this.size == 0; + } + +- public Node[] getHeap() { ++ public synchronized Node[] getHeap() { + Node[] nodes = new Node[this.size()]; + System.arraycopy(this.heap, 0, nodes, 0, this.size()); + return nodes; +diff --git a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java +index b0bae04ab5a93dd4cf1eeeb02bed1e508e1f2913..d427735eff0056c171591709829d0bb76f7bb6f3 100644 +--- a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java ++++ b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java +@@ -1,6 +1,7 @@ + package net.minecraft.world.level.pathfinder; + + import it.unimi.dsi.fastutil.longs.Long2ObjectMap; ++import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; + import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + import java.util.EnumSet; + import java.util.List; +@@ -15,7 +16,7 @@ import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.phys.AABB; + + public class FlyNodeEvaluator extends WalkNodeEvaluator { +- private final Long2ObjectMap pathTypeByPosCache = new Long2ObjectOpenHashMap<>(); ++ private final Long2ObjectMap pathTypeByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); + private static final float SMALL_MOB_INFLATED_START_NODE_BOUNDING_BOX = 1.5F; + private static final int MAX_START_NODE_CANDIDATES = 10; + +diff --git a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java +index a8a2594b8f5b3ebf6a1f918c7d822ad35b051b17..c614bcfc2bbbbccc7c4aac9389d4780478e739d2 100644 +--- a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java ++++ b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java +@@ -1,6 +1,7 @@ + package net.minecraft.world.level.pathfinder; + + import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ++import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; + import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + import net.minecraft.core.BlockPos; + import net.minecraft.util.Mth; +@@ -11,7 +12,7 @@ import net.minecraft.world.level.PathNavigationRegion; + public abstract class NodeEvaluator { + protected PathNavigationRegion level; + protected Mob mob; +- protected final Int2ObjectMap nodes = new Int2ObjectOpenHashMap<>(); ++ protected final Int2ObjectMap nodes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>()); + protected int entityWidth; + protected int entityHeight; + protected int entityDepth; +diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java +index a8af51a25b0f99c3a64d9150fdfcd6b818aa7581..cd2592552339a79361d2a4e7936731330e15f6fa 100644 +--- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java ++++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java +@@ -31,7 +31,7 @@ public class PathFinder { + } + + @Nullable +- public Path findPath(PathNavigationRegion world, Mob mob, Set positions, float followRange, int distance, float rangeMultiplier) { ++ public synchronized Path findPath(PathNavigationRegion world, Mob mob, Set positions, float followRange, int distance, float rangeMultiplier) { + this.openSet.clear(); + this.nodeEvaluator.prepare(world, mob); + Node node = this.nodeEvaluator.getStart(); +diff --git a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java +index 6084631b5b502279b84f190dc62fc76b770e368e..f526adbd31e65fc74af48f6137d293a7a7ceafbb 100644 +--- a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java ++++ b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java +@@ -2,6 +2,7 @@ package net.minecraft.world.level.pathfinder; + + import com.google.common.collect.Maps; + import it.unimi.dsi.fastutil.longs.Long2ObjectMap; ++import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; + import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + import java.util.Map; + import javax.annotation.Nullable; +@@ -17,7 +18,7 @@ import net.minecraft.world.level.material.FluidState; + + public class SwimNodeEvaluator extends NodeEvaluator { + private final boolean allowBreaching; +- private final Long2ObjectMap pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); ++ private final Long2ObjectMap pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); + + public SwimNodeEvaluator(boolean canJumpOutOfWater) { + this.allowBreaching = canJumpOutOfWater; +diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java +index 894881018c659d874f28f5744f0b8247cfecb1c1..ae06f7ef9c4b8147508984f8b46176de46171285 100644 +--- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java ++++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java +@@ -1,8 +1,10 @@ + package net.minecraft.world.level.pathfinder; + + import it.unimi.dsi.fastutil.longs.Long2ObjectMap; ++import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; + import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + import it.unimi.dsi.fastutil.objects.Object2BooleanMap; ++import it.unimi.dsi.fastutil.objects.Object2BooleanMaps; + import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; + import java.util.EnumSet; + import javax.annotation.Nullable; +@@ -33,8 +35,8 @@ public class WalkNodeEvaluator extends NodeEvaluator { + public static final double SPACE_BETWEEN_WALL_POSTS = 0.5D; + private static final double DEFAULT_MOB_JUMP_HEIGHT = 1.125D; + protected float oldWaterCost; +- private final Long2ObjectMap pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); +- private final Object2BooleanMap collisionCache = new Object2BooleanOpenHashMap<>(); ++ private final Long2ObjectMap pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); ++ private final Object2BooleanMap collisionCache = Object2BooleanMaps.synchronize(new Object2BooleanOpenHashMap<>()); + + @Override + public void prepare(PathNavigationRegion cachedWorld, Mob entity) { diff --git a/patches/server/0043-Hearse-MC-code-changes.patch b/patches/server/0043-Hearse-MC-code-changes.patch new file mode 100644 index 00000000..e30416c2 --- /dev/null +++ b/patches/server/0043-Hearse-MC-code-changes.patch @@ -0,0 +1,1047 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Sun, 8 Jan 2023 21:48:49 +0800 +Subject: [PATCH] Hearse: MC code changes + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java +index 24c677e80af652952263253409c050641e72e3b5..c6f9fb3efb92de0879eab6389fabd531bb4cfcb2 100644 +--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java ++++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java +@@ -1,7 +1,8 @@ + package net.minecraft.network.protocol.game; + +-import it.unimi.dsi.fastutil.shorts.ShortIterator; + import it.unimi.dsi.fastutil.shorts.ShortSet; ++import java.util.ArrayList; ++import java.util.List; + import java.util.function.BiConsumer; + import net.minecraft.core.BlockPos; + import net.minecraft.core.SectionPos; +@@ -22,14 +23,16 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet copy = new ArrayList<>(positions); ++ this.positions = new short[copy.size()]; ++ this.states = new BlockState[copy.size()]; ++ for (int i = 0; i < copy.size(); i++) { ++ this.positions[i] = copy.get(i); ++ } + +- this.positions = new short[i]; +- this.states = new BlockState[i]; +- int j = 0; + +- for (ShortIterator shortiterator = positions.iterator(); shortiterator.hasNext(); ++j) { +- short short0 = (Short) shortiterator.next(); ++ for (int j = 0;j < this.positions.length;j++) { ++ short short0 = this.positions[j]; + + this.positions[j] = short0; + this.states[j] = (section != null) ? section.getBlockState(SectionPos.sectionRelativeX(short0), SectionPos.sectionRelativeY(short0), SectionPos.sectionRelativeZ(short0)) : net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(); // CraftBukkit - SPIGOT-6076, Mojang bug when empty chunk section notified +diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java +index bc46479fd0622a90fd98ac88f92b2840a22a2d04..e642b4a83687d03e55feb340452d608c53ae7cce 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java ++++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +@@ -2,19 +2,9 @@ package net.minecraft.server.level; + + import com.mojang.datafixers.util.Either; + import com.mojang.datafixers.util.Pair; +-import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; ++import it.unimi.dsi.fastutil.shorts.ShortArraySet; + import it.unimi.dsi.fastutil.shorts.ShortSet; +-import java.util.ArrayList; +-import java.util.BitSet; +-import java.util.List; +-import java.util.Optional; +-import java.util.concurrent.CompletableFuture; +-import java.util.concurrent.Executor; +-import java.util.concurrent.atomic.AtomicReferenceArray; +-import java.util.function.IntConsumer; +-import java.util.function.IntSupplier; +-import javax.annotation.Nullable; +-import net.minecraft.Util; ++import it.unimi.dsi.fastutil.shorts.ShortSets; + import net.minecraft.core.BlockPos; + import net.minecraft.core.SectionPos; + import net.minecraft.network.protocol.Packet; +@@ -29,15 +19,14 @@ import net.minecraft.world.level.LevelHeightAccessor; + import net.minecraft.world.level.LightLayer; + import net.minecraft.world.level.block.entity.BlockEntity; + import net.minecraft.world.level.block.state.BlockState; +-import net.minecraft.world.level.chunk.ChunkAccess; +-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.LevelChunkSection; +-import net.minecraft.world.level.chunk.ProtoChunk; ++import net.minecraft.world.level.chunk.*; + import net.minecraft.world.level.lighting.LevelLightEngine; +-// CraftBukkit start +-import net.minecraft.server.MinecraftServer; ++import javax.annotation.Nullable; ++import java.util.BitSet; ++import java.util.List; ++import java.util.concurrent.CompletableFuture; ++import java.util.function.IntConsumer; ++import java.util.function.IntSupplier; + // CraftBukkit end + + public class ChunkHolder { +@@ -233,7 +222,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] = ShortSets.synchronize(new ShortArraySet()); + } + + this.changedBlocksPerSection[i].add(SectionPos.sectionRelativePos(pos)); +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index beb7c22cb63021f26c06f91050361e1b25fcc72d..10c7d776ca0d959541d3110c75ceb45a340278ac 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -1,57 +1,25 @@ + package net.minecraft.server.level; + +-import co.aikar.timings.Timing; // Paper + import com.google.common.collect.ImmutableList; + import com.google.common.collect.ImmutableList.Builder; + import com.google.common.collect.Iterables; +-import com.google.common.collect.ComparisonChain; // Paper + import com.google.common.collect.Lists; + import com.google.common.collect.Queues; +-import com.google.common.collect.Sets; + import com.google.gson.JsonElement; + import com.mojang.datafixers.DataFixer; + import com.mojang.datafixers.util.Either; + import com.mojang.logging.LogUtils; + import com.mojang.serialization.DataResult; + import com.mojang.serialization.JsonOps; ++import io.papermc.paper.util.MCUtil; + import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ++import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; + import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +-import it.unimi.dsi.fastutil.longs.Long2ByteMap; +-import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; +-import it.unimi.dsi.fastutil.longs.Long2LongMap; +-import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; +-import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +-import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; +-import it.unimi.dsi.fastutil.longs.LongIterator; +-import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +-import it.unimi.dsi.fastutil.longs.LongSet; +-import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator; ++import it.unimi.dsi.fastutil.longs.*; + import it.unimi.dsi.fastutil.objects.ObjectIterator; +-import java.io.IOException; +-import java.io.Writer; +-import java.nio.file.Path; +-import java.util.ArrayList; +-import java.util.BitSet; +-import java.util.Iterator; +-import java.util.List; +-import java.util.Locale; +-import java.util.Objects; +-import java.util.Optional; +-import java.util.Queue; +-import java.util.Set; +-import java.util.concurrent.CancellationException; +-import java.util.concurrent.CompletableFuture; +-import java.util.concurrent.CompletionException; +-import java.util.concurrent.CompletionStage; +-import java.util.concurrent.Executor; +-import java.util.concurrent.atomic.AtomicInteger; +-import java.util.function.BooleanSupplier; +-import java.util.function.Consumer; +-import java.util.function.IntFunction; +-import java.util.function.IntSupplier; +-import java.util.function.Supplier; +-import java.util.stream.Collectors; +-import javax.annotation.Nullable; ++import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; ++import it.unimi.dsi.fastutil.objects.ReferenceSet; ++import it.unimi.dsi.fastutil.objects.ReferenceSets; + import net.minecraft.CrashReport; + import net.minecraft.CrashReportCategory; + import net.minecraft.ReportedException; +@@ -63,36 +31,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 +52,24 @@ import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; + import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; + import net.minecraft.world.level.levelgen.RandomState; + import net.minecraft.world.level.levelgen.blending.BlendingData; +-import net.minecraft.world.level.levelgen.structure.StructureStart; + import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; + import net.minecraft.world.level.storage.DimensionDataStorage; + import net.minecraft.world.level.storage.LevelStorageSource; +-import net.minecraft.world.phys.Vec3; +-import org.apache.commons.lang3.mutable.MutableBoolean; + import org.apache.commons.lang3.mutable.MutableObject; +-import org.slf4j.Logger; + import org.bukkit.craftbukkit.generator.CustomChunkGenerator; +-import org.bukkit.entity.Player; +-// CraftBukkit end ++import org.slf4j.Logger; + +-import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper ++import javax.annotation.Nullable; ++import java.io.IOException; ++import java.io.Writer; ++import java.nio.file.Path; ++import java.util.*; ++import java.util.concurrent.CancellationException; ++import java.util.concurrent.CompletableFuture; ++import java.util.concurrent.CompletionException; ++import java.util.concurrent.Executor; ++import java.util.concurrent.atomic.AtomicInteger; ++import java.util.function.*; + + public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider { + +@@ -153,7 +109,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + private final Queue unloadQueue; + int viewDistance; + public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper +- public final ReferenceOpenHashSet needsChangeBroadcasting = new ReferenceOpenHashSet<>(); ++ public final ReferenceSet needsChangeBroadcasting = ReferenceSets.synchronize(new ReferenceOpenHashSet<>()); + + // Paper - rewrite chunk system + // Paper start - optimise checkDespawn +@@ -295,9 +251,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper - rewrite chunk system + this.tickingGenerated = new AtomicInteger(); + this.playerMap = new PlayerMap(); +- this.entityMap = new Int2ObjectOpenHashMap(); +- this.chunkTypeCache = new Long2ByteOpenHashMap(); +- this.chunkSaveCooldowns = new Long2LongOpenHashMap(); ++ this.entityMap = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap()); ++ this.chunkTypeCache = Long2ByteMaps.synchronize(new Long2ByteOpenHashMap()); ++ this.chunkSaveCooldowns = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); + this.unloadQueue = Queues.newConcurrentLinkedQueue(); + this.structureTemplateManager = structureTemplateManager; + Path path = session.getDimensionPath(world.dimension()); +diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java +index 52cba8f68d274cce106304aef1249a95474d3238..a9ba8adc5f290f6e2820632bdae8e50165595706 100644 +--- a/src/main/java/net/minecraft/server/level/DistanceManager.java ++++ b/src/main/java/net/minecraft/server/level/DistanceManager.java +@@ -1,42 +1,20 @@ + package net.minecraft.server.level; + +-import com.google.common.annotations.VisibleForTesting; +-import com.google.common.collect.ImmutableList; +-import com.google.common.collect.ImmutableSet; +-import com.google.common.collect.Sets; +-import com.mojang.datafixers.util.Either; + import com.mojang.logging.LogUtils; +-import it.unimi.dsi.fastutil.longs.Long2ByteMap; +-import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; +-import it.unimi.dsi.fastutil.longs.Long2IntMap; +-import it.unimi.dsi.fastutil.longs.Long2IntMaps; +-import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +-import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +-import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; +-import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +-import it.unimi.dsi.fastutil.longs.LongIterator; +-import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +-import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.longs.*; + import it.unimi.dsi.fastutil.objects.ObjectIterator; +-import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; + import it.unimi.dsi.fastutil.objects.ObjectSet; +-import java.io.File; +-import java.io.FileOutputStream; +-import java.io.IOException; +-import java.nio.charset.StandardCharsets; +-import java.util.Iterator; +-import java.util.Objects; +-import java.util.Set; +-import java.util.concurrent.CompletableFuture; +-import java.util.concurrent.Executor; +-import javax.annotation.Nullable; + import net.minecraft.core.SectionPos; + import net.minecraft.util.SortedArraySet; +-import net.minecraft.util.thread.ProcessorHandle; + import net.minecraft.world.level.ChunkPos; + import net.minecraft.world.level.chunk.ChunkStatus; +-import net.minecraft.world.level.chunk.LevelChunk; + import org.slf4j.Logger; ++import javax.annotation.Nullable; ++import java.io.File; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.nio.charset.StandardCharsets; ++import java.util.concurrent.Executor; + + public abstract class DistanceManager { + +@@ -52,7 +30,7 @@ public abstract class DistanceManager { + private static final int INITIAL_TICKET_LIST_CAPACITY = 4; + private static final int ENTITY_TICKING_LEVEL_THRESHOLD = 32; + private static final int BLOCK_TICKING_LEVEL_THRESHOLD = 33; +- final Long2ObjectMap> playersPerChunk = new Long2ObjectOpenHashMap(); ++ final Long2ObjectMap> playersPerChunk = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); + // Paper - rewrite chunk system + public static final int MOB_SPAWN_RANGE = 8; // private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - no longer used + //private final TickingTracker tickingTicketsTracker = new TickingTracker(); // Paper - no longer used +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 0ae45cf5a084fd412305e8b2f5dabe608b4eb1c1..a2935994edc279d880ff26dd5cc4e33f1105acc8 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -4,43 +4,19 @@ import com.google.common.annotations.VisibleForTesting; + import com.google.common.collect.Lists; + import com.mojang.datafixers.DataFixer; + import com.mojang.datafixers.util.Either; +-import java.io.File; +-import java.io.IOException; +-import java.util.Arrays; +-import java.util.Collections; +-import java.util.Iterator; +-import java.util.List; +-import java.util.Objects; +-import java.util.Optional; +-import java.util.concurrent.CompletableFuture; +-import java.util.concurrent.Executor; +-import java.util.function.BooleanSupplier; +-import java.util.function.Consumer; +-import java.util.function.Supplier; +-import javax.annotation.Nullable; ++import it.unimi.dsi.fastutil.objects.ObjectArraySet; ++import it.unimi.dsi.fastutil.objects.ObjectSet; + import net.minecraft.Util; + import net.minecraft.core.BlockPos; + import net.minecraft.core.SectionPos; + import net.minecraft.network.protocol.Packet; + import net.minecraft.server.level.progress.ChunkProgressListener; + import net.minecraft.util.VisibleForDebug; +-import net.minecraft.util.profiling.ProfilerFiller; + import net.minecraft.util.thread.BlockableEventLoop; + import net.minecraft.world.entity.Entity; + import net.minecraft.world.entity.ai.village.poi.PoiManager; +-import net.minecraft.world.level.BlockGetter; +-import net.minecraft.world.level.ChunkPos; +-import net.minecraft.world.level.GameRules; +-import net.minecraft.world.level.Level; +-import net.minecraft.world.level.LightLayer; +-import net.minecraft.world.level.LocalMobCapCalculator; +-import net.minecraft.world.level.NaturalSpawner; +-import net.minecraft.world.level.chunk.ChunkAccess; +-import net.minecraft.world.level.chunk.ChunkGenerator; +-import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +-import net.minecraft.world.level.chunk.ChunkSource; +-import net.minecraft.world.level.chunk.ChunkStatus; +-import net.minecraft.world.level.chunk.LevelChunk; ++import net.minecraft.world.level.*; ++import net.minecraft.world.level.chunk.*; + import net.minecraft.world.level.chunk.storage.ChunkScanAccess; + import net.minecraft.world.level.entity.ChunkStatusUpdateListener; + import net.minecraft.world.level.levelgen.RandomState; +@@ -48,7 +24,16 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp + import net.minecraft.world.level.storage.DimensionDataStorage; + import net.minecraft.world.level.storage.LevelData; + import net.minecraft.world.level.storage.LevelStorageSource; +-import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper ++ ++import javax.annotation.Nullable; ++import java.io.File; ++import java.io.IOException; ++import java.util.*; ++import java.util.concurrent.CompletableFuture; ++import java.util.concurrent.Executor; ++import java.util.function.BooleanSupplier; ++import java.util.function.Consumer; ++import java.util.function.Supplier; + + public class ServerChunkCache extends ChunkSource { + +@@ -803,7 +788,7 @@ public class ServerChunkCache extends ChunkSource { + //gameprofilerfiller.popPush("broadcast"); // Purpur + //this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur + if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { +- ReferenceOpenHashSet copy = this.chunkMap.needsChangeBroadcasting.clone(); ++ ObjectSet copy = new ObjectArraySet<>(this.chunkMap.needsChangeBroadcasting); + this.chunkMap.needsChangeBroadcasting.clear(); + for (ChunkHolder holder : copy) { + holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded +diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java +index 660693c6dc0ef86f4013df980b6d0c11c03e46cd..1ea9699ce1f77a551a45fc06dad55df4cc1a4f4d 100644 +--- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java ++++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java +@@ -1,14 +1,9 @@ + package net.minecraft.server.level; + +-import com.mojang.datafixers.util.Pair; ++import ca.spottedleaf.starlight.common.light.StarLightEngine; + import com.mojang.logging.LogUtils; +-import it.unimi.dsi.fastutil.objects.ObjectArrayList; +-import it.unimi.dsi.fastutil.objects.ObjectList; +-import it.unimi.dsi.fastutil.objects.ObjectListIterator; +-import java.util.concurrent.CompletableFuture; +-import java.util.concurrent.atomic.AtomicBoolean; +-import java.util.function.IntSupplier; +-import javax.annotation.Nullable; ++import io.papermc.paper.util.CoordinateUtils; ++import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; + import net.minecraft.Util; + import net.minecraft.core.BlockPos; + import net.minecraft.core.SectionPos; +@@ -17,21 +12,17 @@ import net.minecraft.util.thread.ProcessorMailbox; + import net.minecraft.world.level.ChunkPos; + import net.minecraft.world.level.LightLayer; + import net.minecraft.world.level.chunk.ChunkAccess; ++import net.minecraft.world.level.chunk.ChunkStatus; + import net.minecraft.world.level.chunk.DataLayer; +-import net.minecraft.world.level.chunk.LevelChunkSection; + import net.minecraft.world.level.chunk.LightChunkGetter; ++import net.minecraft.world.level.lighting.LayerLightEventListener; + import net.minecraft.world.level.lighting.LevelLightEngine; + import org.slf4j.Logger; + +-// Paper start +-import ca.spottedleaf.starlight.common.light.StarLightEngine; +-import io.papermc.paper.util.CoordinateUtils; ++import javax.annotation.Nullable; ++import java.util.concurrent.CompletableFuture; ++import java.util.function.IntSupplier; + import java.util.function.Supplier; +-import net.minecraft.world.level.lighting.LayerLightEventListener; +-import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +-import it.unimi.dsi.fastutil.longs.LongArrayList; +-import it.unimi.dsi.fastutil.longs.LongIterator; +-import net.minecraft.world.level.chunk.ChunkStatus; + // Paper end + + public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable { +@@ -145,20 +136,30 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + return; + } + +- final int references = this.chunksBeingWorkedOn.addTo(key, 1); ++ int references; ++ synchronized (this.chunksBeingWorkedOn){ ++ references = this.chunksBeingWorkedOn.addTo(key, 1); ++ } + if (references == 0) { + final ChunkPos pos = new ChunkPos(chunkX, chunkZ); + world.getChunkSource().addRegionTicket(ca.spottedleaf.starlight.common.light.StarLightInterface.CHUNK_WORK_TICKET, pos, 0, pos); + } + + updateFuture.thenAcceptAsync((final Void ignore) -> { +- final int newReferences = this.chunksBeingWorkedOn.get(key); ++ int newReferences; ++ synchronized (this.chunksBeingWorkedOn){ ++ newReferences = this.chunksBeingWorkedOn.get(key); ++ } + if (newReferences == 1) { +- this.chunksBeingWorkedOn.remove(key); ++ synchronized (this.chunksBeingWorkedOn){ ++ this.chunksBeingWorkedOn.remove(key); ++ } + final ChunkPos pos = new ChunkPos(chunkX, chunkZ); + world.getChunkSource().removeRegionTicket(ca.spottedleaf.starlight.common.light.StarLightInterface.CHUNK_WORK_TICKET, pos, 0, pos); + } else { +- this.chunksBeingWorkedOn.put(key, newReferences - 1); ++ synchronized (this.chunksBeingWorkedOn){ ++ this.chunksBeingWorkedOn.put(key, newReferences - 1); ++ } + } + }, world.getChunkSource().chunkMap.mainThreadExecutor).whenComplete((final Void ignore, final Throwable thr) -> { + if (thr != null) { +diff --git a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java +index 50a9f33aa31e9273c7c52d4bb2b02f0f884f7ba5..d5802cfe08f92b55ff1fd41648abda9ef2b7dd20 100644 +--- a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java ++++ b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java +@@ -10,12 +10,14 @@ import java.util.Collections; + import java.util.Iterator; + import java.util.List; + import java.util.Map; ++import java.util.concurrent.CopyOnWriteArrayList; ++import java.util.stream.Collector; + import java.util.stream.Collectors; + + public class ClassInstanceMultiMap extends AbstractCollection { +- private final Map, List> byClass = Maps.newHashMap(); ++ private final Map, List> byClass = Maps.newConcurrentMap(); + private final Class baseClass; +- private final List allInstances = Lists.newArrayList(); ++ private final List allInstances = Lists.newCopyOnWriteArrayList(); + + public ClassInstanceMultiMap(Class elementType) { + this.baseClass = elementType; +@@ -59,12 +61,16 @@ public class ClassInstanceMultiMap extends AbstractCollection { + throw new IllegalArgumentException("Don't know how to search for " + type); + } else { + List list = this.byClass.computeIfAbsent(type, (typeClass) -> { +- return this.allInstances.stream().filter(typeClass::isInstance).collect(Collectors.toList()); ++ return this.allInstances.stream().filter(typeClass::isInstance).collect(toList()); + }); +- return Collections.unmodifiableCollection(list); ++ return (Collection) Collections.unmodifiableCollection(list); + } + } + ++ public static Collector> toList() { ++ return Collectors.toCollection(CopyOnWriteArrayList::new); ++ } ++ + @Override + public Iterator iterator() { + return (Iterator)(this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator())); +diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java +index 83701fbfaa56a232593ee8f11a3afb8941238bfa..0c70810edace99bb5037d927388e055a514fcbde 100644 +--- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java ++++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java +@@ -8,6 +8,7 @@ import java.util.Queue; + import java.util.concurrent.CompletableFuture; + import java.util.concurrent.Executor; + import java.util.concurrent.locks.LockSupport; ++import java.util.concurrent.locks.StampedLock; + import java.util.function.BooleanSupplier; + import java.util.function.Supplier; + import net.minecraft.util.profiling.metrics.MetricCategory; +@@ -20,6 +21,7 @@ public abstract class BlockableEventLoop implements Profiler + private final String name; + private static final Logger LOGGER = LogUtils.getLogger(); + private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); ++ private final StampedLock lock = new StampedLock(); + private int blockingCount; + + protected BlockableEventLoop(String name) { +@@ -42,7 +44,12 @@ public abstract class BlockableEventLoop implements Profiler + } + + public int getPendingTasksCount() { +- return this.pendingRunnables.size(); ++ final long id = this.lock.readLock(); ++ try{ ++ return this.pendingRunnables.size(); ++ }finally { ++ this.lock.unlockRead(id); ++ } + } + + @Override +@@ -88,7 +95,12 @@ public abstract class BlockableEventLoop implements Profiler + + @Override + public void tell(R runnable) { +- this.pendingRunnables.add(runnable); ++ final long id = this.lock.writeLock(); ++ try { ++ this.pendingRunnables.add(runnable); ++ }finally { ++ this.lock.unlockWrite(id); ++ } + LockSupport.unpark(this.getRunningThread()); + } + +@@ -107,7 +119,12 @@ public abstract class BlockableEventLoop implements Profiler + } + + protected void dropAllTasks() { +- this.pendingRunnables.clear(); ++ final long id = this.lock.writeLock(); ++ try { ++ this.pendingRunnables.clear(); ++ }finally { ++ this.lock.unlockWrite(id); ++ } + } + + protected void runAllTasks() { +@@ -117,14 +134,19 @@ public abstract class BlockableEventLoop implements Profiler + } + + public boolean pollTask() { +- R runnable = this.pendingRunnables.peek(); +- if (runnable == null) { +- return false; +- } else if (this.blockingCount == 0 && !this.shouldRun(runnable)) { +- return false; +- } else { +- this.doRunTask(this.pendingRunnables.remove()); +- return true; ++ final long id = this.lock.writeLock(); ++ try { ++ R runnable = this.pendingRunnables.peek(); ++ if (runnable == null) { ++ return false; ++ } else if (this.blockingCount == 0 && !this.shouldRun(runnable)) { ++ return false; ++ } else { ++ this.doRunTask(this.pendingRunnables.remove()); ++ return true; ++ } ++ }finally { ++ this.lock.unlockWrite(id); + } + } + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 99912ae883227e1c0a0adc1edbb4c4baee4fe2bc..f33476a35706d7a236fe3bb178d166d568c07674 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -164,76 +164,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + } + + // Paper start +- public static RandomSource SHARED_RANDOM = new RandomRandomSource(); +- private static final class RandomRandomSource extends java.util.Random implements net.minecraft.world.level.levelgen.BitRandomSource { +- private boolean locked = false; +- +- @Override +- public synchronized void setSeed(long seed) { +- if (locked) { +- LOGGER.error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable()); +- } else { +- super.setSeed(seed); +- locked = true; +- } +- } +- +- @Override +- public RandomSource fork() { +- return new net.minecraft.world.level.levelgen.LegacyRandomSource(this.nextLong()); +- } +- +- @Override +- public net.minecraft.world.level.levelgen.PositionalRandomFactory forkPositional() { +- return new net.minecraft.world.level.levelgen.LegacyRandomSource.LegacyPositionalRandomFactory(this.nextLong()); +- } +- +- // these below are added to fix reobf issues that I don't wanna deal with right now +- @Override +- public int next(int bits) { +- return super.next(bits); +- } +- +- @Override +- public int nextInt(int origin, int bound) { +- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(origin, bound); +- } +- +- @Override +- public long nextLong() { +- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextLong(); +- } +- +- @Override +- public int nextInt() { +- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(); +- } +- +- @Override +- public int nextInt(int bound) { +- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(bound); +- } +- +- @Override +- public boolean nextBoolean() { +- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextBoolean(); +- } +- +- @Override +- public float nextFloat() { +- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextFloat(); +- } +- +- @Override +- public double nextDouble() { +- return net.minecraft.world.level.levelgen.BitRandomSource.super.nextDouble(); +- } +- +- @Override +- public double nextGaussian() { +- return super.nextGaussian(); +- } +- } ++ public static RandomSource SHARED_RANDOM = RandomSource.create(); + // Paper end + public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper + +diff --git a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java b/src/main/java/net/minecraft/world/level/entity/EntityLookup.java +index d45d832232be5017dde53816191c2b1830a0da32..f73f78e2f7c6e3eae66f7608a92854b3246e153d 100644 +--- a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java ++++ b/src/main/java/net/minecraft/world/level/entity/EntityLookup.java +@@ -8,13 +8,15 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; + import java.util.Map; + import java.util.UUID; + import javax.annotation.Nullable; ++ ++import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; + import net.minecraft.util.AbortableIterationConsumer; + import org.slf4j.Logger; + + public class EntityLookup { + private static final Logger LOGGER = LogUtils.getLogger(); +- private final Int2ObjectMap byId = new Int2ObjectLinkedOpenHashMap<>(); +- private final Map byUuid = Maps.newHashMap(); ++ private final Int2ObjectMap byId = Int2ObjectMaps.synchronize(new Int2ObjectLinkedOpenHashMap<>()); ++ private final Map byUuid = Maps.newConcurrentMap(); + + public void getEntities(EntityTypeTest filter, AbortableIterationConsumer consumer) { + for(T entityAccess : this.byId.values()) { +diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +index 57fcf3910f45ce371ac2e237b277b1034caaac4e..00f7b58db5948f4d7c7f07736d8fcf1972009c77 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,22 @@ + 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.Iterator; ++import java.util.Set; + import java.util.function.Consumer; +-import javax.annotation.Nullable; ++import it.unimi.dsi.fastutil.objects.ObjectArraySet; ++import it.unimi.dsi.fastutil.objects.ObjectSets; + import net.minecraft.world.entity.Entity; + + public class EntityTickList { +- public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? // Pufferfish - private->public +- +- private void ensureActiveIsNotIterated() { +- // Paper - replace with better logic, do not delay removals +- +- } ++ public final Set entities = ObjectSets.synchronize(new ObjectArraySet<>()); + + 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,16 +26,8 @@ public class EntityTickList { + + public void forEach(Consumer action) { + io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist iteration"); // Paper +- // Paper start - replace with better logic, do not delay removals/additions +- // To ensure nothing weird happens with dimension travelling, do not iterate over new entries... +- // (by dfl iterator() is configured to not iterate over new entries) +- io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator iterator = this.entities.iterator(); +- try { +- while (iterator.hasNext()) { +- action.accept(iterator.next()); +- } +- } finally { +- iterator.finishedIterating(); ++ for (Entity entity : this.entities) { ++ action.accept(entity); + } + // Paper end - replace with better logic, do not delay removals/additions + } +diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java +index a77985b2dd7137d8eea03909403fc08e89376d73..6bcbbbfc39432076a3d7714ecc2d05d9112d405c 100644 +--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java ++++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java +@@ -4,12 +4,8 @@ import com.google.common.collect.ImmutableList; + import com.google.common.collect.Queues; + import com.google.common.collect.Sets; + import com.mojang.logging.LogUtils; +-import it.unimi.dsi.fastutil.longs.Long2ObjectMap; ++import it.unimi.dsi.fastutil.longs.*; + import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; +-import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +-import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +-import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +-import it.unimi.dsi.fastutil.longs.LongSet; + import it.unimi.dsi.fastutil.objects.ObjectIterator; + import java.io.IOException; + import java.io.UncheckedIOException; +@@ -39,15 +35,15 @@ import org.bukkit.craftbukkit.event.CraftEventFactory; + public class PersistentEntitySectionManager implements AutoCloseable { + + static final Logger LOGGER = LogUtils.getLogger(); +- final Set knownUuids = Sets.newHashSet(); ++ final Set knownUuids = Sets.newConcurrentHashSet(); + final LevelCallback callbacks; + public final EntityPersistentStorage permanentStorage; + private final EntityLookup visibleEntityStorage = new EntityLookup<>(); + final EntitySectionStorage sectionStorage; + private final LevelEntityGetter entityGetter; +- private final Long2ObjectMap chunkVisibility = new Long2ObjectOpenHashMap(); +- private final Long2ObjectMap chunkLoadStatuses = new Long2ObjectOpenHashMap(); +- private final LongSet chunksToUnload = new LongOpenHashSet(); ++ private final Long2ObjectMap chunkVisibility = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); ++ private final Long2ObjectMap chunkLoadStatuses = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); ++ private final LongSet chunksToUnload = LongSets.synchronize(new LongOpenHashSet()); + private final Queue> loadingInbox = Queues.newConcurrentLinkedQueue(); + + public PersistentEntitySectionManager(Class entityClass, LevelCallback handler, EntityPersistentStorage dataAccess) { +diff --git a/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java b/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java +index daa03360dd7044f10b20f36023b305dc7e0bb7df..35de9e9a9d211b16a8b945bc512c128709ec6bfc 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java +@@ -19,17 +19,17 @@ public class LegacyRandomSource implements BitRandomSource { + } + + @Override +- public RandomSource fork() { ++ public synchronized RandomSource fork() { + return new LegacyRandomSource(this.nextLong()); + } + + @Override +- public PositionalRandomFactory forkPositional() { ++ public synchronized PositionalRandomFactory forkPositional() { + return new LegacyRandomSource.LegacyPositionalRandomFactory(this.nextLong()); + } + + @Override +- public void setSeed(long seed) { ++ public synchronized void setSeed(long seed) { + if (!this.seed.compareAndSet(this.seed.get(), (seed ^ 25214903917L) & 281474976710655L)) { + throw ThreadingDetector.makeThreadingException("LegacyRandomSource", (Thread)null); + } else { +@@ -38,7 +38,7 @@ public class LegacyRandomSource implements BitRandomSource { + } + + @Override +- public int next(int bits) { ++ public synchronized int next(int bits) { + long l = this.seed.get(); + long m = l * 25214903917L + 11L & 281474976710655L; + if (!this.seed.compareAndSet(l, m)) { +@@ -49,7 +49,7 @@ public class LegacyRandomSource implements BitRandomSource { + } + + @Override +- public double nextGaussian() { ++ public synchronized double nextGaussian() { + return this.gaussianSource.nextGaussian(); + } + +@@ -61,21 +61,21 @@ public class LegacyRandomSource implements BitRandomSource { + } + + @Override +- public RandomSource at(int x, int y, int z) { ++ public synchronized RandomSource at(int x, int y, int z) { + long l = Mth.getSeed(x, y, z); + long m = l ^ this.seed; + return new LegacyRandomSource(m); + } + + @Override +- public RandomSource fromHashOf(String seed) { ++ public synchronized RandomSource fromHashOf(String seed) { + int i = seed.hashCode(); + return new LegacyRandomSource((long)i ^ this.seed); + } + + @VisibleForTesting + @Override +- public void parityConfigString(StringBuilder info) { ++ public synchronized void parityConfigString(StringBuilder info) { + info.append("LegacyPositionalRandomFactory{").append(this.seed).append("}"); + } + } +diff --git a/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java b/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java +index 3d5ce92c77bc107e2ec2f54dc849b99c3abf9718..88d78f77740ee436fedd5159f8bafe91c1eb5ec1 100644 +--- a/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java ++++ b/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java +@@ -1,17 +1,16 @@ + package net.minecraft.world.level.lighting; + +-import it.unimi.dsi.fastutil.longs.Long2ByteMap; +-import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; +-import it.unimi.dsi.fastutil.longs.LongArrayList; +-import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; +-import it.unimi.dsi.fastutil.longs.LongList; ++import it.unimi.dsi.fastutil.longs.*; ++ ++import java.util.Deque; ++import java.util.concurrent.ConcurrentLinkedDeque; + import java.util.function.LongPredicate; + import net.minecraft.util.Mth; + + public abstract class DynamicGraphMinFixedPoint { + private static final int NO_COMPUTED_LEVEL = 255; + private final int levelCount; +- private final LongLinkedOpenHashSet[] queues; ++ private final Deque[] queues; + private final Long2ByteMap computedLevels; + private int firstQueuedLevel; + private volatile boolean hasWork; +@@ -21,17 +20,10 @@ public abstract class DynamicGraphMinFixedPoint { + throw new IllegalArgumentException("Level count must be < 254."); + } else { + this.levelCount = levelCount; +- this.queues = new LongLinkedOpenHashSet[levelCount]; ++ this.queues = new Deque[levelCount]; + + for(int i = 0; i < levelCount; ++i) { +- this.queues[i] = new LongLinkedOpenHashSet(expectedLevelSize, 0.5F) { +- protected void rehash(int i) { +- if (i > expectedLevelSize) { +- super.rehash(i); +- } +- +- } +- }; ++ this.queues[i] = new ConcurrentLinkedDeque(); + } + + this.computedLevels = new Long2ByteOpenHashMap(expectedTotalSize, 0.5F) { +@@ -191,8 +183,8 @@ public abstract class DynamicGraphMinFixedPoint { + } else { + while(this.firstQueuedLevel < this.levelCount && maxSteps > 0) { + --maxSteps; +- LongLinkedOpenHashSet longLinkedOpenHashSet = this.queues[this.firstQueuedLevel]; +- long l = longLinkedOpenHashSet.removeFirstLong(); ++ Deque longLinkedOpenHashSet = this.queues[this.firstQueuedLevel]; ++ long l = longLinkedOpenHashSet.removeFirst(); + int i = Mth.clamp(this.getLevel(l), 0, this.levelCount - 1); + if (longLinkedOpenHashSet.isEmpty()) { + this.checkFirstQueuedLevel(this.levelCount); +diff --git a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java +index b1c594dc6a6b8a6c737b99272acab9e7dbd0ed63..a097e00bd62f53630568f68854d3a34300012277 100644 +--- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java ++++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java +@@ -4,6 +4,8 @@ import com.mojang.logging.LogUtils; + import java.util.ArrayDeque; + import java.util.ArrayList; + import java.util.List; ++import java.util.concurrent.CopyOnWriteArrayList; ++import java.util.concurrent.locks.StampedLock; + import javax.annotation.Nullable; + import net.minecraft.core.BlockPos; + import net.minecraft.core.Direction; +@@ -17,9 +19,11 @@ public class CollectingNeighborUpdater implements NeighborUpdater { + private final Level level; + private final int maxChainedNeighborUpdates; + private final ArrayDeque stack = new ArrayDeque<>(); +- private final List addedThisLayer = new ArrayList<>(); ++ private final List addedThisLayer = new CopyOnWriteArrayList<>(); + private int count = 0; + ++ private final StampedLock lock = new StampedLock(); ++ + public CollectingNeighborUpdater(Level world, int maxChainDepth) { + this.level = world; + this.maxChainedNeighborUpdates = maxChainDepth; +@@ -49,14 +53,19 @@ public class CollectingNeighborUpdater implements NeighborUpdater { + boolean bl = this.count > 0; + boolean bl2 = this.maxChainedNeighborUpdates >= 0 && this.count >= this.maxChainedNeighborUpdates; + ++this.count; +- if (!bl2) { +- if (bl) { +- this.addedThisLayer.add(entry); +- } else { +- this.stack.push(entry); ++ final long lockId = this.lock.writeLock(); ++ try { ++ if (!bl2) { ++ if (bl) { ++ this.addedThisLayer.add(entry); ++ } else { ++ this.stack.push(entry); ++ } ++ } else if (this.count - 1 == this.maxChainedNeighborUpdates) { ++ LOGGER.error("Too many chained neighbor updates. Skipping the rest. First skipped position: " + pos.toShortString()); + } +- } else if (this.count - 1 == this.maxChainedNeighborUpdates) { +- LOGGER.error("Too many chained neighbor updates. Skipping the rest. First skipped position: " + pos.toShortString()); ++ }finally { ++ this.lock.unlockWrite(lockId); + } + + if (!bl) { +@@ -66,28 +75,31 @@ public class CollectingNeighborUpdater implements NeighborUpdater { + } + + private void runUpdates() { ++ final long lockid = this.lock.writeLock(); + try { +- while(!this.stack.isEmpty() || !this.addedThisLayer.isEmpty()) { +- for(int i = this.addedThisLayer.size() - 1; i >= 0; --i) { +- this.stack.push(this.addedThisLayer.get(i)); +- } +- +- this.addedThisLayer.clear(); +- CollectingNeighborUpdater.NeighborUpdates neighborUpdates = this.stack.peek(); +- +- while(this.addedThisLayer.isEmpty()) { +- if (!neighborUpdates.runNext(this.level)) { +- this.stack.pop(); +- break; ++ try { ++ while(!this.stack.isEmpty() || !this.addedThisLayer.isEmpty()) { ++ for(int i = this.addedThisLayer.size() - 1; i >= 0; --i) { ++ this.stack.push(this.addedThisLayer.get(i)); ++ } ++ this.addedThisLayer.clear(); ++ CollectingNeighborUpdater.NeighborUpdates neighborUpdates = this.stack.peek(); ++ ++ while(this.addedThisLayer.isEmpty()) { ++ if (!neighborUpdates.runNext(this.level)) { ++ this.stack.pop(); ++ break; ++ } + } + } ++ } finally { ++ this.stack.clear(); ++ this.addedThisLayer.clear(); ++ this.count = 0; + } +- } finally { +- this.stack.clear(); +- this.addedThisLayer.clear(); +- this.count = 0; ++ }finally { ++ this.lock.unlockWrite(lockid); + } +- + } + + static record FullNeighborUpdate(BlockState state, BlockPos pos, Block block, BlockPos neighborPos, boolean movedByPiston) implements CollectingNeighborUpdater.NeighborUpdates { diff --git a/patches/server/0044-Hearse-MC-code-changes-2.patch b/patches/server/0044-Hearse-MC-code-changes-2.patch new file mode 100644 index 00000000..8b08166a --- /dev/null +++ b/patches/server/0044-Hearse-MC-code-changes-2.patch @@ -0,0 +1,386 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Sun, 8 Jan 2023 21:59:47 +0800 +Subject: [PATCH] Hearse: MC code changes 2 + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 03b8ef3409fd5f7a4d4b06e13cf8eb22b3bbf8a1..3d99330160a3ba0715394d0c88533bc4a79fe3a2 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -3,10 +3,12 @@ package net.minecraft.server.level; + import com.google.common.annotations.VisibleForTesting; + import co.aikar.timings.TimingHistory; // Paper + import com.google.common.collect.Lists; ++import com.google.common.collect.Sets; + import com.mojang.datafixers.DataFixer; + import com.mojang.datafixers.util.Pair; + import com.mojang.logging.LogUtils; + import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ++import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; + import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + import it.unimi.dsi.fastutil.longs.LongSet; + import it.unimi.dsi.fastutil.longs.LongSets; +@@ -21,15 +23,8 @@ import java.io.IOException; + import java.io.Writer; + import java.nio.file.Files; + import java.nio.file.Path; +-import java.util.ArrayList; +-import java.util.Comparator; +-import java.util.Iterator; +-import java.util.List; +-import java.util.Locale; +-import java.util.Objects; +-import java.util.Optional; +-import java.util.Set; +-import java.util.UUID; ++import java.util.*; ++import java.util.concurrent.ConcurrentLinkedDeque; + import java.util.concurrent.Executor; + import java.util.function.BooleanSupplier; + import java.util.function.Function; +@@ -204,7 +199,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + final Set navigatingMobs; + volatile boolean isUpdatingNavigations; + protected final Raids raids; +- private final ObjectLinkedOpenHashSet blockEvents; ++ private final Deque blockEvents; + private final List blockEventsToReschedule; + private boolean handlingTick; + private final List customSpawners; +@@ -536,10 +531,10 @@ public class ServerLevel extends Level implements WorldGenLevel { + this.entityTickList = new EntityTickList(); + this.blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier()); + this.fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier()); +- this.navigatingMobs = new ObjectOpenHashSet(); +- this.blockEvents = new ObjectLinkedOpenHashSet(); +- this.blockEventsToReschedule = new ArrayList(64); +- this.dragonParts = new Int2ObjectOpenHashMap(); ++ this.navigatingMobs = Sets.newConcurrentHashSet(); ++ this.blockEvents = new ConcurrentLinkedDeque<>(); ++ this.blockEventsToReschedule = Collections.synchronizedList(new ArrayList(64)); ++ this.dragonParts = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap()); + this.tickTime = flag1; + this.server = minecraftserver; + this.customSpawners = list; +@@ -1777,10 +1772,8 @@ public class ServerLevel extends Level implements WorldGenLevel { + + private void runBlockEvents() { + this.blockEventsToReschedule.clear(); +- +- while (!this.blockEvents.isEmpty()) { +- BlockEventData blockactiondata = (BlockEventData) this.blockEvents.removeFirst(); +- ++ BlockEventData blockactiondata; ++ while ((blockactiondata = (BlockEventData) this.blockEvents.pollFirst())!=null) { + if (this.shouldTickBlocksAt(blockactiondata.pos())) { + if (this.doBlockEvent(blockactiondata)) { + this.server.getPlayerList().broadcast((Player) null, (double) blockactiondata.pos().getX(), (double) blockactiondata.pos().getY(), (double) blockactiondata.pos().getZ(), 64.0D, this.dimension(), new ClientboundBlockEventPacket(blockactiondata.pos(), blockactiondata.block(), blockactiondata.paramA(), blockactiondata.paramB())); +diff --git a/src/main/java/net/minecraft/util/ThreadingDetector.java b/src/main/java/net/minecraft/util/ThreadingDetector.java +index b6e98aaebe57453b8eceaa633a989aa24409830f..60162cccf765800c6172d1544f2cd9bcf30cbd97 100644 +--- a/src/main/java/net/minecraft/util/ThreadingDetector.java ++++ b/src/main/java/net/minecraft/util/ThreadingDetector.java +@@ -17,7 +17,7 @@ import org.slf4j.Logger; + public class ThreadingDetector { + private static final Logger LOGGER = LogUtils.getLogger(); + private final String name; +- private final Semaphore lock = new Semaphore(1); ++ private final Semaphore lock = new Semaphore(255); + private final Lock stackTraceLock = new ReentrantLock(); + @Nullable + private volatile Thread threadThatFailedToAcquire; +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java +index d3827215ef19f6e1e63f846d91ed00525a318c7a..80215972538d5cfce5f224253ea0e34ea4fd45a4 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java +@@ -40,7 +40,7 @@ public class LongJumpToRandomPos extends Behavior { + protected final int maxLongJumpHeight; + protected final int maxLongJumpWidth; + protected final float maxJumpVelocity; +- protected List jumpCandidates = Lists.newArrayList(); ++ protected List jumpCandidates = Lists.newCopyOnWriteArrayList(); + protected Optional initialPosition = Optional.empty(); + @Nullable + protected Vec3 chosenJump; +diff --git a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java +index 878a42695ecedf0c3f2e6310e3ce44c6b6c36858..0c308e12f9b590fa169babac487c8adc7e3f823c 100644 +--- a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java ++++ b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java +@@ -2,18 +2,17 @@ package net.minecraft.world.level.gameevent; + + import com.google.common.collect.Lists; + import com.google.common.collect.Sets; +-import java.util.Iterator; +-import java.util.List; +-import java.util.Optional; +-import java.util.Set; ++ ++import java.util.*; ++ + import net.minecraft.network.protocol.game.DebugPackets; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.phys.Vec3; + + public class EuclideanGameEventListenerRegistry implements GameEventListenerRegistry { +- private final List listeners = Lists.newArrayList(); +- private final Set listenersToRemove = Sets.newHashSet(); +- private final List listenersToAdd = Lists.newArrayList(); ++ private final List listeners = Collections.synchronizedList(Lists.newArrayList()); ++ private final Set listenersToRemove = Sets.newConcurrentHashSet(); ++ private final List listenersToAdd = Lists.newCopyOnWriteArrayList(); + private boolean processing; + private final ServerLevel level; + +diff --git a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java +index ac807277a6b26d140ea9873d17c7aa4fb5fe37b2..4c75f50ab0184637b72e08936ff8808ad6c6fb5f 100644 +--- a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java ++++ b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java +@@ -13,6 +13,8 @@ import java.util.function.Function; + import java.util.function.Predicate; + import java.util.stream.Stream; + import javax.annotation.Nullable; ++ ++import it.unimi.dsi.fastutil.objects.ObjectSets; + import net.minecraft.core.BlockPos; + import net.minecraft.nbt.ListTag; + import net.minecraft.world.level.ChunkPos; +@@ -21,7 +23,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + private final Queue> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER); + @Nullable + private List> pendingTicks; +- private final Set> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); ++ private final Set> ticksPerPosition = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); + @Nullable + private BiConsumer, ScheduledTick> onTickAdded; + +@@ -29,11 +31,11 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + private boolean dirty; + private long lastSaved = Long.MIN_VALUE; + +- public boolean isDirty(final long tick) { ++ public synchronized boolean isDirty(final long tick) { + return this.dirty || (!this.tickQueue.isEmpty() && tick != this.lastSaved); + } + +- public void clearDirty() { ++ public synchronized void clearDirty() { + this.dirty = false; + } + // Paper end - add dirty flag +@@ -50,17 +52,17 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + + } + +- public void setOnTickAdded(@Nullable BiConsumer, ScheduledTick> tickConsumer) { ++ public synchronized void setOnTickAdded(@Nullable BiConsumer, ScheduledTick> tickConsumer) { + this.onTickAdded = tickConsumer; + } + + @Nullable +- public ScheduledTick peek() { ++ public synchronized ScheduledTick peek() { + return this.tickQueue.peek(); + } + + @Nullable +- public ScheduledTick poll() { ++ public synchronized ScheduledTick poll() { + ScheduledTick scheduledTick = this.tickQueue.poll(); + if (scheduledTick != null) { + this.dirty = true; // Paper - add dirty flag +@@ -71,7 +73,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + } + + @Override +- public void schedule(ScheduledTick orderedTick) { ++ public synchronized void schedule(ScheduledTick orderedTick) { + if (this.ticksPerPosition.add(orderedTick)) { + this.dirty = true; // Paper - add dirty flag + this.scheduleUnchecked(orderedTick); +@@ -88,11 +90,11 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + } + + @Override +- public boolean hasScheduledTick(BlockPos pos, T type) { ++ public synchronized boolean hasScheduledTick(BlockPos pos, T type) { + return this.ticksPerPosition.contains(ScheduledTick.probe(type, pos)); + } + +- public void removeIf(Predicate> predicate) { ++ public synchronized void removeIf(Predicate> predicate) { + Iterator> iterator = this.tickQueue.iterator(); + + while(iterator.hasNext()) { +@@ -105,17 +107,17 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + + } + +- public Stream> getAll() { ++ public synchronized Stream> getAll() { + return this.tickQueue.stream(); + } + + @Override +- public int count() { ++ public synchronized int count() { + return this.tickQueue.size() + (this.pendingTicks != null ? this.pendingTicks.size() : 0); + } + + @Override +- public ListTag save(long l, Function function) { ++ public synchronized ListTag save(long l, Function function) { + this.lastSaved = l; // Paper - add dirty system to level ticks + ListTag listTag = new ListTag(); + if (this.pendingTicks != null) { +@@ -131,7 +133,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + return listTag; + } + +- public void unpack(long time) { ++ public synchronized void unpack(long time) { + if (this.pendingTicks != null) { + // Paper start - add dirty system to level chunk ticks + if (this.tickQueue.isEmpty()) { +diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java +index 5dea8414964e0d2d1fb15a6baa27227e9722bfc7..2203adc2a68e7fb253e353098fd6ddad521e3a32 100644 +--- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java ++++ b/src/main/java/net/minecraft/world/ticks/LevelTicks.java +@@ -1,24 +1,9 @@ + package net.minecraft.world.ticks; + +-import it.unimi.dsi.fastutil.longs.Long2LongMap; +-import it.unimi.dsi.fastutil.longs.Long2LongMaps; +-import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; +-import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +-import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.longs.*; + import it.unimi.dsi.fastutil.objects.ObjectIterator; + import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; +-import java.util.ArrayDeque; +-import java.util.ArrayList; +-import java.util.Comparator; +-import java.util.List; +-import java.util.LongSummaryStatistics; +-import java.util.PriorityQueue; +-import java.util.Queue; +-import java.util.Set; +-import java.util.function.BiConsumer; +-import java.util.function.LongPredicate; +-import java.util.function.Predicate; +-import java.util.function.Supplier; ++import it.unimi.dsi.fastutil.objects.ObjectSets; + import net.minecraft.Util; + import net.minecraft.core.BlockPos; + import net.minecraft.core.SectionPos; +@@ -26,31 +11,36 @@ import net.minecraft.core.Vec3i; + import net.minecraft.util.profiling.ProfilerFiller; + import net.minecraft.world.level.ChunkPos; + import net.minecraft.world.level.levelgen.structure.BoundingBox; ++import java.util.*; ++import java.util.concurrent.ConcurrentLinkedDeque; ++import java.util.concurrent.CopyOnWriteArrayList; ++import java.util.function.BiConsumer; ++import java.util.function.LongPredicate; ++import java.util.function.Predicate; ++import java.util.function.Supplier; + + public class LevelTicks implements LevelTickAccess { + private static final Comparator> CONTAINER_DRAIN_ORDER = (a, b) -> { + return ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek()); + }; + private final LongPredicate tickCheck; +- private final Supplier profiler; +- private final Long2ObjectMap> allContainers = new Long2ObjectOpenHashMap<>(); ++ private final Long2ObjectMap> allContainers = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); + private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), (map) -> { + map.defaultReturnValue(Long.MAX_VALUE); + }); + private final Queue> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER); +- private final Queue> toRunThisTick = new ArrayDeque<>(); +- private final List> alreadyRunThisTick = new ArrayList<>(); +- private final Set> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); ++ private final Queue> toRunThisTick = new ConcurrentLinkedDeque<>(); ++ private final List> alreadyRunThisTick = new CopyOnWriteArrayList<>(); ++ private final Set> toRunThisTickSet = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); ++ + private final BiConsumer, ScheduledTick> chunkScheduleUpdater = (chunkTickScheduler, tick) -> { + if (tick.equals(chunkTickScheduler.peek())) { + this.updateContainerScheduling(tick); + } +- + }; + + public LevelTicks(LongPredicate tickingFutureReadyPredicate, Supplier profilerGetter) { + this.tickCheck = tickingFutureReadyPredicate; +- this.profiler = profilerGetter; + } + + public void addContainer(ChunkPos pos, LevelChunkTicks scheduler) { +@@ -123,7 +113,9 @@ public class LevelTicks implements LevelTickAccess { + entry.setValue(scheduledTick.triggerTick()); + } else if (this.tickCheck.test(l)) { + objectIterator.remove(); +- this.containersToTick.add(levelChunkTicks); ++ synchronized (this.containersToTick){ ++ this.containersToTick.add(levelChunkTicks); ++ } + } + } + } +@@ -133,27 +125,29 @@ public class LevelTicks implements LevelTickAccess { + + private void drainContainers(long time, int maxTicks) { + LevelChunkTicks levelChunkTicks; +- while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { +- ScheduledTick scheduledTick = levelChunkTicks.poll(); +- this.scheduleForThisTick(scheduledTick); +- this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks); +- ScheduledTick scheduledTick2 = levelChunkTicks.peek(); +- if (scheduledTick2 != null) { +- if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) { +- this.containersToTick.add(levelChunkTicks); +- } else { +- this.updateContainerScheduling(scheduledTick2); ++ synchronized (this.containersToTick){ ++ while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { ++ ScheduledTick scheduledTick = levelChunkTicks.poll(); ++ this.scheduleForThisTick(scheduledTick); ++ this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks); ++ ScheduledTick scheduledTick2 = levelChunkTicks.peek(); ++ if (scheduledTick2 != null) { ++ if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) { ++ this.containersToTick.add(levelChunkTicks); ++ } else { ++ this.updateContainerScheduling(scheduledTick2); ++ } + } + } + } +- + } + + private void rescheduleLeftoverContainers() { +- for(LevelChunkTicks levelChunkTicks : this.containersToTick) { +- this.updateContainerScheduling(levelChunkTicks.peek()); ++ synchronized (this.containersToTick){ ++ for(LevelChunkTicks levelChunkTicks : this.containersToTick) { ++ this.updateContainerScheduling(levelChunkTicks.peek()); ++ } + } +- + } + + private void updateContainerScheduling(ScheduledTick tick) { +@@ -201,7 +195,9 @@ public class LevelTicks implements LevelTickAccess { + + private void cleanupAfterTick() { + this.toRunThisTick.clear(); +- this.containersToTick.clear(); ++ synchronized (this.containersToTick){ ++ this.containersToTick.clear(); ++ } + this.alreadyRunThisTick.clear(); + this.toRunThisTickSet.clear(); + } diff --git a/patches/server/0045-Hearse-Add-base-codes.patch b/patches/server/0045-Hearse-Add-base-codes.patch new file mode 100644 index 00000000..6125d36b --- /dev/null +++ b/patches/server/0045-Hearse-Add-base-codes.patch @@ -0,0 +1,394 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Sun, 8 Jan 2023 22:12:06 +0800 +Subject: [PATCH] Hearse: Add base codes + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/HearseConfig.java b/src/main/java/co/earthme/hearse/HearseConfig.java +new file mode 100644 +index 0000000000000000000000000000000000000000..912da4787f83f656da67e9533b60183c17e6c345 +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/HearseConfig.java +@@ -0,0 +1,4 @@ ++package co.earthme.hearse; ++ ++public class HearseConfig { ++} +diff --git a/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java b/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3fbc81cb880cf6d38bb4c940b4cc1fa828c2ef17 +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java +@@ -0,0 +1,76 @@ ++package co.earthme.hearse.concurrent; ++ ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.Queue; ++import java.util.concurrent.*; ++import java.util.concurrent.locks.LockSupport; ++ ++public class ThreadPool extends ThreadPoolExecutor { ++ public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue) { ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); ++ } ++ ++ public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull ThreadFactory threadFactory) { ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); ++ } ++ ++ public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull RejectedExecutionHandler handler) { ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); ++ } ++ ++ public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull ThreadFactory threadFactory, @NotNull RejectedExecutionHandler handler) { ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); ++ } ++ ++ private final Queue taskEntries = new ConcurrentLinkedQueue<>(); ++ ++ public void executeWithSubTask(Runnable mainTask,Runnable subTask){ ++ final TaskEntry wrapped = new TaskEntry(subTask,mainTask); ++ this.taskEntries.offer(wrapped); ++ this.execute(wrapped); ++ } ++ ++ public void runAllSubTasks(){ ++ TaskEntry task; ++ while ((task = this.taskEntries.poll())!=null){ ++ while (!task.allRunned()){ ++ LockSupport.parkNanos(this,10000000); ++ } ++ } ++ } ++ ++ private static class TaskEntry implements Runnable{ ++ private final Runnable mainTask; ++ private final Runnable subTask; ++ private volatile boolean mainTaskFinished = false; ++ ++ public TaskEntry(Runnable subTask,Runnable mainTask){ ++ this.subTask = subTask; ++ this.mainTask = mainTask; ++ } ++ ++ public boolean allRunned(){ ++ if (!this.mainTaskFinished){ ++ return false; ++ } ++ try { ++ this.subTask.run(); ++ }catch (Exception e){ ++ e.printStackTrace(); ++ } ++ return true; ++ } ++ ++ @Override ++ public void run() { ++ try { ++ this.mainTask.run(); ++ }catch(Exception e){ ++ e.printStackTrace(); ++ }finally { ++ this.mainTaskFinished = true; ++ } ++ } ++ } ++} +diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +new file mode 100644 +index 0000000000000000000000000000000000000000..06f55f26eb63e356b3558622bf68711f18cda1c6 +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +@@ -0,0 +1,13 @@ ++package co.earthme.hearse.concurrent; ++ ++import io.papermc.paper.util.TickThread; ++ ++public class WorkerThread extends TickThread { ++ public WorkerThread(String name) { ++ super(name); ++ } ++ ++ public static boolean isWorker(){ ++ return Thread.currentThread() instanceof WorkerThread; ++ } ++} +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 3d99330160a3ba0715394d0c88533bc4a79fe3a2..30cde5b9cd8d179f4b332563f2999f675851fbd1 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -1,64 +1,33 @@ + package net.minecraft.server.level; + ++import co.aikar.timings.TimingHistory; + import com.google.common.annotations.VisibleForTesting; +-import co.aikar.timings.TimingHistory; // Paper + import com.google.common.collect.Lists; + import com.google.common.collect.Sets; + import com.mojang.datafixers.DataFixer; + import com.mojang.datafixers.util.Pair; + import com.mojang.logging.LogUtils; ++import io.papermc.paper.util.MCUtil; + import it.unimi.dsi.fastutil.ints.Int2ObjectMap; + import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; + import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.ints.IntArrayList; + import it.unimi.dsi.fastutil.longs.LongSet; + import it.unimi.dsi.fastutil.longs.LongSets; + import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry; + import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; + import it.unimi.dsi.fastutil.objects.ObjectArrayList; + import it.unimi.dsi.fastutil.objects.ObjectIterator; +-import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +-import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +-import java.io.BufferedWriter; +-import java.io.IOException; +-import java.io.Writer; +-import java.nio.file.Files; +-import java.nio.file.Path; +-import java.util.*; +-import java.util.concurrent.ConcurrentLinkedDeque; +-import java.util.concurrent.Executor; +-import java.util.function.BooleanSupplier; +-import java.util.function.Function; +-import java.util.function.Predicate; +-import java.util.stream.Collectors; +-import java.util.stream.Stream; +-import javax.annotation.Nonnull; +-import javax.annotation.Nullable; + import net.minecraft.CrashReport; + import net.minecraft.Util; +-import net.minecraft.core.BlockPos; +-import net.minecraft.core.Direction; +-import net.minecraft.core.Holder; +-import net.minecraft.core.HolderSet; +-import net.minecraft.core.RegistryAccess; +-import net.minecraft.core.SectionPos; ++import net.minecraft.core.*; + import net.minecraft.core.particles.ParticleOptions; + import net.minecraft.core.registries.BuiltInRegistries; + import net.minecraft.core.registries.Registries; + import net.minecraft.network.chat.Component; +-import net.minecraft.network.chat.MutableComponent; + import net.minecraft.network.protocol.Packet; +-import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; +-import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; +-import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; +-import net.minecraft.network.protocol.game.ClientboundExplodePacket; +-import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; +-import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; +-import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket; +-import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; +-import net.minecraft.network.protocol.game.ClientboundSoundPacket; +-import net.minecraft.network.protocol.game.DebugPackets; ++import net.minecraft.network.protocol.game.*; + import net.minecraft.resources.ResourceKey; +-import io.papermc.paper.util.MCUtil; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.ServerScoreboard; + import net.minecraft.server.level.progress.ChunkProgressListener; +@@ -66,21 +35,10 @@ import net.minecraft.server.players.SleepStatus; + import net.minecraft.sounds.SoundEvent; + import net.minecraft.sounds.SoundSource; + import net.minecraft.tags.TagKey; +-import net.minecraft.util.AbortableIterationConsumer; +-import net.minecraft.util.CsvOutput; +-import net.minecraft.util.Mth; +-import net.minecraft.util.ProgressListener; +-import net.minecraft.util.Unit; +-import net.minecraft.util.profiling.ProfilerFiller; ++import net.minecraft.util.*; + import net.minecraft.world.DifficultyInstance; + import net.minecraft.world.damagesource.DamageSource; +-import net.minecraft.world.entity.Entity; +-import net.minecraft.world.entity.EntityType; +-import net.minecraft.world.entity.LightningBolt; +-import net.minecraft.world.entity.LivingEntity; +-import net.minecraft.world.entity.Mob; +-import net.minecraft.world.entity.MobCategory; +-import net.minecraft.world.entity.ReputationEventHandler; ++import net.minecraft.world.entity.*; + import net.minecraft.world.entity.ai.navigation.PathNavigation; + import net.minecraft.world.entity.ai.village.ReputationEventType; + import net.minecraft.world.entity.ai.village.poi.PoiManager; +@@ -97,17 +55,7 @@ import net.minecraft.world.entity.raid.Raid; + import net.minecraft.world.entity.raid.Raids; + import net.minecraft.world.flag.FeatureFlagSet; + import net.minecraft.world.item.crafting.RecipeManager; +-import net.minecraft.world.level.BlockEventData; +-import net.minecraft.world.level.ChunkPos; +-import net.minecraft.world.level.CustomSpawner; +-import net.minecraft.world.level.Explosion; +-import net.minecraft.world.level.ExplosionDamageCalculator; +-import net.minecraft.world.level.ForcedChunksSavedData; +-import net.minecraft.world.level.GameRules; +-import net.minecraft.world.level.Level; +-import net.minecraft.world.level.NaturalSpawner; +-import net.minecraft.world.level.StructureManager; +-import net.minecraft.world.level.WorldGenLevel; ++import net.minecraft.world.level.*; + import net.minecraft.world.level.biome.Biome; + import net.minecraft.world.level.biome.BiomeSource; + import net.minecraft.world.level.block.Block; +@@ -123,12 +71,10 @@ import net.minecraft.world.level.chunk.storage.EntityStorage; + import net.minecraft.world.level.dimension.BuiltinDimensionTypes; + import net.minecraft.world.level.dimension.LevelStem; + import net.minecraft.world.level.dimension.end.EndDragonFight; +-import net.minecraft.world.level.entity.EntityPersistentStorage; + import net.minecraft.world.level.entity.EntityTickList; + import net.minecraft.world.level.entity.EntityTypeTest; + import net.minecraft.world.level.entity.LevelCallback; + import net.minecraft.world.level.entity.LevelEntityGetter; +-import net.minecraft.world.level.entity.PersistentEntitySectionManager; + import net.minecraft.world.level.gameevent.DynamicGameEventListener; + import net.minecraft.world.level.gameevent.GameEvent; + import net.minecraft.world.level.gameevent.GameEventDispatcher; +@@ -153,21 +99,32 @@ import net.minecraft.world.phys.shapes.BooleanOp; + import net.minecraft.world.phys.shapes.Shapes; + import net.minecraft.world.phys.shapes.VoxelShape; + import net.minecraft.world.ticks.LevelTicks; +-import org.slf4j.Logger; + import org.bukkit.Bukkit; +-import org.bukkit.Location; + import org.bukkit.WeatherType; + import org.bukkit.craftbukkit.event.CraftEventFactory; + import org.bukkit.craftbukkit.generator.CustomWorldChunkManager; +-import org.bukkit.craftbukkit.util.CraftNamespacedKey; + import org.bukkit.craftbukkit.util.WorldUUID; + import org.bukkit.event.entity.CreatureSpawnEvent; + import org.bukkit.event.server.MapInitializeEvent; + import org.bukkit.event.weather.LightningStrikeEvent; +-import org.bukkit.event.world.GenericGameEvent; + import org.bukkit.event.world.TimeSkipEvent; +-// CraftBukkit end +-import it.unimi.dsi.fastutil.ints.IntArrayList; // Paper ++import org.slf4j.Logger; ++ ++import javax.annotation.Nonnull; ++import javax.annotation.Nullable; ++import java.io.BufferedWriter; ++import java.io.IOException; ++import java.io.Writer; ++import java.nio.file.Files; ++import java.nio.file.Path; ++import java.util.*; ++import java.util.concurrent.ConcurrentLinkedDeque; ++import java.util.concurrent.Executor; ++import java.util.function.BooleanSupplier; ++import java.util.function.Function; ++import java.util.function.Predicate; ++import java.util.stream.Collectors; ++import java.util.stream.Stream; + + public class ServerLevel extends Level implements WorldGenLevel { + +@@ -988,7 +945,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + if (this.canSleepThroughNights()) { + if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) { + int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); +- MutableComponent ichatmutablecomponent; ++ Component ichatmutablecomponent; + + if (this.sleepStatus.areEnoughSleeping(i)) { + ichatmutablecomponent = Component.translatable("sleep.skipping_night"); +@@ -1656,56 +1613,56 @@ public class ServerLevel extends Level implements WorldGenLevel { + + @Override + public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { +- if (this.isUpdatingNavigations) { +- String s = "recursive call to sendBlockUpdated"; ++ synchronized (this.navigatingMobs){ ++ if (this.isUpdatingNavigations) { ++ return; ++ } + +- Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated")); +- } ++ this.getChunkSource().blockChanged(pos); ++ if(this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates ++ VoxelShape voxelshape = oldState.getCollisionShape(this, pos); ++ VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); + +- this.getChunkSource().blockChanged(pos); +- if(this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates +- VoxelShape voxelshape = oldState.getCollisionShape(this, pos); +- VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); ++ if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) { ++ List list = new ObjectArrayList(); ++ Iterator iterator = this.navigatingMobs.iterator(); + +- if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) { +- List list = new ObjectArrayList(); +- Iterator iterator = this.navigatingMobs.iterator(); ++ while (iterator.hasNext()) { ++ // CraftBukkit start - fix SPIGOT-6362 ++ Mob entityinsentient; ++ try { ++ entityinsentient = (Mob) iterator.next(); ++ } catch (java.util.ConcurrentModificationException ex) { ++ // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register ++ // In this case we just run the update again across all the iterators as the chunk will then be loaded ++ // As this is a relative edge case it is much faster than copying navigators (on either read or write) ++ this.sendBlockUpdated(pos, oldState, newState, flags); ++ return; ++ } ++ // CraftBukkit end ++ PathNavigation navigationabstract = entityinsentient.getNavigation(); + +- while (iterator.hasNext()) { +- // CraftBukkit start - fix SPIGOT-6362 +- Mob entityinsentient; +- try { +- entityinsentient = (Mob) iterator.next(); +- } catch (java.util.ConcurrentModificationException ex) { +- // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register +- // In this case we just run the update again across all the iterators as the chunk will then be loaded +- // As this is a relative edge case it is much faster than copying navigators (on either read or write) +- this.sendBlockUpdated(pos, oldState, newState, flags); +- return; +- } +- // CraftBukkit end +- PathNavigation navigationabstract = entityinsentient.getNavigation(); ++ if (navigationabstract.shouldRecomputePath(pos)) { ++ list.add(navigationabstract); ++ } ++ } + +- if (navigationabstract.shouldRecomputePath(pos)) { +- list.add(navigationabstract); +- } +- } ++ try { ++ this.isUpdatingNavigations = true; ++ iterator = list.iterator(); + +- try { +- this.isUpdatingNavigations = true; +- iterator = list.iterator(); ++ while (iterator.hasNext()) { ++ PathNavigation navigationabstract1 = (PathNavigation) iterator.next(); + +- while (iterator.hasNext()) { +- PathNavigation navigationabstract1 = (PathNavigation) iterator.next(); ++ navigationabstract1.recomputePath(); ++ } ++ } finally { ++ this.isUpdatingNavigations = false; ++ } + +- navigationabstract1.recomputePath(); + } +- } finally { +- this.isUpdatingNavigations = false; +- } +- ++ } // Paper + } +- } // Paper + } + + @Override diff --git a/patches/server/0046-Hearse-Update-codes.patch b/patches/server/0046-Hearse-Update-codes.patch new file mode 100644 index 00000000..22891c42 --- /dev/null +++ b/patches/server/0046-Hearse-Update-codes.patch @@ -0,0 +1,315 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 09:26:38 +0800 +Subject: [PATCH] Hearse: Update codes + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +index 06f55f26eb63e356b3558622bf68711f18cda1c6..2a43625d13d7aa253c15aba8092ac9361785a5f0 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java ++++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +@@ -3,8 +3,11 @@ package co.earthme.hearse.concurrent; + import io.papermc.paper.util.TickThread; + + public class WorkerThread extends TickThread { ++ + public WorkerThread(String name) { + super(name); ++ this.setDaemon(true); ++ this.setPriority(Thread.NORM_PRIORITY - 2); + } + + public static boolean isWorker(){ +diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e65b1eba68003a9f7ce5080d07a521817831ff48 +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java +@@ -0,0 +1,5 @@ ++package co.earthme.hearse.concurrent; ++ ++public interface WorkerThreadFactory { ++ WorkerThread getNewThread(Runnable task); ++} +diff --git a/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java +similarity index 62% +rename from src/main/java/co/earthme/hearse/concurrent/ThreadPool.java +rename to src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java +index 3fbc81cb880cf6d38bb4c940b4cc1fa828c2ef17..f7ca6d650d9089b65137d61acca64c89e5b4db22 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/ThreadPool.java ++++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java +@@ -6,25 +6,17 @@ import java.util.Queue; + import java.util.concurrent.*; + import java.util.concurrent.locks.LockSupport; + +-public class ThreadPool extends ThreadPoolExecutor { +- public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue) { +- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); +- } +- +- public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull ThreadFactory threadFactory) { +- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); +- } ++public class WorkerThreadPoolExecutor extends ThreadPoolExecutor { ++ private final Queue taskEntries = new ConcurrentLinkedQueue<>(); + +- public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull RejectedExecutionHandler handler) { +- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); ++ public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull WorkerThreadFactory workerThreadFactory) { ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,r->workerThreadFactory.getNewThread(r)); + } + +- public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull ThreadFactory threadFactory, @NotNull RejectedExecutionHandler handler) { +- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); ++ public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull WorkerThreadFactory workerThreadFactory, @NotNull RejectedExecutionHandler handler) { ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,r->workerThreadFactory.getNewThread(r), handler); + } + +- private final Queue taskEntries = new ConcurrentLinkedQueue<>(); +- + public void executeWithSubTask(Runnable mainTask,Runnable subTask){ + final TaskEntry wrapped = new TaskEntry(subTask,mainTask); + this.taskEntries.offer(wrapped); +diff --git a/src/main/java/co/earthme/hearse/server/ServerHook.java b/src/main/java/co/earthme/hearse/server/ServerHook.java +new file mode 100644 +index 0000000000000000000000000000000000000000..22260735664d986fed6bf82e4016b647417e1932 +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/server/ServerHook.java +@@ -0,0 +1,66 @@ ++package co.earthme.hearse.server; ++ ++import co.earthme.hearse.concurrent.WorkerThread; ++import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.world.entity.Entity; ++import java.util.concurrent.LinkedBlockingQueue; ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.atomic.AtomicInteger; ++ ++public class ServerHook { ++ private static volatile boolean firstTick = false; ++ private static final AtomicInteger threadId = new AtomicInteger(); ++ private static final WorkerThreadPoolExecutor worker = new WorkerThreadPoolExecutor( ++ Runtime.getRuntime().availableProcessors(), ++ Runtime.getRuntime().availableProcessors(), ++ 100, ++ TimeUnit.MILLISECONDS, ++ new LinkedBlockingQueue<>(), ++ task -> { ++ WorkerThread workerThread = new WorkerThread("Hearse-Worker-Thread # "+threadId.getAndIncrement()); ++ return workerThread; ++ } ++ ); ++ ++ public static void executeAsyncTask(Runnable task){ ++ worker.execute(task); ++ } ++ ++ public static void executeAsyncTaskWithMainThreadCallback(Runnable task,Runnable callBack){ ++ worker.executeWithSubTask(task,callBack); ++ } ++ ++ public static void callPostTick(){ ++ if (!firstTick){ ++ firstTick = true; ++ return; ++ } ++ worker.runAllSubTasks(); ++ } ++ ++ public static void callAsyncEntityTick(Entity entity, ServerLevel level){ ++ MinecraftServer.getServer().executeMidTickTasks(); ++ worker.execute(()->{ ++ entity.activatedPriorityReset = false; ++ if (!entity.isRemoved()) { ++ entity.checkDespawn(); ++ Entity entity1 = entity.getVehicle(); ++ if (entity1 != null) { ++ if (!entity1.isRemoved() && entity1.hasPassenger(entity)) { ++ return; ++ } ++ entity.stopRiding(); ++ } ++ try { ++ level.tickNonPassenger(entity); ++ } catch (Throwable throwable) { ++ if (throwable instanceof ThreadDeath) throw throwable; ++ level.getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(throwable.getMessage(), throwable))); ++ throwable.printStackTrace(); ++ } ++ } ++ }); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 3b7e4b724e86518ea57f5ed5ef0b8b3741d10f6f..e0e169a4403926ff6be004de1bf5ec2079acb440 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1,5 +1,6 @@ + package net.minecraft.server; + ++import co.earthme.hearse.server.ServerHook; + import com.google.common.base.Splitter; + import com.google.common.collect.ImmutableList; + import co.aikar.timings.Timings; +@@ -1407,6 +1408,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 5000000000L) { + this.lastServerStatus = i; + this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount())); +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index a2935994edc279d880ff26dd5cc4e33f1105acc8..81697ea6d00967852556c3bb741317db030c24db 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -833,12 +833,10 @@ public class ServerChunkCache extends ChunkSource { + if (chunkMap.playerMobDistanceMap != null && _pufferfish_spawnCountsReady.getAndSet(false)) { + net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> { + int mapped = distanceManager.getNaturalSpawnChunkCount(); +- io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator objectiterator = +- level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); ++ Iterator objectiterator = level.entityTickList.entities.iterator(); + gg.pufferfish.pufferfish.util.IterableWrapper wrappedIterator = + new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator); + lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true); +- objectiterator.finishedIterating(); + _pufferfish_spawnCountsReady.set(true); + }); + } +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 6074956d677542a46066dc45ceda9d73a2e09d27..125e5fb54acb68d58ea9fb973894b02facb04edc 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -1,6 +1,8 @@ + package net.minecraft.server.level; + + import co.aikar.timings.TimingHistory; ++import co.earthme.hearse.concurrent.WorkerThread; ++import co.earthme.hearse.server.ServerHook; + import com.google.common.annotations.VisibleForTesting; + import com.google.common.collect.Lists; + import com.google.common.collect.Sets; +@@ -221,7 +223,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + + public final void loadChunksForMoveAsync(AABB axisalignedbb, ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority priority, + java.util.function.Consumer> onLoad) { +- if (Thread.currentThread() != this.thread) { ++ if (Thread.currentThread() != this.thread && !WorkerThread.isWorker()) { + this.getChunkSource().mainThreadProcessor.execute(() -> { + this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad); + }); +@@ -643,71 +645,15 @@ public class ServerLevel extends Level implements WorldGenLevel { + //timings.doSounds.stopTiming(); // Spigot // Purpur + this.handlingTick = false; + //gameprofilerfiller.pop(); // Purpur +- boolean flag = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players +- +- if (flag) { +- this.resetEmptyTime(); +- } +- +- if (flag || this.emptyTime++ < 300) { +- //gameprofilerfiller.push("entities"); // Purpur +- //timings.tickEntities.startTiming(); // Spigot // Purpur +- if (this.dragonFight != null) { +- //gameprofilerfiller.push("dragonFight"); // Purpur +- this.dragonFight.tick(); +- //gameprofilerfiller.pop(); // Purpur +- } +- +- org.spigotmc.ActivationRange.activateEntities(this); // Spigot +- //timings.entityTick.startTiming(); // Spigot // Purpur +- this.entityTickList.forEach((entity) -> { +- entity.activatedPriorityReset = false; // Pufferfish - DAB +- if (!entity.isRemoved()) { +- if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed +- entity.discard(); +- } else { +- //gameprofilerfiller.push("checkDespawn"); // Purpur +- entity.checkDespawn(); +- //gameprofilerfiller.pop(); // Purpur +- if (true || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { // Paper - now always true if in the ticking list +- Entity entity1 = entity.getVehicle(); +- +- if (entity1 != null) { +- if (!entity1.isRemoved() && entity1.hasPassenger(entity)) { +- return; +- } +- +- entity.stopRiding(); +- } +- +- //gameprofilerfiller.push("tick"); // Purpur +- // Pufferfish start - copied from this.guardEntityTick +- try { +- this.tickNonPassenger(entity); // Pufferfish - changed +- MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick +- } catch (Throwable throwable) { +- if (throwable instanceof ThreadDeath) throw throwable; // Paper +- // Paper start - Prevent tile entity and entity crashes +- final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); +- MinecraftServer.LOGGER.error(msg, throwable); +- getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); +- entity.discard(); +- // Paper end +- } +- // Pufferfish end +- //gameprofilerfiller.pop(); // Purpur +- } +- } +- } +- }); +- //timings.entityTick.stopTiming(); // Spigot // Purpur +- //timings.tickEntities.stopTiming(); // Spigot // Purpur +- //gameprofilerfiller.pop(); // Purpur +- this.tickBlockEntities(); ++ this.resetEmptyTime(); ++ if (this.dragonFight != null) { ++ this.dragonFight.tick(); + } +- +- //gameprofilerfiller.push("entityManagement"); // Purpur +- //this.entityManager.tick(); // Paper - rewrite chunk system ++ org.spigotmc.ActivationRange.activateEntities(this); // Spigot ++ this.entityTickList.forEach((entity) -> { ++ ServerHook.callAsyncEntityTick(entity,this); ++ }); ++ this.tickBlockEntities(); + } + + @Override +@@ -746,7 +692,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + + } + +- private boolean shouldDiscardEntity(Entity entity) { ++ public boolean shouldDiscardEntity(Entity entity) { + return !this.server.isSpawningAnimals() && (entity instanceof Animal || entity instanceof WaterAnimal) ? true : !this.server.areNpcsEnabled() && entity instanceof Npc; + } + +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 958e0ee29915bddde2cb8ebfd578448b83e2b149..de78c5bdde53b4adfed9fda4d473560849bdb5aa 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -2,6 +2,7 @@ package net.minecraft.world.level; + + import co.aikar.timings.Timing; + import co.aikar.timings.Timings; ++import co.earthme.hearse.concurrent.WorkerThread; + import com.destroystokyo.paper.event.server.ServerExceptionEvent; + import com.destroystokyo.paper.exception.ServerInternalException; + import com.google.common.base.MoreObjects; +@@ -1116,7 +1117,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + } + // Paper end + // CraftBukkit end +- return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system ++ return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() && !WorkerThread.isWorker() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system + } + + public void setBlockEntity(BlockEntity blockEntity) { diff --git a/patches/server/0047-Hearse-Complete-half-of-the-code-and-fix-some-proble.patch b/patches/server/0047-Hearse-Complete-half-of-the-code-and-fix-some-proble.patch new file mode 100644 index 00000000..4a8e5d1c --- /dev/null +++ b/patches/server/0047-Hearse-Complete-half-of-the-code-and-fix-some-proble.patch @@ -0,0 +1,1764 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 12:35:11 +0800 +Subject: [PATCH] Hearse: Complete half of the code and fix some problems + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +index 2a43625d13d7aa253c15aba8092ac9361785a5f0..52f0c9dddf29a28cc360fbacb923445e5c3f82a6 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java ++++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +@@ -4,12 +4,19 @@ import io.papermc.paper.util.TickThread; + + public class WorkerThread extends TickThread { + ++ + public WorkerThread(String name) { + super(name); + this.setDaemon(true); + this.setPriority(Thread.NORM_PRIORITY - 2); + } + ++ public WorkerThread(Runnable run, String name) { ++ super(run, name); ++ this.setDaemon(true); ++ this.setPriority(Thread.NORM_PRIORITY - 2); ++ } ++ + public static boolean isWorker(){ + return Thread.currentThread() instanceof WorkerThread; + } +diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java +index f7ca6d650d9089b65137d61acca64c89e5b4db22..8899c02a2242b51097a03c7e3ca03b8768c60117 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java ++++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java +@@ -10,11 +10,11 @@ public class WorkerThreadPoolExecutor extends ThreadPoolExecutor { + private final Queue taskEntries = new ConcurrentLinkedQueue<>(); + + public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull WorkerThreadFactory workerThreadFactory) { +- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,r->workerThreadFactory.getNewThread(r)); ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, workerThreadFactory::getNewThread); + } + + public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue workQueue, @NotNull WorkerThreadFactory workerThreadFactory, @NotNull RejectedExecutionHandler handler) { +- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,r->workerThreadFactory.getNewThread(r), handler); ++ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, workerThreadFactory::getNewThread, handler); + } + + public void executeWithSubTask(Runnable mainTask,Runnable subTask){ +diff --git a/src/main/java/co/earthme/hearse/server/ServerHook.java b/src/main/java/co/earthme/hearse/server/ServerHook.java +index 22260735664d986fed6bf82e4016b647417e1932..524a55c3298a079e416c742641af55725a602a2b 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerHook.java +@@ -5,6 +5,8 @@ import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.entity.Entity; ++import org.apache.logging.log4j.LogManager; ++ + import java.util.concurrent.LinkedBlockingQueue; + import java.util.concurrent.TimeUnit; + import java.util.concurrent.atomic.AtomicInteger; +@@ -19,7 +21,7 @@ public class ServerHook { + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + task -> { +- WorkerThread workerThread = new WorkerThread("Hearse-Worker-Thread # "+threadId.getAndIncrement()); ++ WorkerThread workerThread = new WorkerThread(task,"Hearse-Worker-Thread # "+threadId.getAndIncrement()); + return workerThread; + } + ); +diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +index f72d99bea1c78c8882793fa8ee3dc3642be1b8c7..62a74cbdb7f04b652dddac9e9c6191d5b86c3323 100644 +--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java ++++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +@@ -37,11 +37,11 @@ public final class PlayerChunkLoader { + public static final int LOADED_TICKET_LEVEL = 33; + + public static int getTickViewDistance(final Player player) { +- return getTickViewDistance(((CraftPlayer)player).getHandle()); ++ return getTickViewDistance(((CraftPlayer) player).getHandle()); + } + + public static int getTickViewDistance(final ServerPlayer player) { +- final ServerLevel level = (ServerLevel)player.level; ++ final ServerLevel level = (ServerLevel) player.level; + final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player); + if (data == null) { + return level.chunkSource.chunkMap.playerChunkManager.getTargetTickViewDistance(); +@@ -50,11 +50,11 @@ public final class PlayerChunkLoader { + } + + public static int getLoadViewDistance(final Player player) { +- return getLoadViewDistance(((CraftPlayer)player).getHandle()); ++ return getLoadViewDistance(((CraftPlayer) player).getHandle()); + } + + public static int getLoadViewDistance(final ServerPlayer player) { +- final ServerLevel level = (ServerLevel)player.level; ++ final ServerLevel level = (ServerLevel) player.level; + final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player); + if (data == null) { + return level.chunkSource.chunkMap.playerChunkManager.getLoadDistance(); +@@ -63,11 +63,11 @@ public final class PlayerChunkLoader { + } + + public static int getSendViewDistance(final Player player) { +- return getSendViewDistance(((CraftPlayer)player).getHandle()); ++ return getSendViewDistance(((CraftPlayer) player).getHandle()); + } + + public static int getSendViewDistance(final ServerPlayer player) { +- final ServerLevel level = (ServerLevel)player.level; ++ final ServerLevel level = (ServerLevel) player.level; + final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player); + if (data == null) { + return level.chunkSource.chunkMap.playerChunkManager.getTargetSendDistance(); +@@ -75,9 +75,6 @@ public final class PlayerChunkLoader { + return data.getTargetSendViewDistance(); + } + +- private final StampedLock sendLock = new StampedLock(); +- private final StampedLock loadLock = new StampedLock(); +- + protected final ChunkMap chunkMap; + protected final Reference2ObjectLinkedOpenHashMap playerMap = new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f); + protected final ReferenceLinkedOpenHashSet chunkSendQueue = new ReferenceLinkedOpenHashSet<>(512, 0.7f); +@@ -376,7 +373,7 @@ public final class PlayerChunkLoader { + } + + return !(data.hasSentChunk(chunkX - 1, chunkZ) && data.hasSentChunk(chunkX + 1, chunkZ) && +- data.hasSentChunk(chunkX, chunkZ - 1) && data.hasSentChunk(chunkX, chunkZ + 1)); ++ data.hasSentChunk(chunkX, chunkZ - 1) && data.hasSentChunk(chunkX, chunkZ + 1)); + } + + protected int getMaxConcurrentChunkSends() { +@@ -386,15 +383,15 @@ public final class PlayerChunkLoader { + protected int getMaxChunkLoads() { + double config = GlobalConfiguration.get().chunkLoading.playerMaxConcurrentLoads; + double max = GlobalConfiguration.get().chunkLoading.globalMaxConcurrentLoads; +- return (int)Math.ceil(Math.min(config * MinecraftServer.getServer().getPlayerCount(), max <= 1.0 ? Double.MAX_VALUE : max)); ++ return (int) Math.ceil(Math.min(config * MinecraftServer.getServer().getPlayerCount(), max <= 1.0 ? Double.MAX_VALUE : max)); + } + + protected long getTargetSendPerPlayerAddend() { +- return GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate); ++ return GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate <= 1.0 ? 0L : (long) Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate); + } + + protected long getMaxSendAddend() { +- return GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate); ++ return GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate <= 1.0 ? 0L : (long) Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate); + } + + public void onChunkPlayerTickReady(final int chunkX, final int chunkZ) { +@@ -416,7 +413,7 @@ public final class PlayerChunkLoader { + if (!(raw instanceof ServerPlayer)) { + continue; + } +- this.onChunkSendReady((ServerPlayer)raw, chunkX, chunkZ); ++ this.onChunkSendReady((ServerPlayer) raw, chunkX, chunkZ); + } + } + +@@ -485,19 +482,9 @@ public final class PlayerChunkLoader { + } + loaderData.remove(); + +- long id1 = this.loadLock.writeLock(); +- try { +- this.chunkLoadQueue.remove(loaderData); +- }finally { +- this.loadLock.unlockWrite(id1); +- } ++ this.chunkLoadQueue.remove(loaderData); + +- final long id = this.sendLock.writeLock(); +- try { +- this.chunkSendQueue.remove(loaderData); +- }finally { +- this.sendLock.unlockWrite(id); +- } ++ this.chunkSendQueue.remove(loaderData); + + this.chunkSendWaitQueue.remove(loaderData); + synchronized (this.sendingChunkCounts) { +@@ -534,49 +521,34 @@ public final class PlayerChunkLoader { + protected static final AtomicInteger concurrentChunkSends = new AtomicInteger(); + protected final Reference2IntOpenHashMap sendingChunkCounts = new Reference2IntOpenHashMap<>(); + private static long nextChunkSend; ++ + private void trySendChunks() { + final long time = System.nanoTime(); + if (time < nextChunkSend) { + return; + } +- final long id = this.sendLock.writeLock(); +- try { +- // drain entries from wait queue +- while (!this.chunkSendWaitQueue.isEmpty()) { +- final PlayerLoaderData data = this.chunkSendWaitQueue.first(); ++ // drain entries from wait queue ++ while (!this.chunkSendWaitQueue.isEmpty()) { ++ final PlayerLoaderData data = this.chunkSendWaitQueue.first(); + +- if (data.nextChunkSendTarget > time) { +- break; +- } ++ if (data.nextChunkSendTarget > time) { ++ break; ++ } + +- this.chunkSendWaitQueue.pollFirst(); ++ this.chunkSendWaitQueue.pollFirst(); + +- this.chunkSendQueue.add(data); +- } +- }finally { +- this.sendLock.unlockWrite(id); ++ this.chunkSendQueue.add(data); + } + +- long id2 = this.sendLock.readLock(); +- try { +- if (this.chunkSendQueue.isEmpty()) { +- return; +- } +- }finally { +- this.sendLock.unlockRead(id2); ++ if (this.chunkSendQueue.isEmpty()) { ++ return; + } + + final int maxSends = this.getMaxConcurrentChunkSends(); + final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time; +- for (;;) { +- +- long id3 = this.sendLock.readLock(); +- try { +- if (this.chunkSendQueue.isEmpty()) { +- break; +- } +- }finally { +- this.sendLock.unlockRead(id3); ++ for (; ; ) { ++ if (this.chunkSendQueue.isEmpty()) { ++ break; + } + + final int currSends = concurrentChunkSends.get(); +@@ -590,29 +562,17 @@ public final class PlayerChunkLoader { + + // send chunk + +- PlayerLoaderData data; +- +- final long id4 = this.sendLock.writeLock(); +- try { +- data = this.chunkSendQueue.removeFirst(); +- }finally { +- this.sendLock.unlockWrite(id4); +- } ++ PlayerLoaderData data = this.chunkSendQueue.removeFirst(); + + 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 +- final long id5 = this.sendLock.readLock(); +- try { +- if (this.chunkSendQueue.isEmpty()) { +- // nothing left +- break; +- } +- continue; +- }finally { +- this.sendLock.unlockRead(id5); ++ if (this.chunkSendQueue.isEmpty()) { ++ // nothing left ++ break; + } ++ continue; + } + + if (!this.isChunkPlayerLoaded(queuedSend.chunkX, queuedSend.chunkZ)) { +@@ -620,7 +580,6 @@ public final class PlayerChunkLoader { + } + + data.nextChunkSendTarget = nextPlayerDeadline; +- this.sendLock.writeLock(); + + synchronized (this.sendingChunkCounts) { + this.sendingChunkCounts.addTo(data, 1); +@@ -652,43 +611,27 @@ public final class PlayerChunkLoader { + + protected int concurrentChunkLoads; + // this interval prevents bursting a lot of chunk loads +- protected static final IntervalledCounter TICKET_ADDITION_COUNTER_SHORT = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms ++ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_SHORT = new IntervalledCounter((long) (1.0e6 * 50.0)); // 50ms + // this interval ensures the rate is kept between ticks correctly +- protected static final IntervalledCounter TICKET_ADDITION_COUNTER_LONG = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms ++ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_LONG = new IntervalledCounter((long) (1.0e6 * 1000.0)); // 1000ms ++ + private void tryLoadChunks() { +- final long id = this.loadLock.writeLock(); +- try { +- if (this.chunkLoadQueue.isEmpty()) { +- return; +- } +- }finally { +- this.loadLock.unlockWrite(id); ++ if (this.chunkLoadQueue.isEmpty()) { ++ return; + } + + final int maxLoads = this.getMaxChunkLoads(); + final long time = System.nanoTime(); + boolean updatedCounters = false; +- for (;;) { +- PlayerLoaderData data; +- +- final long id1 = this.loadLock.writeLock(); +- try { +- data = this.chunkLoadQueue.pollFirst(); +- }finally { +- this.loadLock.unlock(id1); +- } ++ for (; ; ) { ++ PlayerLoaderData data = this.chunkLoadQueue.pollFirst(); + + data.lastChunkLoad = time; + + final ChunkPriorityHolder queuedLoad = data.loadQueue.peekFirst(); + if (queuedLoad == null) { +- long id2 = this.loadLock.writeLock(); +- try { +- if (this.chunkLoadQueue.isEmpty()) { +- break; +- } +- }finally { +- this.loadLock.unlockWrite(id2); ++ if (this.chunkLoadQueue.isEmpty()) { ++ break; + } + continue; + } +@@ -704,12 +647,7 @@ public final class PlayerChunkLoader { + if (this.isChunkPlayerLoaded(queuedLoad.chunkX, queuedLoad.chunkZ)) { + // already loaded! + data.loadQueue.pollFirst(); // already loaded so we just skip +- final long id3 = this.loadLock.writeLock(); +- try { +- this.chunkLoadQueue.add(data); +- }finally { +- this.loadLock.unlockWrite(id3); +- } ++ this.chunkLoadQueue.add(data); + // ensure the chunk is queued to send + this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ); + continue; +@@ -739,14 +677,9 @@ public final class PlayerChunkLoader { + + final int currentChunkLoads = this.concurrentChunkLoads; + if (currentChunkLoads >= maxLoads || (GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate > 0 && (TICKET_ADDITION_COUNTER_SHORT.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate || TICKET_ADDITION_COUNTER_LONG.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate)) +- || (GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate > 0.0 && (data.ticketAdditionCounterShort.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate || data.ticketAdditionCounterLong.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate))) { ++ || (GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate > 0.0 && (data.ticketAdditionCounterShort.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate || data.ticketAdditionCounterLong.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate))) { + // don't poll, we didn't load it +- final long id4 = this.loadLock.writeLock(); +- try { +- this.chunkLoadQueue.add(data); +- }finally { +- this.loadLock.unlockWrite(id4); +- } ++ this.chunkLoadQueue.add(data); + break; + } + } +@@ -755,12 +688,7 @@ public final class PlayerChunkLoader { + data.loadQueue.pollFirst(); + + // now that we've polled we can re-add to load queue +- final long id4 = this.loadLock.writeLock(); +- try { +- this.chunkLoadQueue.add(data); +- }finally { +- this.loadLock.unlockWrite(id4); +- } ++ this.chunkLoadQueue.add(data); + + // add necessary tickets to load chunk up to send-ready + for (int dz = -1; dz <= 1; ++dz) { +@@ -840,7 +768,7 @@ public final class PlayerChunkLoader { + protected static final double PRIORITISED_DISTANCE = 12.0 * 16.0; + + // Player max sprint speed is approximately 8m/s +- protected static final double LOOK_PRIORITY_SPEED_THRESHOLD = (10.0/20.0) * (10.0/20.0); ++ protected static final double LOOK_PRIORITY_SPEED_THRESHOLD = (10.0 / 20.0) * (10.0 / 20.0); + protected static final double LOOK_PRIORITY_YAW_DELTA_RECALC_THRESHOLD = 3.0f; + + protected double lastLocX = Double.NEGATIVE_INFINITY; +@@ -887,9 +815,9 @@ public final class PlayerChunkLoader { + protected long nextChunkSendTarget; + + // this interval prevents bursting a lot of chunk loads +- protected final IntervalledCounter ticketAdditionCounterShort = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms ++ protected final IntervalledCounter ticketAdditionCounterShort = new IntervalledCounter((long) (1.0e6 * 50.0)); // 50ms + // this ensures the rate is kept between ticks correctly +- protected final IntervalledCounter ticketAdditionCounterLong = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms ++ protected final IntervalledCounter ticketAdditionCounterLong = new IntervalledCounter((long) (1.0e6 * 1000.0)); // 1000ms + + public long lastChunkLoad; + +@@ -986,14 +914,14 @@ public final class PlayerChunkLoader { + // b = ((p3z - p1z)(targetX - p3x) + (p1x - p3x)(targetZ - p3z)) / d + // c = 1.0 - a - b + +- final double d = (p2z - p3z)*(p1x - p3x) + (p3x - p2x)*(p1z - p3z); +- final double a = ((p2z - p3z)*(targetX - p3x) + (p3x - p2x)*(targetZ - p3z)) / d; ++ final double d = (p2z - p3z) * (p1x - p3x) + (p3x - p2x) * (p1z - p3z); ++ final double a = ((p2z - p3z) * (targetX - p3x) + (p3x - p2x) * (targetZ - p3z)) / d; + + if (a < 0.0 || a > 1.0) { + return false; + } + +- final double b = ((p3z - p1z)*(targetX - p3x) + (p1x - p3x)*(targetZ - p3z)) / d; ++ final double b = ((p3z - p1z) * (targetX - p3x) + (p1x - p3x) * (targetZ - p3z)) / d; + if (b < 0.0 || b > 1.0) { + return false; + } +@@ -1040,14 +968,14 @@ public final class PlayerChunkLoader { + && tickViewDistance == this.lastTickDistance + + && (this.usingLookingPriority ? ( +- // has our block stayed the same (this also accounts for chunk change)? +- Mth.floor(this.lastLocX) == Mth.floor(posX) ++ // has our block stayed the same (this also accounts for chunk change)? ++ Mth.floor(this.lastLocX) == Mth.floor(posX) + && Mth.floor(this.lastLocZ) == Mth.floor(posZ) +- ) : ( +- // has our chunk stayed the same +- (Mth.floor(this.lastLocX) >> 4) == (Mth.floor(posX) >> 4) ++ ) : ( ++ // has our chunk stayed the same ++ (Mth.floor(this.lastLocX) >> 4) == (Mth.floor(posX) >> 4) + && (Mth.floor(this.lastLocZ) >> 4) == (Mth.floor(posZ) >> 4) +- )) ++ )) + + // has our decision about look priority changed? + && this.usingLookingPriority == useLookPriority +@@ -1096,15 +1024,15 @@ public final class PlayerChunkLoader { + final double p1z = posZ; + + // to the left of the looking direction +- final double p2x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector ++ final double p2x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw + (double) (FOV / 2.0))) // calculate rotated vector + + p1x; // offset vector +- final double p2z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector ++ final double p2z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw + (double) (FOV / 2.0))) // calculate rotated vector + + p1z; // offset vector + + // to the right of the looking direction +- final double p3x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector ++ final double p3x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw - (double) (FOV / 2.0))) // calculate rotated vector + + p1x; // offset vector +- final double p3z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector ++ final double p3z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw - (double) (FOV / 2.0))) // calculate rotated vector + + p1z; // offset vector + + // now that we have all of our points, we can recalculate the load queue +@@ -1142,7 +1070,7 @@ public final class PlayerChunkLoader { + p1x, p1z, p2x, p2z, p3x, p3z, + + // center of chunk +- (double)((chunkX << 4) | 8), (double)((chunkZ << 4) | 8) ++ (double) ((chunkX << 4) | 8), (double) ((chunkZ << 4) | 8) + ); + + final int manhattanDistance = Math.abs(dx) + Math.abs(dz); +@@ -1157,9 +1085,9 @@ public final class PlayerChunkLoader { + if (prioritised) { + // we don't prioritise these chunks above others because we also want to make sure some chunks + // will be loaded if the player changes direction +- priority = (double)manhattanDistance / 6.0; ++ priority = (double) manhattanDistance / 6.0; + } else { +- priority = (double)manhattanDistance; ++ priority = (double) manhattanDistance; + } + } + +@@ -1186,23 +1114,13 @@ public final class PlayerChunkLoader { + }); + + // we're modifying loadQueue, must remove +- final long id3 = this.loader.loadLock.writeLock(); +- try { +- this.loader.chunkLoadQueue.remove(this); +- }finally { +- this.loader.loadLock.unlockWrite(id3); +- } ++ this.loader.chunkLoadQueue.remove(this); + + this.loadQueue.clear(); + this.loadQueue.addAll(loadQueue); + +- final long id4 = this.loader.loadLock.writeLock(); +- try { +- // must re-add +- this.loader.chunkLoadQueue.add(this); +- }finally { +- this.loader.loadLock.unlockWrite(id4); +- } ++ // must re-add ++ this.loader.chunkLoadQueue.add(this); + + // update the chunk center + // this must be done last so that the client does not ignore any of our unload chunk packets +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 36d3b0d0e1e716cb0e5a2e98c37a834776e26c20..26e1b4060f2a93cb659170f83e6ce64086e0eb0c 100644 +--- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java ++++ b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java +@@ -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; +@@ -49,17 +55,16 @@ public final class EntityLookup implements LevelEntityGetter { + + private final StampedLock entityByLock = new StampedLock(); + private final StampedLock stateLock = new StampedLock(); ++ //Hearse -- Just for multithreaded entity ticking + +- private final ReadWriteLock paraEntityLock = new ReentrantReadWriteLock(false); //Hearse -- Just for multithreaded entity ticking +- +- protected final Long2ObjectOpenHashMap regions = new Long2ObjectOpenHashMap<>(128, 0.5f); ++ protected final Long2ObjectMap regions = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(128, 0.5f)); + + private final int minSection; // inclusive + private final int maxSection; // inclusive + private final LevelCallback worldCallback; + +- private final Int2ReferenceOpenHashMap entityById = new Int2ReferenceOpenHashMap<>(); +- private final Object2ReferenceOpenHashMap entityByUUID = new Object2ReferenceOpenHashMap<>(); ++ private final Int2ReferenceMap entityById = Int2ReferenceMaps.synchronize(new Int2ReferenceOpenHashMap<>()); ++ private final Object2ReferenceMap entityByUUID = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenHashMap<>()); + private final EntityList accessibleEntities = new EntityList(); + + public EntityLookup(final ServerLevel world, final LevelCallback worldCallback) { +@@ -80,61 +85,51 @@ public final class EntityLookup implements LevelEntityGetter { + @Nullable + @Override + public Entity get(final int id) { +- this.paraEntityLock.readLock().lock();//Hearse +- try { +- final long attempt = this.entityByLock.tryOptimisticRead(); +- if (attempt != 0L) { +- try { +- final Entity ret = this.entityById.get(id); ++ final long attempt = this.entityByLock.tryOptimisticRead(); ++ if (attempt != 0L) { ++ try { ++ final Entity ret = this.entityById.get(id); + +- if (this.entityByLock.validate(attempt)) { +- return maskNonAccessible(ret); +- } +- } catch (final Error error) { +- throw error; +- } catch (final Throwable thr) { +- // ignore ++ if (this.entityByLock.validate(attempt)) { ++ return maskNonAccessible(ret); + } ++ } catch (final Error error) { ++ throw error; ++ } catch (final Throwable thr) { ++ // ignore + } ++ } + +- this.entityByLock.readLock(); +- try { +- return maskNonAccessible(this.entityById.get(id)); +- } finally { +- this.entityByLock.tryUnlockRead(); +- } +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse ++ this.entityByLock.readLock(); ++ try { ++ return maskNonAccessible(this.entityById.get(id)); ++ } finally { ++ this.entityByLock.tryUnlockRead(); + } + } + + @Nullable + @Override + public Entity get(final UUID id) { +- this.paraEntityLock.readLock().lock();//Hearse +- try { +- final long attempt = this.entityByLock.tryOptimisticRead(); +- if (attempt != 0L) { +- try { +- final Entity ret = this.entityByUUID.get(id); +- if (this.entityByLock.validate(attempt)) { +- return maskNonAccessible(ret); +- } +- } catch (final Error error) { +- throw error; +- } catch (final Throwable thr) { +- // ignore ++ final long attempt = this.entityByLock.tryOptimisticRead(); ++ if (attempt != 0L) { ++ try { ++ final Entity ret = this.entityByUUID.get(id); ++ if (this.entityByLock.validate(attempt)) { ++ return maskNonAccessible(ret); + } ++ } catch (final Error error) { ++ throw error; ++ } catch (final Throwable thr) { ++ // ignore + } ++ } + +- this.entityByLock.readLock(); +- try { +- return maskNonAccessible(this.entityByUUID.get(id)); +- } finally { +- this.entityByLock.tryUnlockRead(); +- } +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse ++ this.entityByLock.readLock(); ++ try { ++ return maskNonAccessible(this.entityByUUID.get(id)); ++ } finally { ++ this.entityByLock.tryUnlockRead(); + } + } + +@@ -143,12 +138,7 @@ public final class EntityLookup implements LevelEntityGetter { + } + + public String getDebugInfo() { +- this.paraEntityLock.readLock();//Hearse +- try { +- return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + this.regions.size(); +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse +- } ++ return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + this.regions.size(); + } + + static final class ArrayIterable implements Iterable { +@@ -206,30 +196,20 @@ public final class EntityLookup implements LevelEntityGetter { + + @Override + public Iterable getAll() { +- this.paraEntityLock.readLock().lock();//Hearse +- try { +- return new ArrayIterable<>(this.accessibleEntities.getRawData(), 0, this.accessibleEntities.size()); +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse +- } ++ return new ArrayIterable<>(this.accessibleEntities.getRawData(), 0, this.accessibleEntities.size()); + } + + @Override + public void get(final EntityTypeTest filter, final AbortableIterationConsumer action) { +- this.paraEntityLock.readLock().lock();//Hearse +- try { +- for (final Entity entity : this.entityById.values()) { +- final Visibility visibility = EntityLookup.getEntityStatus(entity); +- if (!visibility.isAccessible()) { +- continue; +- } +- final U casted = filter.tryCast(entity); +- if (casted != null && action.accept(casted).shouldAbort()) { +- break; +- } ++ for (final Entity entity : this.entityById.values()) { ++ final Visibility visibility = EntityLookup.getEntityStatus(entity); ++ if (!visibility.isAccessible()) { ++ continue; ++ } ++ final U casted = filter.tryCast(entity); ++ if (casted != null && action.accept(casted).shouldAbort()) { ++ break; + } +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -257,55 +237,50 @@ public final class EntityLookup implements LevelEntityGetter { + public void entityStatusChange(final Entity entity, final ChunkEntitySlices slices, final Visibility oldVisibility, final Visibility newVisibility, final boolean moved, + final boolean created, final boolean destroyed) { + TickThread.ensureTickThread(entity, "Entity status change must only happen on the main thread"); +- this.paraEntityLock.writeLock().lock();//Hearse ++ final Boolean ticketBlockBefore = this.world.chunkTaskScheduler.chunkHolderManager.blockTicketUpdates(); + try { +- final Boolean ticketBlockBefore = this.world.chunkTaskScheduler.chunkHolderManager.blockTicketUpdates(); +- 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 { +- this.world.chunkTaskScheduler.chunkHolderManager.unblockTicketUpdates(ticketBlockBefore); ++ } ++ ++ if (moved && newVisibility.isAccessible()) { ++ EntityLookup.this.worldCallback.onSectionChange(entity); ++ } ++ ++ if (destroyed) { ++ EntityLookup.this.worldCallback.onDestroyed(entity); + } + } finally { +- this.paraEntityLock.writeLock().lock();//Hearse ++ this.world.chunkTaskScheduler.chunkHolderManager.unblockTicketUpdates(ticketBlockBefore); + } + } + +@@ -350,53 +325,48 @@ public final class EntityLookup implements LevelEntityGetter { + final int sectionZ = pos.getZ() >> 4; + TickThread.ensureTickThread(this.world, sectionX, sectionZ, "Cannot add entity off-main thread"); + +- this.paraEntityLock.writeLock().lock();//Hearse +- try { ++ if (entity.isRemoved()) { ++ LOGGER.warn("Refusing to add removed entity: " + entity); ++ return false; ++ } ++ ++ if (fromDisk) { ++ ChunkSystem.onEntityPreAdd(this.world, entity); + if (entity.isRemoved()) { +- LOGGER.warn("Refusing to add removed entity: " + entity); ++ // removed from checkDupeUUID call + return false; + } ++ } + +- if (fromDisk) { +- ChunkSystem.onEntityPreAdd(this.world, entity); +- if (entity.isRemoved()) { +- // removed from checkDupeUUID call +- return false; +- } ++ this.entityByLock.writeLock(); ++ try { ++ if (this.entityById.containsKey(entity.getId())) { ++ LOGGER.warn("Entity id already exists: " + entity.getId() + ", mapped to " + this.entityById.get(entity.getId()) + ", can't add " + entity); ++ return false; + } +- +- this.entityByLock.writeLock(); +- try { +- if (this.entityById.containsKey(entity.getId())) { +- LOGGER.warn("Entity id already exists: " + entity.getId() + ", mapped to " + this.entityById.get(entity.getId()) + ", can't add " + entity); +- return false; +- } +- if (this.entityByUUID.containsKey(entity.getUUID())) { +- LOGGER.warn("Entity uuid already exists: " + entity.getUUID() + ", mapped to " + this.entityByUUID.get(entity.getUUID()) + ", can't add " + entity); +- return false; +- } +- this.entityById.put(entity.getId(), entity); +- this.entityByUUID.put(entity.getUUID(), entity); +- } finally { +- this.entityByLock.tryUnlockWrite(); ++ if (this.entityByUUID.containsKey(entity.getUUID())) { ++ LOGGER.warn("Entity uuid already exists: " + entity.getUUID() + ", mapped to " + this.entityByUUID.get(entity.getUUID()) + ", can't add " + entity); ++ return false; + } ++ this.entityById.put(entity.getId(), entity); ++ this.entityByUUID.put(entity.getUUID(), entity); ++ } finally { ++ this.entityByLock.tryUnlockWrite(); ++ } + +- entity.sectionX = sectionX; +- entity.sectionY = sectionY; +- entity.sectionZ = sectionZ; +- final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ); +- if (!slices.addEntity(entity, sectionY)) { +- LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")"); +- } ++ entity.sectionX = sectionX; ++ entity.sectionY = sectionY; ++ entity.sectionZ = sectionZ; ++ final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ); ++ if (!slices.addEntity(entity, sectionY)) { ++ LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")"); ++ } + +- entity.setLevelCallback(new EntityCallback(entity)); ++ entity.setLevelCallback(new EntityCallback(entity)); + +- this.entityStatusChange(entity, slices, Visibility.HIDDEN, getEntityStatus(entity), false, !fromDisk, false); ++ this.entityStatusChange(entity, slices, Visibility.HIDDEN, getEntityStatus(entity), false, !fromDisk, false); + +- return true; +- }finally { +- this.paraEntityLock.writeLock().unlock();//Hearse +- } ++ return true; + } + + private void removeEntity(final Entity entity) { +@@ -407,32 +377,27 @@ public final class EntityLookup implements LevelEntityGetter { + if (!entity.isRemoved()) { + throw new IllegalStateException("Only call Entity#setRemoved to remove an entity"); + } +- this.paraEntityLock.writeLock().lock();//Hearse +- try { +- final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ); +- // all entities should be in a chunk +- if (slices == null) { +- LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")"); +- } else { +- if (!slices.removeEntity(entity, sectionY)) { +- LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")"); +- } ++ final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ); ++ // all entities should be in a chunk ++ if (slices == null) { ++ LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")"); ++ } else { ++ if (!slices.removeEntity(entity, sectionY)) { ++ LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")"); + } +- entity.sectionX = entity.sectionY = entity.sectionZ = Integer.MIN_VALUE; ++ } ++ entity.sectionX = entity.sectionY = entity.sectionZ = Integer.MIN_VALUE; + +- this.entityByLock.writeLock(); +- try { +- if (!this.entityById.remove(entity.getId(), entity)) { +- LOGGER.warn("Failed to remove entity " + entity + " by id, current entity mapped: " + this.entityById.get(entity.getId())); +- } +- if (!this.entityByUUID.remove(entity.getUUID(), entity)) { +- LOGGER.warn("Failed to remove entity " + entity + " by uuid, current entity mapped: " + this.entityByUUID.get(entity.getUUID())); +- } +- } finally { +- this.entityByLock.tryUnlockWrite(); ++ this.entityByLock.writeLock(); ++ try { ++ if (!this.entityById.remove(entity.getId(), entity)) { ++ LOGGER.warn("Failed to remove entity " + entity + " by id, current entity mapped: " + this.entityById.get(entity.getId())); ++ } ++ if (!this.entityByUUID.remove(entity.getUUID(), entity)) { ++ LOGGER.warn("Failed to remove entity " + entity + " by uuid, current entity mapped: " + this.entityByUUID.get(entity.getUUID())); + } +- }finally { +- this.paraEntityLock.writeLock().unlock();//Hearse ++ } finally { ++ this.entityByLock.tryUnlockWrite(); + } + } + +@@ -455,27 +420,22 @@ public final class EntityLookup implements LevelEntityGetter { + // ensure the old section is owned by this tick thread + TickThread.ensureTickThread(this.world, entity.sectionX, entity.sectionZ, "Cannot move entity off-main"); + +- this.paraEntityLock.writeLock().lock();//Hearse +- try { +- final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ); +- final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ); ++ final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ); ++ final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ); + +- if (!old.removeEntity(entity, entity.sectionY)) { +- LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section"); +- } ++ if (!old.removeEntity(entity, entity.sectionY)) { ++ LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section"); ++ } + +- if (!slices.addEntity(entity, newSectionY)) { +- LOGGER.warn("Could not add entity " + entity + " to its new chunk section (" + newSectionX + "," + newSectionY + "," + newSectionZ + ") as it is already contained in the section"); +- } ++ if (!slices.addEntity(entity, newSectionY)) { ++ LOGGER.warn("Could not add entity " + entity + " to its new chunk section (" + newSectionX + "," + newSectionY + "," + newSectionZ + ") as it is already contained in the section"); ++ } + +- entity.sectionX = newSectionX; +- entity.sectionY = newSectionY; +- entity.sectionZ = newSectionZ; ++ entity.sectionX = newSectionX; ++ entity.sectionY = newSectionY; ++ entity.sectionZ = newSectionZ; + +- return slices; +- }finally { +- this.paraEntityLock.writeLock().unlock();//Hearse +- } ++ return slices; + } + + public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List into, final Predicate predicate) { +@@ -489,36 +449,31 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- this.paraEntityLock.readLock().lock();//Hearse +- try { +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- if (region == null) { +- continue; +- } +- +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ if (region == null) { ++ continue; ++ } + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- chunk.getEntitiesWithoutDragonParts(except, box, into, predicate); ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; + } ++ ++ chunk.getEntitiesWithoutDragonParts(except, box, into, predicate); + } + } + } +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -533,36 +488,31 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- this.paraEntityLock.readLock().lock();//Hearse +- try { +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; +- +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- if (region == null) { +- continue; +- } ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ if (region == null) { ++ continue; ++ } + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- chunk.getEntities(except, box, into, predicate); ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; + } ++ ++ chunk.getEntities(except, box, into, predicate); + } + } + } +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -577,36 +527,31 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- this.paraEntityLock.readLock().lock();//Hearse +- try { +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; +- +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- if (region == null) { +- continue; +- } ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ if (region == null) { ++ continue; ++ } + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- chunk.getHardCollidingEntities(except, box, into, predicate); ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; + } ++ ++ chunk.getHardCollidingEntities(except, box, into, predicate); + } + } + } +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -622,36 +567,31 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- this.paraEntityLock.readLock().lock();//Hearse +- try { +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; +- +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- if (region == null) { +- continue; +- } ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ if (region == null) { ++ continue; ++ } + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- chunk.getEntities(type, box, (List)into, (Predicate)predicate); ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; + } ++ ++ chunk.getEntities(type, box, (List) into, (Predicate) predicate); + } + } + } +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -667,36 +607,31 @@ public final class EntityLookup implements LevelEntityGetter { + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + +- this.paraEntityLock.readLock().unlock();//Hearse +- try { +- for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { +- final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; +- final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; +- +- for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { +- final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + +- if (region == null) { +- continue; +- } ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + +- final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; +- final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ if (region == null) { ++ continue; ++ } + +- for (int currZ = minZ; currZ <= maxZ; ++currZ) { +- for (int currX = minX; currX <= maxX; ++currX) { +- final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); +- if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { +- continue; +- } ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + +- chunk.getEntities(clazz, except, box, into, predicate); ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; + } ++ ++ chunk.getEntities(clazz, except, box, into, predicate); + } + } + } +- }finally { +- this.paraEntityLock.readLock().unlock();//Hearse + } + } + +@@ -875,9 +810,11 @@ public final class EntityLookup implements LevelEntityGetter { + public static final NoOpCallback INSTANCE = new NoOpCallback(); + + @Override +- public void onMove() {} ++ public void onMove() { ++ } + + @Override +- public void onRemove(final Entity.RemovalReason reason) {} ++ public void onRemove(final Entity.RemovalReason reason) { ++ } + } + } +diff --git a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java +index 0fd814f1d65c111266a2b20f86561839a4cef755..fe4d76875462ac9d408c972b968647af78f2ed14 100644 +--- a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java ++++ b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java +@@ -94,7 +94,7 @@ public final class IteratorSafeOrderedReferenceSet { + return 1.0 - ((double)this.indexMap.size() / (double)this.listSize); + } + +- public int createRawIterator() { ++ public synchronized int createRawIterator() { + if (this.allowSafeIteration()) { + ++this.iteratorCount; + } +@@ -105,7 +105,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + } + +- public int advanceRawIterator(final int index) { ++ public synchronized int advanceRawIterator(final int index) { + final E[] elements = this.listElements; + int ret = index + 1; + for (int len = this.listSize; ret < len; ++ret) { +@@ -117,7 +117,7 @@ public final class IteratorSafeOrderedReferenceSet { + return -1; + } + +- public void finishRawIterator() { ++ public synchronized void finishRawIterator() { + if (this.allowSafeIteration() && --this.iteratorCount == 0) { + if (this.getFragFactor() >= this.maxFragFactor) { + this.defrag(); +@@ -125,7 +125,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + } + +- public boolean remove(final E element) { ++ public synchronized boolean remove(final E element) { + final int index = this.indexMap.removeInt(element); + if (index >= 0) { + if (this.firstInvalidIndex < 0 || index < this.firstInvalidIndex) { +@@ -144,11 +144,11 @@ public final class IteratorSafeOrderedReferenceSet { + return false; + } + +- public boolean contains(final E element) { ++ public synchronized boolean contains(final E element) { + return this.indexMap.containsKey(element); + } + +- public boolean add(final E element) { ++ public synchronized boolean add(final E element) { + final int listSize = this.listSize; + + final int previous = this.indexMap.putIfAbsent(element, listSize); +@@ -223,30 +223,30 @@ public final class IteratorSafeOrderedReferenceSet { + //this.check(); + } + +- public E rawGet(final int index) { ++ public synchronized E rawGet(final int index) { + return this.listElements[index]; + } + +- public int size() { ++ public synchronized int size() { + // always returns the correct amount - listSize can be different + return this.indexMap.size(); + } + +- public IteratorSafeOrderedReferenceSet.Iterator iterator() { ++ public synchronized IteratorSafeOrderedReferenceSet.Iterator iterator() { + return this.iterator(0); + } + +- public IteratorSafeOrderedReferenceSet.Iterator iterator(final int flags) { ++ public synchronized IteratorSafeOrderedReferenceSet.Iterator iterator(final int flags) { + if (this.allowSafeIteration()) { + ++this.iteratorCount; + } + return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize); + } + +- public java.util.Iterator unsafeIterator() { ++ public synchronized java.util.Iterator unsafeIterator() { + return this.unsafeIterator(0); + } +- public java.util.Iterator unsafeIterator(final int flags) { ++ public synchronized java.util.Iterator unsafeIterator(final int flags) { + return new BaseIterator<>(this, false, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize); + } + +@@ -273,7 +273,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + + @Override +- public boolean hasNext() { ++ public synchronized boolean hasNext() { + if (this.finished) { + return false; + } +@@ -297,7 +297,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + + @Override +- public E next() { ++ public synchronized E next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } +@@ -310,7 +310,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + + @Override +- public void remove() { ++ public synchronized void remove() { + final E lastReturned = this.lastReturned; + if (lastReturned == null) { + throw new IllegalStateException(); +@@ -320,7 +320,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + + @Override +- public void finishedIterating() { ++ public synchronized void finishedIterating() { + if (this.finished || !this.canFinish) { + throw new IllegalStateException(); + } +diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +index f6c8dccb7f7cd287f1ebdf46481365b952baa891..ae22ca9ea5fd3d78d8c5bf9f1ab96f1129fddc11 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; +@@ -35,7 +36,7 @@ public final class ChunkEntitySlices { + + protected final EntityCollectionBySection allEntities; + protected final EntityCollectionBySection hardCollidingEntities; +- protected final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByClass; ++ protected final Reference2ObjectMap, EntityCollectionBySection> entitiesByClass; + protected final EntityList entities = new EntityList(); + + public ChunkHolder.FullChunkStatus status; +@@ -62,7 +63,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; + } +@@ -199,7 +200,7 @@ public final class ChunkEntitySlices { + } + + for (final Iterator, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { ++ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { + final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); + + if (entry.getKey().isInstance(entity)) { +@@ -224,7 +225,7 @@ public final class ChunkEntitySlices { + } + + for (final Iterator, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { ++ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { + final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); + + if (entry.getKey().isInstance(entity)) { +@@ -302,11 +303,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; + } + +@@ -318,7 +319,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(); +@@ -328,7 +329,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) { +@@ -340,7 +341,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; +@@ -357,7 +358,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; + } + } +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index 10c7d776ca0d959541d3110c75ceb45a340278ac..a23379120ddc3653b58bdb08f9c837c60b6b83ca 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -1,10 +1,7 @@ + package net.minecraft.server.level; + +-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.Lists; +-import com.google.common.collect.Queues; + import com.google.gson.JsonElement; + import com.mojang.datafixers.DataFixer; + import com.mojang.datafixers.util.Either; +@@ -103,7 +100,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + public final StructureTemplateManager structureTemplateManager; // Paper - rewrite chunk system + private final String storageName; + private final PlayerMap playerMap; +- public final Int2ObjectMap entityMap; ++ public final Map entityMap; + private final Long2ByteMap chunkTypeCache; + private final Long2LongMap chunkSaveCooldowns; + private final Queue unloadQueue; +@@ -251,7 +248,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper - rewrite chunk system + this.tickingGenerated = new AtomicInteger(); + this.playerMap = new PlayerMap(); +- this.entityMap = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap()); ++ this.entityMap = Maps.newConcurrentMap(); + this.chunkTypeCache = Long2ByteMaps.synchronize(new Long2ByteOpenHashMap()); + this.chunkSaveCooldowns = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); + this.unloadQueue = Queues.newConcurrentLinkedQueue(); +@@ -1165,11 +1162,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + ServerPlayer entityplayer = (ServerPlayer) entity; + + this.updatePlayerStatus(entityplayer, true); +- ObjectIterator objectiterator = this.entityMap.values().iterator(); +- +- while (objectiterator.hasNext()) { +- ChunkMap.TrackedEntity playerchunkmap_entitytracker1 = (ChunkMap.TrackedEntity) objectiterator.next(); + ++ for (TrackedEntity playerchunkmap_entitytracker1 : this.entityMap.values()) { + if (playerchunkmap_entitytracker1.entity != entityplayer) { + playerchunkmap_entitytracker1.updatePlayer(entityplayer); + } +@@ -1187,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); + } + } +@@ -1237,7 +1228,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper end - optimized tracker + List list = Lists.newArrayList(); + List list1 = this.level.players(); +- ObjectIterator objectiterator = this.entityMap.values().iterator(); ++ Iterator objectiterator = this.entityMap.values().iterator(); + //level.timings.tracker1.startTiming(); // Paper // Purpur + + ChunkMap.TrackedEntity playerchunkmap_entitytracker; +diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java +index 0c70810edace99bb5037d927388e055a514fcbde..0eadacb873658a0c7bd9ab24f191bc75eaebcaca 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.concurrent.locks.StampedLock; +@@ -20,7 +23,7 @@ import org.slf4j.Logger; + public abstract class BlockableEventLoop implements ProfilerMeasured, ProcessorHandle, Executor { + private final String name; + private static final Logger LOGGER = LogUtils.getLogger(); +- private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); ++ private final Deque pendingRunnables = new ConcurrentLinkedDeque<>(); + private final StampedLock lock = new StampedLock(); + private int blockingCount; + +@@ -44,12 +47,7 @@ public abstract class BlockableEventLoop implements Profiler + } + + public int getPendingTasksCount() { +- final long id = this.lock.readLock(); +- try{ +- return this.pendingRunnables.size(); +- }finally { +- this.lock.unlockRead(id); +- } ++ return this.pendingRunnables.size(); + } + + @Override +@@ -95,12 +93,7 @@ public abstract class BlockableEventLoop implements Profiler + + @Override + public void tell(R runnable) { +- final long id = this.lock.writeLock(); +- try { +- this.pendingRunnables.add(runnable); +- }finally { +- this.lock.unlockWrite(id); +- } ++ this.pendingRunnables.add(runnable); + LockSupport.unpark(this.getRunningThread()); + } + +@@ -119,12 +112,7 @@ public abstract class BlockableEventLoop implements Profiler + } + + protected void dropAllTasks() { +- final long id = this.lock.writeLock(); +- try { +- this.pendingRunnables.clear(); +- }finally { +- this.lock.unlockWrite(id); +- } ++ this.pendingRunnables.clear(); + } + + protected void runAllTasks() { +@@ -134,19 +122,15 @@ public abstract class BlockableEventLoop implements Profiler + } + + public boolean pollTask() { +- final long id = this.lock.writeLock(); +- try { +- R runnable = this.pendingRunnables.peek(); +- if (runnable == null) { +- return false; +- } else if (this.blockingCount == 0 && !this.shouldRun(runnable)) { +- return false; +- } else { +- this.doRunTask(this.pendingRunnables.remove()); +- return true; +- } +- }finally { +- this.lock.unlockWrite(id); ++ 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(runnable); ++ return true; + } + } + +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 00f7b58db5948f4d7c7f07736d8fcf1972009c77..dca7c7f83043452b5fef3c1d24a99f08dfaf242a 100644 +--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java ++++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +@@ -1,14 +1,15 @@ + package net.minecraft.world.level.entity; + +-import java.util.Iterator; +-import java.util.Set; ++import java.util.*; + import java.util.function.Consumer; ++ ++import com.google.common.collect.Lists; + import it.unimi.dsi.fastutil.objects.ObjectArraySet; + import it.unimi.dsi.fastutil.objects.ObjectSets; + import net.minecraft.world.entity.Entity; + + public class EntityTickList { +- public final Set entities = ObjectSets.synchronize(new ObjectArraySet<>()); ++ public final List entities = Lists.newCopyOnWriteArrayList(); + + public void add(Entity entity) { + io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper +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 a097e00bd62f53630568f68854d3a34300012277..799cd9d04d156ed87e9b1dfde75ae15280c9eb0d 100644 +--- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java ++++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java +@@ -22,8 +22,6 @@ public class CollectingNeighborUpdater implements NeighborUpdater { + private final List addedThisLayer = new CopyOnWriteArrayList<>(); + private int count = 0; + +- private final StampedLock lock = new StampedLock(); +- + public CollectingNeighborUpdater(Level world, int maxChainDepth) { + this.level = world; + this.maxChainedNeighborUpdates = maxChainDepth; +@@ -49,23 +47,18 @@ public class CollectingNeighborUpdater implements NeighborUpdater { + this.addAndRun(pos, new CollectingNeighborUpdater.MultiNeighborUpdate(pos.immutable(), sourceBlock, except)); + } + +- private void addAndRun(BlockPos pos, CollectingNeighborUpdater.NeighborUpdates entry) { ++ private synchronized void addAndRun(BlockPos pos, CollectingNeighborUpdater.NeighborUpdates entry) { + boolean bl = this.count > 0; + boolean bl2 = this.maxChainedNeighborUpdates >= 0 && this.count >= this.maxChainedNeighborUpdates; + ++this.count; +- final long lockId = this.lock.writeLock(); +- try { +- if (!bl2) { +- if (bl) { +- this.addedThisLayer.add(entry); +- } else { +- this.stack.push(entry); +- } +- } else if (this.count - 1 == this.maxChainedNeighborUpdates) { +- LOGGER.error("Too many chained neighbor updates. Skipping the rest. First skipped position: " + pos.toShortString()); ++ if (!bl2) { ++ if (bl) { ++ this.addedThisLayer.add(entry); ++ } else { ++ this.stack.push(entry); + } +- }finally { +- this.lock.unlockWrite(lockId); ++ } else if (this.count - 1 == this.maxChainedNeighborUpdates) { ++ LOGGER.error("Too many chained neighbor updates. Skipping the rest. First skipped position: " + pos.toShortString()); + } + + if (!bl) { +@@ -75,30 +68,25 @@ public class CollectingNeighborUpdater implements NeighborUpdater { + } + + private void runUpdates() { +- final long lockid = this.lock.writeLock(); + try { +- try { +- while(!this.stack.isEmpty() || !this.addedThisLayer.isEmpty()) { +- for(int i = this.addedThisLayer.size() - 1; i >= 0; --i) { +- this.stack.push(this.addedThisLayer.get(i)); +- } +- this.addedThisLayer.clear(); +- CollectingNeighborUpdater.NeighborUpdates neighborUpdates = this.stack.peek(); +- +- while(this.addedThisLayer.isEmpty()) { +- if (!neighborUpdates.runNext(this.level)) { +- this.stack.pop(); +- break; +- } +- } ++ while (!this.stack.isEmpty() || !this.addedThisLayer.isEmpty()) { ++ for (int i = this.addedThisLayer.size() - 1; i >= 0; --i) { ++ this.stack.push(this.addedThisLayer.get(i)); + } +- } finally { +- this.stack.clear(); + this.addedThisLayer.clear(); +- this.count = 0; ++ CollectingNeighborUpdater.NeighborUpdates neighborUpdates = this.stack.peek(); ++ ++ while (this.addedThisLayer.isEmpty()) { ++ if (!neighborUpdates.runNext(this.level)) { ++ this.stack.pop(); ++ break; ++ } ++ } + } +- }finally { +- this.lock.unlockWrite(lockid); ++ } finally { ++ this.stack.clear(); ++ this.addedThisLayer.clear(); ++ this.count = 0; + } + } + diff --git a/patches/server/0048-Hearse-Fix-some-problems-in-ItemEntity.patch b/patches/server/0048-Hearse-Fix-some-problems-in-ItemEntity.patch new file mode 100644 index 00000000..919be843 --- /dev/null +++ b/patches/server/0048-Hearse-Fix-some-problems-in-ItemEntity.patch @@ -0,0 +1,96 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 12:40:56 +0800 +Subject: [PATCH] Hearse: Fix some problems in ItemEntity + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/HearseConfig.java b/src/main/java/co/earthme/hearse/HearseConfig.java +index 912da4787f83f656da67e9533b60183c17e6c345..0a1de52bcdf675b9bfcbf14d39959818a7a0cbbb 100644 +--- a/src/main/java/co/earthme/hearse/HearseConfig.java ++++ b/src/main/java/co/earthme/hearse/HearseConfig.java +@@ -1,4 +1,5 @@ + package co.earthme.hearse; + + public class HearseConfig { ++ + } +diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +index c58496c84b2b3f86890050813041fa49711f3a01..3e8b68b4390a6086b1b1983fe6f8b545c2847ef8 100644 +--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java ++++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +@@ -4,6 +4,8 @@ import java.util.Iterator; + import java.util.List; + import java.util.Objects; + import java.util.UUID; ++import java.util.concurrent.locks.Lock; ++import java.util.concurrent.locks.ReentrantLock; + import javax.annotation.Nullable; + import net.minecraft.world.damagesource.DamageSource; + import net.minecraft.world.entity.Entity; +@@ -239,21 +241,24 @@ public class ItemEntity extends Entity { + this.setDeltaMovement(vec3d.x * 0.949999988079071D, vec3d.y + (double) (vec3d.y < 0.05999999865889549D ? 5.0E-4F : 0.0F), vec3d.z * 0.949999988079071D); + } + ++ private final Lock mergeLock = new ReentrantLock(); ++ + private void mergeWithNeighbours() { +- if (this.isMergable()) { +- // Spigot start +- double radius = level.spigotConfig.itemMerge; +- List list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> { +- // Spigot end +- return entityitem != this && entityitem.isMergable(); +- }); +- Iterator iterator = list.iterator(); +- +- while (iterator.hasNext()) { +- ItemEntity entityitem = (ItemEntity) iterator.next(); +- +- if (entityitem.isMergable()) { +- // Paper Start - Fix items merging through walls ++ if (!this.mergeLock.tryLock()){ ++ return; ++ } ++ try { ++ if (this.isMergable()) { ++ // Spigot start ++ double radius = level.spigotConfig.itemMerge; ++ List list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> { ++ // Spigot end ++ return entityitem != this && entityitem.isMergable(); ++ }); ++ ++ for (ItemEntity entityitem : list) { ++ if (entityitem.isMergable()) { ++ // Paper Start - Fix items merging through walls + if (this.level.paperConfig().fixes.fixItemsMergingThroughWalls) { + // Pufferfish start - skip the allocations + /* +@@ -263,17 +268,19 @@ public class ItemEntity extends Entity { + if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue; + */ + if (level.rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) == +- net.minecraft.world.phys.HitResult.Type.BLOCK) continue; ++ net.minecraft.world.phys.HitResult.Type.BLOCK) continue; + // Pufferfish end + } +- // Paper End +- this.tryToMerge(entityitem); +- if (this.isRemoved()) { +- break; ++ // Paper End ++ this.tryToMerge(entityitem); ++ if (this.isRemoved()) { ++ break; ++ } + } + } + } +- ++ }finally { ++ this.mergeLock.unlock(); + } + } + diff --git a/patches/server/0049-Hearse-Fix-some-problems-in-GoalSelector-and-Shuffli.patch b/patches/server/0049-Hearse-Fix-some-problems-in-GoalSelector-and-Shuffli.patch new file mode 100644 index 00000000..3ebcd595 --- /dev/null +++ b/patches/server/0049-Hearse-Fix-some-problems-in-GoalSelector-and-Shuffli.patch @@ -0,0 +1,185 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 13:15:09 +0800 +Subject: [PATCH] Hearse: Fix some problems in GoalSelector and ShufflingList + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java +index b3329c6fcd6758a781a51f5ba8f5052ac1c77b49..adb02cba6cdb62752f847136000c6f7ca857bd5a 100644 +--- a/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java ++++ b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java +@@ -2,9 +2,6 @@ package com.destroystokyo.paper.util.set; + + import java.util.Collection; + +-/** +- * @author Spottedleaf +- */ + public final class OptimizedSmallEnumSet> { + + private final Class enumClass; +@@ -20,7 +17,7 @@ public final class OptimizedSmallEnumSet> { + this.enumClass = clazz; + } + +- public boolean addUnchecked(final E element) { ++ public synchronized boolean addUnchecked(final E element) { + final int ordinal = element.ordinal(); + final long key = 1L << ordinal; + +@@ -30,7 +27,7 @@ public final class OptimizedSmallEnumSet> { + return (prev & key) == 0; + } + +- public boolean removeUnchecked(final E element) { ++ public synchronized boolean removeUnchecked(final E element) { + final int ordinal = element.ordinal(); + final long key = 1L << ordinal; + +@@ -40,15 +37,15 @@ public final class OptimizedSmallEnumSet> { + return (prev & key) != 0; + } + +- public void clear() { ++ public synchronized void clear() { + this.backingSet = 0L; + } + +- public int size() { ++ public synchronized int size() { + return Long.bitCount(this.backingSet); + } + +- public void addAllUnchecked(final Collection enums) { ++ public synchronized void addAllUnchecked(final Collection enums) { + for (final E element : enums) { + if (element == null) { + throw new NullPointerException("Null element"); +@@ -57,15 +54,15 @@ public final class OptimizedSmallEnumSet> { + } + } + +- public long getBackingSet() { ++ public synchronized long getBackingSet() { + return this.backingSet; + } + +- public boolean hasCommonElements(final OptimizedSmallEnumSet other) { ++ public synchronized boolean hasCommonElements(final OptimizedSmallEnumSet other) { + return (other.backingSet & this.backingSet) != 0; + } + +- public boolean hasElement(final E element) { ++ public synchronized boolean hasElement(final E element) { + return (this.backingSet & (1L << element.ordinal())) != 0; + } + } +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java +index fe3ab3d388f0481fb0db06b7f730f868dbf8e8a5..ac006bacbe8715e5c272c69afd1edab45a6511e8 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java +@@ -25,7 +25,7 @@ public class ShufflingList implements Iterable { + public ShufflingList(boolean isUnsafe) { + this.isUnsafe = isUnsafe; + // Paper end +- this.entries = Lists.newArrayList(); ++ this.entries = Lists.newCopyOnWriteArrayList(); + } + + private ShufflingList(List> list) { +@@ -35,7 +35,7 @@ public class ShufflingList implements Iterable { + private ShufflingList(List> list, boolean isUnsafe) { + this.isUnsafe = isUnsafe; + // Paper end +- this.entries = Lists.newArrayList(list); ++ this.entries = Lists.newCopyOnWriteArrayList(list); + } + + public static Codec> codec(Codec codec) { +@@ -44,12 +44,12 @@ public class ShufflingList implements Iterable { + }); + } + +- public ShufflingList add(U data, int weight) { ++ public synchronized ShufflingList add(U data, int weight) { + this.entries.add(new ShufflingList.WeightedEntry<>(data, weight)); + return this; + } + +- public ShufflingList shuffle() { ++ public synchronized ShufflingList shuffle() { + // Paper start - make concurrent safe, work off a clone of the list + List> list = this.isUnsafe ? Lists.newArrayList(this.entries) : this.entries; + list.forEach(entry -> entry.setRandom(this.random.nextFloat())); +@@ -58,17 +58,17 @@ public class ShufflingList implements Iterable { + // Paper end + } + +- public Stream stream() { ++ public synchronized Stream stream() { + return this.entries.stream().map(ShufflingList.WeightedEntry::getData); + } + + @Override +- public Iterator iterator() { ++ public synchronized Iterator iterator() { + return Iterators.transform(this.entries.iterator(), ShufflingList.WeightedEntry::getData); + } + + @Override +- public String toString() { ++ public synchronized String toString() { + return "ShufflingList[" + this.entries + "]"; + } + +@@ -90,16 +90,16 @@ public class ShufflingList implements Iterable { + this.randWeight = -Math.pow((double)random, (double)(1.0F / (float)this.weight)); + } + +- public T getData() { ++ public synchronized T getData() { + return this.data; + } + +- public int getWeight() { ++ public synchronized int getWeight() { + return this.weight; + } + + @Override +- public String toString() { ++ public synchronized String toString() { + return this.weight + ":" + this.data; + } + +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +index 02978315bc2b828cc603ce7478408f3f82c249c2..96d37e0845df9b22cf60f9835787789d0d0e4a79 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +@@ -3,11 +3,8 @@ package net.minecraft.world.entity.ai.goal; + import com.google.common.annotations.VisibleForTesting; + import com.google.common.collect.Sets; + import com.mojang.logging.LogUtils; +-import java.util.EnumMap; +-import java.util.EnumSet; +-import java.util.Iterator; +-import java.util.Map; +-import java.util.Set; ++ ++import java.util.*; + import java.util.function.Predicate; + import java.util.function.Supplier; + import java.util.stream.Stream; +@@ -27,8 +24,8 @@ public class GoalSelector { + return false; + } + }; +- private final Map lockedFlags = new EnumMap<>(Goal.Flag.class); +- private final Set availableGoals = Sets.newLinkedHashSet(); ++ private final Map lockedFlags = Collections.synchronizedMap(new EnumMap<>(Goal.Flag.class)); ++ private final Set availableGoals = Sets.newCopyOnWriteArraySet(); + private final Supplier profiler; + private final EnumSet disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be. + private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector diff --git a/patches/server/0050-Hearse-Fix-an-UOE-in-GoalSelector.patch b/patches/server/0050-Hearse-Fix-an-UOE-in-GoalSelector.patch new file mode 100644 index 00000000..e0233d35 --- /dev/null +++ b/patches/server/0050-Hearse-Fix-an-UOE-in-GoalSelector.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 13:30:48 +0800 +Subject: [PATCH] Hearse: Fix an UOE in GoalSelector + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index a23379120ddc3653b58bdb08f9c837c60b6b83ca..dfb747eba6bf7088af0ff400da169de00a076365 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -1136,8 +1136,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot + // Paper start - ignore and warn about illegal addEntity calls instead of crashing server + if (!entity.valid || entity.level != this.level || this.entityMap.containsKey(entity.getId())) { +- LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName() +- + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable()); ++ LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName() + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : "")); + return; + } + if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Delay adding to tracker until after list packets +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +index 96d37e0845df9b22cf60f9835787789d0d0e4a79..99142f749371828f6f55e4fbab03b22eb519ec1e 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +@@ -25,7 +25,7 @@ public class GoalSelector { + } + }; + private final Map lockedFlags = Collections.synchronizedMap(new EnumMap<>(Goal.Flag.class)); +- private final Set availableGoals = Sets.newCopyOnWriteArraySet(); ++ private final Set availableGoals = Sets.newConcurrentHashSet(); + private final Supplier profiler; + private final EnumSet disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be. + private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector diff --git a/patches/server/0051-Hearse-Add-config-system-and-rename-a-class.patch b/patches/server/0051-Hearse-Add-config-system-and-rename-a-class.patch new file mode 100644 index 00000000..5e2b0aae --- /dev/null +++ b/patches/server/0051-Hearse-Add-config-system-and-rename-a-class.patch @@ -0,0 +1,269 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 13:55:50 +0800 +Subject: [PATCH] Hearse: Add config system and rename a class + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/HearseConfig.java b/src/main/java/co/earthme/hearse/HearseConfig.java +index 0a1de52bcdf675b9bfcbf14d39959818a7a0cbbb..73b5e76660b5162a7a0b327ddc7dcc3295b86699 100644 +--- a/src/main/java/co/earthme/hearse/HearseConfig.java ++++ b/src/main/java/co/earthme/hearse/HearseConfig.java +@@ -1,5 +1,49 @@ + package co.earthme.hearse; + ++import org.bukkit.configuration.InvalidConfigurationException; ++import org.bukkit.configuration.file.YamlConfiguration; ++import java.io.File; ++import java.io.IOException; ++ + public class HearseConfig { ++ private static final YamlConfiguration configEntry = new YamlConfiguration(); ++ private static final File CONFIG_FILE = new File("hearse.yml"); ++ ++ public static void init(){ ++ try { ++ configEntry.load(CONFIG_FILE); ++ }catch (IOException ignored){ ++ } catch (InvalidConfigurationException e) { ++ e.printStackTrace(); ++ } ++ configEntry.options().copyDefaults(true); ++ } ++ ++ public static void save(){ ++ try { ++ configEntry.save(CONFIG_FILE); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ ++ public static int getInt(String key,int def){ ++ configEntry.addDefault(key,def); ++ return configEntry.getInt(key); ++ } ++ ++ public static long getLong(String key,int def){ ++ configEntry.addDefault(key,def); ++ return configEntry.getLong(key); ++ } ++ ++ public static String getString(String key,String def){ ++ configEntry.addDefault(key,def); ++ return configEntry.getString(key); ++ } + ++ public static boolean getBoolean(String key,boolean def){ ++ configEntry.addDefault(key,def); ++ return configEntry.getBoolean(key); ++ } + } +diff --git a/src/main/java/co/earthme/hearse/server/ServerHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +similarity index 60% +rename from src/main/java/co/earthme/hearse/server/ServerHook.java +rename to src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +index 524a55c3298a079e416c742641af55725a602a2b..8da657836933ae6080e6594ff57dff84155e1820 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +@@ -1,36 +1,51 @@ + package co.earthme.hearse.server; + ++import co.earthme.hearse.HearseConfig; + import co.earthme.hearse.concurrent.WorkerThread; + import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.entity.Entity; +-import org.apache.logging.log4j.LogManager; + + import java.util.concurrent.LinkedBlockingQueue; + import java.util.concurrent.TimeUnit; + import java.util.concurrent.atomic.AtomicInteger; + +-public class ServerHook { ++public class ServerEntityTickHook { + private static volatile boolean firstTick = false; + private static final AtomicInteger threadId = new AtomicInteger(); +- private static final WorkerThreadPoolExecutor worker = new WorkerThreadPoolExecutor( +- Runtime.getRuntime().availableProcessors(), +- Runtime.getRuntime().availableProcessors(), +- 100, +- TimeUnit.MILLISECONDS, +- new LinkedBlockingQueue<>(), +- task -> { +- WorkerThread workerThread = new WorkerThread(task,"Hearse-Worker-Thread # "+threadId.getAndIncrement()); +- return workerThread; +- } +- ); ++ private static WorkerThreadPoolExecutor worker; ++ private static boolean asyncEntityEnabled; + + public static void executeAsyncTask(Runnable task){ ++ if (!asyncEntityEnabled){ ++ throw new IllegalStateException(); ++ } + worker.execute(task); + } + ++ public static void init(){ ++ final boolean asyncEntityEnabled1 = HearseConfig.getBoolean("enable-async-entity",true); ++ final int workerCount = HearseConfig.getInt("async-entity-worker-count",Runtime.getRuntime().availableProcessors()); ++ if (asyncEntityEnabled1){ ++ worker = new WorkerThreadPoolExecutor( ++ workerCount, ++ workerCount, ++ 100, ++ TimeUnit.MILLISECONDS, ++ new LinkedBlockingQueue<>(), ++ task -> { ++ return new WorkerThread(task,"Hearse-Worker-Thread # "+threadId.getAndIncrement()); ++ } ++ ); ++ } ++ asyncEntityEnabled = asyncEntityEnabled1; ++ } ++ + public static void executeAsyncTaskWithMainThreadCallback(Runnable task,Runnable callBack){ ++ if (!asyncEntityEnabled){ ++ throw new IllegalStateException(); ++ } + worker.executeWithSubTask(task,callBack); + } + +@@ -39,12 +54,15 @@ public class ServerHook { + firstTick = true; + return; + } ++ if (!asyncEntityEnabled){ ++ return; ++ } + worker.runAllSubTasks(); + } + + public static void callAsyncEntityTick(Entity entity, ServerLevel level){ + MinecraftServer.getServer().executeMidTickTasks(); +- worker.execute(()->{ ++ Runnable task = ()->{ + entity.activatedPriorityReset = false; + if (!entity.isRemoved()) { + entity.checkDespawn(); +@@ -63,6 +81,11 @@ public class ServerHook { + throwable.printStackTrace(); + } + } +- }); ++ }; ++ if (!asyncEntityEnabled){ ++ task.run(); ++ return; ++ } ++ worker.execute(task); + } + } +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index e0e169a4403926ff6be004de1bf5ec2079acb440..4a9fc14ba51f8177242c0573d37fd1b4742aa0ae 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1,11 +1,9 @@ + package net.minecraft.server; + +-import co.earthme.hearse.server.ServerHook; ++import co.earthme.hearse.HearseConfig; ++import co.earthme.hearse.server.ServerEntityTickHook; + import com.google.common.base.Splitter; + import com.google.common.collect.ImmutableList; +-import co.aikar.timings.Timings; +-import com.destroystokyo.paper.event.server.PaperServerListPingEvent; +-import com.google.common.base.Stopwatch; + import com.google.common.collect.Lists; + import com.google.common.collect.Maps; + import com.google.common.collect.Sets; +@@ -86,7 +84,6 @@ import net.minecraft.server.level.ServerChunkCache; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.server.level.ServerPlayer; + import net.minecraft.server.level.ServerPlayerGameMode; +-import net.minecraft.server.level.TicketType; + import net.minecraft.server.level.progress.ChunkProgressListener; + import net.minecraft.server.level.progress.ChunkProgressListenerFactory; + import net.minecraft.server.network.ServerConnectionListener; +@@ -110,17 +107,14 @@ import net.minecraft.util.NativeModuleLister; + import net.minecraft.util.ProgressListener; + import net.minecraft.util.RandomSource; + import net.minecraft.util.SignatureValidator; +-import net.minecraft.util.Unit; + import net.minecraft.util.datafix.DataFixers; + import net.minecraft.util.profiling.EmptyProfileResults; + import net.minecraft.util.profiling.ProfileResults; + import net.minecraft.util.profiling.ProfilerFiller; + import net.minecraft.util.profiling.ResultField; +-import net.minecraft.util.profiling.SingleTickProfiler; + import net.minecraft.util.profiling.jfr.JvmProfiler; + import net.minecraft.util.profiling.jfr.callback.ProfiledDuration; + import net.minecraft.util.profiling.metrics.profiling.ActiveMetricsRecorder; +-import net.minecraft.util.profiling.metrics.profiling.InactiveMetricsRecorder; + import net.minecraft.util.profiling.metrics.profiling.MetricsRecorder; + import net.minecraft.util.profiling.metrics.profiling.ServerMetricsSamplersProvider; + import net.minecraft.util.profiling.metrics.storage.MetricsPersister; +@@ -184,12 +178,6 @@ import net.minecraft.world.level.levelgen.PatrolSpawner; + import net.minecraft.world.level.levelgen.PhantomSpawner; + import net.minecraft.world.level.levelgen.WorldDimensions; + import net.minecraft.world.level.levelgen.presets.WorldPresets; +-import org.bukkit.Bukkit; +-import org.bukkit.craftbukkit.CraftServer; +-import org.bukkit.craftbukkit.Main; +-import org.bukkit.craftbukkit.util.CraftChatMessage; +-import org.bukkit.craftbukkit.util.LazyPlayerSet; +-import org.bukkit.event.player.AsyncPlayerChatPreviewEvent; + import org.bukkit.event.server.ServerLoadEvent; + // CraftBukkit end + +@@ -411,6 +399,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 5000000000L) { + this.lastServerStatus = i; + this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount())); +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index bc3cc08cd6effb9328ec74e206fe24bafc4e3d16..68523cb53573baa8ca98177f40acac3745cd625a 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -2,7 +2,7 @@ package net.minecraft.server.level; + + import co.aikar.timings.TimingHistory; + import co.earthme.hearse.concurrent.WorkerThread; +-import co.earthme.hearse.server.ServerHook; ++import co.earthme.hearse.server.ServerEntityTickHook; + import com.google.common.annotations.VisibleForTesting; + import com.google.common.collect.Lists; + import com.google.common.collect.Sets; +@@ -651,7 +651,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + } + org.spigotmc.ActivationRange.activateEntities(this); // Spigot + this.entityTickList.forEach((entity) -> { +- ServerHook.callAsyncEntityTick(entity,this); ++ ServerEntityTickHook.callAsyncEntityTick(entity,this); + }); + this.tickBlockEntities(); + } diff --git a/patches/server/0052-Hearse-Run-tasks-when-tick-start.patch b/patches/server/0052-Hearse-Run-tasks-when-tick-start.patch new file mode 100644 index 00000000..f17b004b --- /dev/null +++ b/patches/server/0052-Hearse-Run-tasks-when-tick-start.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 14:55:15 +0800 +Subject: [PATCH] Hearse: Run tasks when tick start + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +index 52f0c9dddf29a28cc360fbacb923445e5c3f82a6..783c8ea55095dbabe594a9fe3dc604515bd0c2f1 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java ++++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +@@ -4,7 +4,6 @@ import io.papermc.paper.util.TickThread; + + public class WorkerThread extends TickThread { + +- + public WorkerThread(String name) { + super(name); + this.setDaemon(true); +diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +index 8da657836933ae6080e6594ff57dff84155e1820..2c2f752e1ae846c2b24c2d46a13473836c5feac6 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +@@ -49,7 +49,7 @@ public class ServerEntityTickHook { + worker.executeWithSubTask(task,callBack); + } + +- public static void callPostTick(){ ++ public static void callTickStart(){ + if (!firstTick){ + firstTick = true; + return; +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 4a9fc14ba51f8177242c0573d37fd1b4742aa0ae..3a7435aabbd759222fbac41ab4ca304bd112a0ac 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1396,10 +1396,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 5000000000L) { + this.lastServerStatus = i; + this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount())); diff --git a/patches/server/0053-Hearse-Adjust-some-locks.patch b/patches/server/0053-Hearse-Adjust-some-locks.patch new file mode 100644 index 00000000..06e4adb9 --- /dev/null +++ b/patches/server/0053-Hearse-Adjust-some-locks.patch @@ -0,0 +1,71 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 14:57:23 +0800 +Subject: [PATCH] Hearse: Adjust some locks + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java +index 799cd9d04d156ed87e9b1dfde75ae15280c9eb0d..a1ff442357dfea868c319fd3c10ae28e6fb81956 100644 +--- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java ++++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java +@@ -3,7 +3,9 @@ package net.minecraft.world.level.redstone; + import com.mojang.logging.LogUtils; + import java.util.ArrayDeque; + import java.util.ArrayList; ++import java.util.Deque; + import java.util.List; ++import java.util.concurrent.ConcurrentLinkedDeque; + import java.util.concurrent.CopyOnWriteArrayList; + import java.util.concurrent.locks.StampedLock; + import javax.annotation.Nullable; +@@ -18,7 +20,7 @@ public class CollectingNeighborUpdater implements NeighborUpdater { + private static final Logger LOGGER = LogUtils.getLogger(); + private final Level level; + private final int maxChainedNeighborUpdates; +- private final ArrayDeque stack = new ArrayDeque<>(); ++ private final Deque stack = new ConcurrentLinkedDeque<>(); + private final List addedThisLayer = new CopyOnWriteArrayList<>(); + private int count = 0; + +@@ -28,26 +30,26 @@ public class CollectingNeighborUpdater implements NeighborUpdater { + } + + @Override +- public void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) { ++ public synchronized void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) { + this.addAndRun(pos, new CollectingNeighborUpdater.ShapeUpdate(direction, neighborState, pos.immutable(), neighborPos.immutable(), flags)); + } + + @Override +- public void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) { ++ public synchronized void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) { + this.addAndRun(pos, new CollectingNeighborUpdater.SimpleNeighborUpdate(pos, sourceBlock, sourcePos.immutable())); + } + + @Override +- public void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) { ++ public synchronized void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) { + this.addAndRun(pos, new CollectingNeighborUpdater.FullNeighborUpdate(state, pos.immutable(), sourceBlock, sourcePos.immutable(), notify)); + } + + @Override +- public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) { ++ public synchronized void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) { + this.addAndRun(pos, new CollectingNeighborUpdater.MultiNeighborUpdate(pos.immutable(), sourceBlock, except)); + } + +- private synchronized void addAndRun(BlockPos pos, CollectingNeighborUpdater.NeighborUpdates entry) { ++ private void addAndRun(BlockPos pos, CollectingNeighborUpdater.NeighborUpdates entry) { + boolean bl = this.count > 0; + boolean bl2 = this.maxChainedNeighborUpdates >= 0 && this.count >= this.maxChainedNeighborUpdates; + ++this.count; +@@ -64,7 +66,6 @@ public class CollectingNeighborUpdater implements NeighborUpdater { + if (!bl) { + this.runUpdates(); + } +- + } + + private void runUpdates() { diff --git a/patches/server/0054-Hearse-Fix-some-concurrent-problems.patch b/patches/server/0054-Hearse-Fix-some-concurrent-problems.patch new file mode 100644 index 00000000..26ad708c --- /dev/null +++ b/patches/server/0054-Hearse-Fix-some-concurrent-problems.patch @@ -0,0 +1,1008 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 18:43:19 +0800 +Subject: [PATCH] Hearse: Fix some concurrent problems + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +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 ++ */ ++ ++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 weakly consistent, returning ++ * elements reflecting the state of the deque at some point at or since the ++ * creation of the iterator. They do not throw ++ * {@link ConcurrentModificationException}, and may proceed concurrently with ++ * other operations. ++ * ++ *

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

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

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

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

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

++ * ++ * If the deque fits in the specified array with room to spare (i.e., the array ++ * has more elements than the deque), the element in the array immediately ++ * following the end of the collection is set to null. This is useful in ++ * determining the length of the deque only if the caller knows that the ++ * deque does not contain any null elements. ++ * ++ * @param a the array into which the elements of the deque are to be stored, if ++ * it is big enough; otherwise, a new array of the same runtime type is ++ * allocated for this purpose. ++ * @return an array containing the elements of the deque. ++ * @throws ArrayStoreException if the runtime type of a is not a supertype of ++ * the runtime type of every element in this deque. ++ * @throws NullPointerException if the specified array is null. ++ */ ++ public T[] toArray(T[] a) { ++ return toArrayList().toArray(a); ++ } ++ ++ /** ++ * Returns a weakly consistent iterator over the elements in this deque, in ++ * first-to-last order. The next method returns elements reflecting the ++ * state of the deque at some point at or since the creation of the iterator. ++ * The method does not throw {@link ConcurrentModificationException}, ++ * and may proceed concurrently with other operations. ++ * ++ * @return an iterator over the elements in this deque ++ */ ++ public Iterator iterator() { ++ return new CLDIterator(); ++ } ++ ++ final class CLDIterator implements Iterator { ++ Node last; ++ ++ Node next = header.forward(); ++ ++ public boolean hasNext() { ++ return next != null; ++ } ++ ++ public E next() { ++ Node l = last = next; ++ if (l == null) ++ throw new NoSuchElementException(); ++ next = next.forward(); ++ return l.element; ++ } ++ ++ public void remove() { ++ Node l = last; ++ if (l == null) ++ throw new IllegalStateException(); ++ while (!l.delete() && !l.isDeleted()) ++ ; ++ } ++ } ++ ++ @Override ++ public boolean addAll(int index, Collection c) { ++ throw new NotImplementedException("TODO"); ++ } ++ ++ @Override ++ public E get(int index) { ++ Node current = header.successor(); ++ if (current == null) { ++ throw new IndexOutOfBoundsException(); ++ } ++ for (; index > 0; index --) { ++ current = current.successor(); ++ if (current == null) { ++ throw new IndexOutOfBoundsException(); ++ } ++ } ++ return current.element; ++ } ++ ++ @Override ++ public E set(int index, E element) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public void add(int index, E element) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public E remove(int index) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public int indexOf(Object o) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public int lastIndexOf(Object o) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public ListIterator listIterator() { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public ListIterator listIterator(int index) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++ @Override ++ public List subList(int fromIndex, int toIndex) { ++ throw new NotImplementedException("INVALID"); ++ } ++ ++} ++ ++/** ++ * Linked Nodes. As a minor efficiency hack, this class opportunistically ++ * inherits from AtomicReference, with the atomic ref used as the "next" link. ++ * ++ * Nodes are in doubly-linked lists. There are three kinds of special nodes, ++ * distinguished by: * The list header has a null prev link * The list trailer ++ * has a null next link * A deletion marker has a prev link pointing to itself. ++ * All three kinds of special nodes have null element fields. ++ * ++ * Regular nodes have non-null element, next, and prev fields. To avoid visible ++ * inconsistencies when deletions overlap element replacement, replacements are ++ * done by replacing the node, not just setting the element. ++ * ++ * Nodes can be traversed by read-only ConcurrentLinkedDeque class operations ++ * just by following raw next pointers, so long as they ignore any special nodes ++ * seen along the way. (This is automated in method forward.) However, traversal ++ * using prev pointers is not guaranteed to see all live nodes since a prev ++ * pointer of a deleted node can become unrecoverably stale. ++ */ ++ ++class Node extends AtomicReference> { ++ ++ private static final long serialVersionUID = 6640557564507962862L; ++ ++ private volatile Node prev; ++ ++ final E element; ++ ++ /** Creates a node with given contents */ ++ Node(E element, Node next, Node prev) { ++ super(next); ++ this.prev = prev; ++ this.element = element; ++ } ++ ++ /** Creates a marker node with given successor */ ++ Node(Node next) { ++ super(next); ++ this.prev = this; ++ this.element = null; ++ } ++ ++ /** ++ * Gets next link (which is actually the value held as atomic reference). ++ */ ++ private Node getNext() { ++ return get(); ++ } ++ ++ /** ++ * Sets next link ++ * ++ * @param n the next node ++ */ ++ void setNext(Node n) { ++ set(n); ++ } ++ ++ /** ++ * compareAndSet next link ++ */ ++ private boolean casNext(Node cmp, Node val) { ++ return compareAndSet(cmp, val); ++ } ++ ++ /** ++ * Gets prev link ++ */ ++ private Node getPrev() { ++ return prev; ++ } ++ ++ /** ++ * Sets prev link ++ * ++ * @param b the previous node ++ */ ++ void setPrev(Node b) { ++ prev = b; ++ } ++ ++ /** ++ * Returns true if this is a header, trailer, or marker node ++ */ ++ boolean isSpecial() { ++ return element == null; ++ } ++ ++ /** ++ * Returns true if this is a trailer node ++ */ ++ boolean isTrailer() { ++ return getNext() == null; ++ } ++ ++ /** ++ * Returns true if this is a header node ++ */ ++ boolean isHeader() { ++ return getPrev() == null; ++ } ++ ++ /** ++ * Returns true if this is a marker node ++ */ ++ boolean isMarker() { ++ return getPrev() == this; ++ } ++ ++ /** ++ * Returns true if this node is followed by a marker, meaning that it is ++ * deleted. ++ * ++ * @return true if this node is deleted ++ */ ++ boolean isDeleted() { ++ Node f = getNext(); ++ return f != null && f.isMarker(); ++ } ++ ++ /** ++ * Returns next node, ignoring deletion marker ++ */ ++ private Node nextNonmarker() { ++ Node f = getNext(); ++ return (f == null || !f.isMarker()) ? f : f.getNext(); ++ } ++ ++ /** ++ * Returns the next non-deleted node, swinging next pointer around any ++ * encountered deleted nodes, and also patching up successor''s prev link to ++ * point back to this. Returns null if this node is trailer so has no successor. ++ * ++ * @return successor, or null if no such ++ */ ++ Node successor() { ++ Node f = nextNonmarker(); ++ for (;;) { ++ if (f == null) ++ return null; ++ if (!f.isDeleted()) { ++ if (f.getPrev() != this && !isDeleted()) ++ f.setPrev(this); // relink f's prev ++ return f; ++ } ++ Node s = f.nextNonmarker(); ++ if (f == getNext()) ++ casNext(f, s); // unlink f ++ f = s; ++ } ++ } ++ ++ /** ++ * Returns the apparent predecessor of target by searching forward for it ++ * starting at this node, patching up pointers while traversing. Used by ++ * predecessor(). ++ * ++ * @return target's predecessor, or null if not found ++ */ ++ private Node findPredecessorOf(Node target) { ++ Node n = this; ++ for (;;) { ++ Node f = n.successor(); ++ if (f == target) ++ return n; ++ if (f == null) ++ return null; ++ n = f; ++ } ++ } ++ ++ /** ++ * Returns the previous non-deleted node, patching up pointers as needed. ++ * Returns null if this node is header so has no successor. May also return null ++ * if this node is deleted, so doesn't have a distinct predecessor. ++ * ++ * @return predecessor or null if not found ++ */ ++ Node predecessor() { ++ Node n = this; ++ for (;;) { ++ Node b = n.getPrev(); ++ if (b == null) ++ return n.findPredecessorOf(this); ++ Node s = b.getNext(); ++ if (s == this) ++ return b; ++ if (s == null || !s.isMarker()) { ++ Node p = b.findPredecessorOf(this); ++ if (p != null) ++ return p; ++ } ++ n = b; ++ } ++ } ++ ++ /** ++ * Returns the next node containing a nondeleted user element. Use for forward ++ * list traversal. ++ * ++ * @return successor, or null if no such ++ */ ++ Node forward() { ++ Node f = successor(); ++ return (f == null || f.isSpecial()) ? null : f; ++ } ++ ++ /** ++ * Returns previous node containing a nondeleted user element, if possible. Use ++ * for backward list traversal, but beware that if this method is called from a ++ * deleted node, it might not be able to determine a usable predecessor. ++ * ++ * @return predecessor, or null if no such could be found ++ */ ++ Node back() { ++ Node f = predecessor(); ++ return (f == null || f.isSpecial()) ? null : f; ++ } ++ ++ /** ++ * Tries to insert a node holding element as successor, failing if this node is ++ * deleted. ++ * ++ * @param element the element ++ * @return the new node, or null on failure. ++ */ ++ Node append(E element) { ++ for (;;) { ++ Node f = getNext(); ++ if (f == null || f.isMarker()) ++ return null; ++ Node x = new Node(element, f, this); ++ if (casNext(f, x)) { ++ f.setPrev(x); // optimistically link ++ return x; ++ } ++ } ++ } ++ ++ /** ++ * Tries to insert a node holding element as predecessor, failing if no live ++ * predecessor can be found to link to. ++ * ++ * @param element the element ++ * @return the new node, or null on failure. ++ */ ++ Node prepend(E element) { ++ for (;;) { ++ Node b = predecessor(); ++ if (b == null) ++ return null; ++ Node x = new Node(element, this, b); ++ if (b.casNext(this, x)) { ++ setPrev(x); // optimistically link ++ return x; ++ } ++ } ++ } ++ ++ /** ++ * Tries to mark this node as deleted, failing if already deleted or if this ++ * node is header or trailer ++ * ++ * @return true if successful ++ */ ++ boolean delete() { ++ Node b = getPrev(); ++ Node f = getNext(); ++ if (b != null && f != null && !f.isMarker() && casNext(f, new Node(f))) { ++ if (b.casNext(this, f)) ++ f.setPrev(b); ++ return true; ++ } ++ return false; ++ } ++ ++ /** ++ * Tries to insert a node holding element to replace this node. failing if ++ * already deleted. ++ * ++ * @param newElement the new element ++ * @return the new node, or null on failure. ++ */ ++ Node replace(E newElement) { ++ for (;;) { ++ Node b = getPrev(); ++ Node f = getNext(); ++ if (b == null || f == null || f.isMarker()) ++ return null; ++ Node x = new Node(newElement, f, b); ++ if (casNext(f, new Node(x))) { ++ b.successor(); // to relink b ++ x.successor(); // to relink f ++ return x; ++ } ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java +index 80215972538d5cfce5f224253ea0e34ea4fd45a4..71e44f4aa64b29610f424ea91a9b54b56a155736 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java +@@ -9,6 +9,8 @@ import java.util.function.BiPredicate; + import java.util.function.Function; + import java.util.stream.Collectors; + import javax.annotation.Nullable; ++ ++import net.himeki.mcmtfabric.parallelised.ConcurrentDoublyLinkedList; + import net.minecraft.core.BlockPos; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.sounds.SoundEvent; +@@ -103,7 +105,7 @@ public class LongJumpToRandomPos extends Behavior { + return !blockPos2.equals(blockPos); + }).map((blockPos2) -> { + return new LongJumpToRandomPos.PossibleJump(blockPos2.immutable(), Mth.ceil(blockPos.distSqr(blockPos2))); +- }).collect(Collectors.toCollection(Lists::newArrayList)); ++ }).collect(Collectors.toCollection(Lists::newCopyOnWriteArrayList)); + } + + @Override +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +index 2e922bb844bc147224a60ef2aae33a0125e6ca4a..a78f04ae5eb2d0f11579cb0a40384f3f103371af 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -88,7 +88,7 @@ public class LevelChunk extends ChunkAccess { + private Supplier fullStatus; + @Nullable + private LevelChunk.PostLoadProcessor postLoad; +- private final Int2ObjectMap gameEventListenerRegistrySections; ++ private final Map gameEventListenerRegistrySections; + private final LevelChunkTicks blockTicks; + private final LevelChunkTicks fluidTicks; + +@@ -114,10 +114,10 @@ public class LevelChunk extends ChunkAccess { + this.setBlockNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world)); + this.setSkyNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world)); + // Paper end - rewrite light engine +- this.tickersInLevel = Maps.newHashMap(); ++ this.tickersInLevel = Maps.newConcurrentMap(); + this.clientLightReady = false; + this.level = (ServerLevel) world; // CraftBukkit - type +- this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap(); ++ this.gameEventListenerRegistrySections = Maps.newConcurrentMap(); + Heightmap.Types[] aheightmap_type = Heightmap.Types.values(); + int j = aheightmap_type.length; + diff --git a/patches/server/0055-Hearse-Add-mcmt-s-collections-and-fix-some-concurren.patch b/patches/server/0055-Hearse-Add-mcmt-s-collections-and-fix-some-concurren.patch new file mode 100644 index 00000000..caaa6a48 --- /dev/null +++ b/patches/server/0055-Hearse-Add-mcmt-s-collections-and-fix-some-concurren.patch @@ -0,0 +1,671 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 21:08:52 +0800 +Subject: [PATCH] Hearse: Add mcmt's collections and fix some concurrent + problem + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/com/destroystokyo/paper/util/maplist/IBlockDataList.java b/src/main/java/com/destroystokyo/paper/util/maplist/IBlockDataList.java +index 277cfd9d1e8fff5d9b5e534b75c3c5162d58b0b7..07247f11b079bfb631010ff06fe353d3dcc0a0f6 100644 +--- a/src/main/java/com/destroystokyo/paper/util/maplist/IBlockDataList.java ++++ b/src/main/java/com/destroystokyo/paper/util/maplist/IBlockDataList.java +@@ -53,7 +53,7 @@ public final class IBlockDataList { + return this.add(getLocationKey(x, y, z), data); + } + +- public long add(final int location, final BlockState data) { ++ public synchronized long add(final int location, final BlockState data) { + final long curr = this.map.get((short)location); + + if (curr == Long.MAX_VALUE) { +@@ -81,7 +81,7 @@ public final class IBlockDataList { + return this.remove(getLocationKey(x, y, z)); + } + +- public long remove(final int location) { ++ public synchronized long remove(final int location) { + final long ret = this.map.remove((short)location); + final int index = getIndexFromRaw(ret); + if (ret == Long.MAX_VALUE) { +@@ -101,11 +101,11 @@ public final class IBlockDataList { + return ret; + } + +- public int size() { ++ public synchronized int size() { + return this.size; + } + +- public long getRaw(final int index) { ++ public synchronized long getRaw(final int index) { + return this.byIndex[index]; + } + +@@ -117,12 +117,12 @@ public final class IBlockDataList { + return getBlockDataFromRaw(this.getRaw(index)); + } + +- public void clear() { ++ public synchronized void clear() { + this.size = 0; + this.map.clear(); + } + +- public LongIterator getRawIterator() { ++ public synchronized LongIterator getRawIterator() { + return this.map.values().iterator(); + } + } +diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +index 62a74cbdb7f04b652dddac9e9c6191d5b86c3323..028b23f5c23bbfd83498c3e06a56079ceb0798ad 100644 +--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java ++++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +@@ -7,9 +7,9 @@ import io.papermc.paper.util.CoordinateUtils; + import io.papermc.paper.util.IntervalledCounter; + import io.papermc.paper.util.TickThread; + import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +-import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap; +-import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.longs.LongSets; ++import it.unimi.dsi.fastutil.objects.*; + import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; + import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket; + import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket; +@@ -24,9 +24,9 @@ import org.bukkit.craftbukkit.entity.CraftPlayer; + import org.bukkit.entity.Player; + + import java.util.*; ++import java.util.concurrent.ConcurrentLinkedDeque; + import java.util.concurrent.ConcurrentSkipListSet; + import java.util.concurrent.atomic.AtomicInteger; +-import java.util.concurrent.locks.StampedLock; + + public final class PlayerChunkLoader { + +@@ -37,11 +37,11 @@ public final class PlayerChunkLoader { + public static final int LOADED_TICKET_LEVEL = 33; + + public static int getTickViewDistance(final Player player) { +- return getTickViewDistance(((CraftPlayer) player).getHandle()); ++ return getTickViewDistance(((CraftPlayer)player).getHandle()); + } + + public static int getTickViewDistance(final ServerPlayer player) { +- final ServerLevel level = (ServerLevel) player.level; ++ final ServerLevel level = (ServerLevel)player.level; + final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player); + if (data == null) { + return level.chunkSource.chunkMap.playerChunkManager.getTargetTickViewDistance(); +@@ -50,11 +50,11 @@ public final class PlayerChunkLoader { + } + + public static int getLoadViewDistance(final Player player) { +- return getLoadViewDistance(((CraftPlayer) player).getHandle()); ++ return getLoadViewDistance(((CraftPlayer)player).getHandle()); + } + + public static int getLoadViewDistance(final ServerPlayer player) { +- final ServerLevel level = (ServerLevel) player.level; ++ final ServerLevel level = (ServerLevel)player.level; + final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player); + if (data == null) { + return level.chunkSource.chunkMap.playerChunkManager.getLoadDistance(); +@@ -63,11 +63,11 @@ public final class PlayerChunkLoader { + } + + public static int getSendViewDistance(final Player player) { +- return getSendViewDistance(((CraftPlayer) player).getHandle()); ++ return getSendViewDistance(((CraftPlayer)player).getHandle()); + } + + public static int getSendViewDistance(final ServerPlayer player) { +- final ServerLevel level = (ServerLevel) player.level; ++ final ServerLevel level = (ServerLevel)player.level; + final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player); + if (data == null) { + return level.chunkSource.chunkMap.playerChunkManager.getTargetSendDistance(); +@@ -76,10 +76,10 @@ public final class PlayerChunkLoader { + } + + protected final ChunkMap chunkMap; +- protected final Reference2ObjectLinkedOpenHashMap playerMap = new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f); +- protected final ReferenceLinkedOpenHashSet chunkSendQueue = new ReferenceLinkedOpenHashSet<>(512, 0.7f); ++ protected final Reference2ObjectMap playerMap = Reference2ObjectMaps.synchronize(new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f)); ++ protected final Deque chunkSendQueue = new ConcurrentLinkedDeque<>(); + +- protected final TreeSet chunkLoadQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> { ++ protected final NavigableSet chunkLoadQueue = new ConcurrentSkipListSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> { + if (p1 == p2) { + return 0; + } +@@ -308,8 +308,8 @@ public final class PlayerChunkLoader { + }); + } + +- protected final LongOpenHashSet isTargetedForPlayerLoad = new LongOpenHashSet(); +- protected final LongOpenHashSet chunkTicketTracker = new LongOpenHashSet(); ++ protected final LongSet isTargetedForPlayerLoad = LongSets.synchronize(new LongOpenHashSet()); ++ protected final LongSet chunkTicketTracker = LongSets.synchronize(new LongOpenHashSet()); + + public boolean isChunkNearPlayers(final int chunkX, final int chunkZ) { + final PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInSendRange = this.broadcastMap.getObjectsInRange(chunkX, chunkZ); +@@ -383,15 +383,15 @@ public final class PlayerChunkLoader { + protected int getMaxChunkLoads() { + double config = GlobalConfiguration.get().chunkLoading.playerMaxConcurrentLoads; + double max = GlobalConfiguration.get().chunkLoading.globalMaxConcurrentLoads; +- return (int) Math.ceil(Math.min(config * MinecraftServer.getServer().getPlayerCount(), max <= 1.0 ? Double.MAX_VALUE : max)); ++ return (int)Math.ceil(Math.min(config * MinecraftServer.getServer().getPlayerCount(), max <= 1.0 ? Double.MAX_VALUE : max)); + } + + protected long getTargetSendPerPlayerAddend() { +- return GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate <= 1.0 ? 0L : (long) Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate); ++ return GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate); + } + + protected long getMaxSendAddend() { +- return GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate <= 1.0 ? 0L : (long) Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate); ++ return GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate); + } + + public void onChunkPlayerTickReady(final int chunkX, final int chunkZ) { +@@ -413,7 +413,7 @@ public final class PlayerChunkLoader { + if (!(raw instanceof ServerPlayer)) { + continue; + } +- this.onChunkSendReady((ServerPlayer) raw, chunkX, chunkZ); ++ this.onChunkSendReady((ServerPlayer)raw, chunkX, chunkZ); + } + } + +@@ -481,11 +481,8 @@ public final class PlayerChunkLoader { + return; + } + loaderData.remove(); +- + this.chunkLoadQueue.remove(loaderData); +- + this.chunkSendQueue.remove(loaderData); +- + this.chunkSendWaitQueue.remove(loaderData); + synchronized (this.sendingChunkCounts) { + final int count = this.sendingChunkCounts.removeInt(loaderData); +@@ -521,23 +518,21 @@ public final class PlayerChunkLoader { + protected static final AtomicInteger concurrentChunkSends = new AtomicInteger(); + protected final Reference2IntOpenHashMap sendingChunkCounts = new Reference2IntOpenHashMap<>(); + private static long nextChunkSend; +- + private void trySendChunks() { + final long time = System.nanoTime(); + if (time < nextChunkSend) { + return; + } ++ 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()) { +@@ -546,11 +541,9 @@ public final class PlayerChunkLoader { + + final int maxSends = this.getMaxConcurrentChunkSends(); + final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time; +- for (; ; ) { +- if (this.chunkSendQueue.isEmpty()) { +- break; +- } +- ++ final Deque tempCopy = new ArrayDeque<>(this.chunkSendQueue); ++ PlayerLoaderData data; ++ while ((data = tempCopy.pollFirst())!=null) { + final int currSends = concurrentChunkSends.get(); + if (currSends >= maxSends) { + break; +@@ -559,19 +552,12 @@ public final class PlayerChunkLoader { + if (!concurrentChunkSends.compareAndSet(currSends, currSends + 1)) { + continue; + } +- + // send chunk +- +- 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; + } + +@@ -580,22 +566,24 @@ public final class PlayerChunkLoader { + } + + data.nextChunkSendTarget = nextPlayerDeadline; ++ this.chunkSendWaitQueue.add(data); + + synchronized (this.sendingChunkCounts) { + 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); + } + } + +@@ -611,10 +599,9 @@ public final class PlayerChunkLoader { + + protected int concurrentChunkLoads; + // this interval prevents bursting a lot of chunk loads +- protected static final IntervalledCounter TICKET_ADDITION_COUNTER_SHORT = new IntervalledCounter((long) (1.0e6 * 50.0)); // 50ms ++ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_SHORT = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms + // this interval ensures the rate is kept between ticks correctly +- protected static final IntervalledCounter TICKET_ADDITION_COUNTER_LONG = new IntervalledCounter((long) (1.0e6 * 1000.0)); // 1000ms +- ++ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_LONG = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms + private void tryLoadChunks() { + if (this.chunkLoadQueue.isEmpty()) { + return; +@@ -623,16 +610,12 @@ public final class PlayerChunkLoader { + final int maxLoads = this.getMaxChunkLoads(); + final long time = System.nanoTime(); + boolean updatedCounters = false; +- for (; ; ) { +- 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; + } + +@@ -648,6 +631,7 @@ public final class PlayerChunkLoader { + // already loaded! + data.loadQueue.pollFirst(); // already loaded so we just skip + this.chunkLoadQueue.add(data); ++ + // ensure the chunk is queued to send + this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ); + continue; +@@ -768,7 +752,7 @@ public final class PlayerChunkLoader { + protected static final double PRIORITISED_DISTANCE = 12.0 * 16.0; + + // Player max sprint speed is approximately 8m/s +- protected static final double LOOK_PRIORITY_SPEED_THRESHOLD = (10.0 / 20.0) * (10.0 / 20.0); ++ protected static final double LOOK_PRIORITY_SPEED_THRESHOLD = (10.0/20.0) * (10.0/20.0); + protected static final double LOOK_PRIORITY_YAW_DELTA_RECALC_THRESHOLD = 3.0f; + + protected double lastLocX = Double.NEGATIVE_INFINITY; +@@ -790,11 +774,11 @@ public final class PlayerChunkLoader { + + // warning: modifications of this field must be aware that the loadQueue inside PlayerChunkLoader uses this field + // in a comparator! +- protected final ArrayDeque loadQueue = new ArrayDeque<>(); +- protected final LongOpenHashSet sentChunks = new LongOpenHashSet(); +- protected final LongOpenHashSet chunksToBeSent = new LongOpenHashSet(); ++ protected final Deque loadQueue = new ConcurrentLinkedDeque<>(); ++ protected final LongSet sentChunks = LongSets.synchronize(new LongOpenHashSet()); ++ protected final LongSet chunksToBeSent = LongSets.synchronize(new LongOpenHashSet()); + +- protected final TreeSet sendQueue = new TreeSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> { ++ protected final NavigableSet sendQueue = new ConcurrentSkipListSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> { + final int distanceCompare = Integer.compare(p1.manhattanDistanceToPlayer, p2.manhattanDistanceToPlayer); + if (distanceCompare != 0) { + return distanceCompare; +@@ -815,9 +799,9 @@ public final class PlayerChunkLoader { + protected long nextChunkSendTarget; + + // this interval prevents bursting a lot of chunk loads +- protected final IntervalledCounter ticketAdditionCounterShort = new IntervalledCounter((long) (1.0e6 * 50.0)); // 50ms ++ protected final IntervalledCounter ticketAdditionCounterShort = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms + // this ensures the rate is kept between ticks correctly +- protected final IntervalledCounter ticketAdditionCounterLong = new IntervalledCounter((long) (1.0e6 * 1000.0)); // 1000ms ++ protected final IntervalledCounter ticketAdditionCounterLong = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms + + public long lastChunkLoad; + +@@ -914,14 +898,14 @@ public final class PlayerChunkLoader { + // b = ((p3z - p1z)(targetX - p3x) + (p1x - p3x)(targetZ - p3z)) / d + // c = 1.0 - a - b + +- final double d = (p2z - p3z) * (p1x - p3x) + (p3x - p2x) * (p1z - p3z); +- final double a = ((p2z - p3z) * (targetX - p3x) + (p3x - p2x) * (targetZ - p3z)) / d; ++ final double d = (p2z - p3z)*(p1x - p3x) + (p3x - p2x)*(p1z - p3z); ++ final double a = ((p2z - p3z)*(targetX - p3x) + (p3x - p2x)*(targetZ - p3z)) / d; + + if (a < 0.0 || a > 1.0) { + return false; + } + +- final double b = ((p3z - p1z) * (targetX - p3x) + (p1x - p3x) * (targetZ - p3z)) / d; ++ final double b = ((p3z - p1z)*(targetX - p3x) + (p1x - p3x)*(targetZ - p3z)) / d; + if (b < 0.0 || b > 1.0) { + return false; + } +@@ -1024,15 +1008,15 @@ public final class PlayerChunkLoader { + final double p1z = posZ; + + // to the left of the looking direction +- final double p2x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw + (double) (FOV / 2.0))) // calculate rotated vector ++ final double p2x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector + + p1x; // offset vector +- final double p2z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw + (double) (FOV / 2.0))) // calculate rotated vector ++ final double p2z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector + + p1z; // offset vector + + // to the right of the looking direction +- final double p3x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw - (double) (FOV / 2.0))) // calculate rotated vector ++ final double p3x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector + + p1x; // offset vector +- final double p3z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw - (double) (FOV / 2.0))) // calculate rotated vector ++ final double p3z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector + + p1z; // offset vector + + // now that we have all of our points, we can recalculate the load queue +@@ -1070,7 +1054,7 @@ public final class PlayerChunkLoader { + p1x, p1z, p2x, p2z, p3x, p3z, + + // center of chunk +- (double) ((chunkX << 4) | 8), (double) ((chunkZ << 4) | 8) ++ (double)((chunkX << 4) | 8), (double)((chunkZ << 4) | 8) + ); + + final int manhattanDistance = Math.abs(dx) + Math.abs(dz); +@@ -1085,9 +1069,9 @@ public final class PlayerChunkLoader { + if (prioritised) { + // we don't prioritise these chunks above others because we also want to make sure some chunks + // will be loaded if the player changes direction +- priority = (double) manhattanDistance / 6.0; ++ priority = (double)manhattanDistance / 6.0; + } else { +- priority = (double) manhattanDistance; ++ priority = (double)manhattanDistance; + } + } + +diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java +index 470402573bc31106d5a63e415b958fb7f9c36aa9..762f09c8f374fbccc9f5be985401ad334e1655a0 100644 +--- a/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java ++++ b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java +@@ -94,24 +94,24 @@ public final class Delayed26WayDistancePropagator3D { + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); + + this.levelIncreaseWorkQueueBitset |= (1L << level); + } + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[index]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); + + this.levelIncreaseWorkQueueBitset |= (1L << index); + } + + protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); + + this.levelRemoveWorkQueueBitset |= (1L << level); + } +@@ -164,8 +164,8 @@ public final class Delayed26WayDistancePropagator3D { + + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); ++ byte level = queue.queuedLevels.removeFirst(); + + final boolean neighbourCheck = level < 0; + +@@ -233,8 +233,8 @@ public final class Delayed26WayDistancePropagator3D { + + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- final byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); ++ final byte level = queue.queuedLevels.removeFirst(); + + final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); + if (currentLevel == 0) { +diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java +index 808d1449ac44ae86a650932365081fbaf178d141..8c5a51b5992eccf3627f326e164288b5f6bbcff6 100644 +--- a/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java ++++ b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java +@@ -1,12 +1,13 @@ + package io.papermc.paper.util.misc; + ++import io.papermc.paper.util.MCUtil; + import it.unimi.dsi.fastutil.HashCommon; +-import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue; + import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; +-import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; + import it.unimi.dsi.fastutil.longs.LongIterator; + import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; +-import io.papermc.paper.util.MCUtil; ++ ++import java.util.Deque; ++import java.util.concurrent.ConcurrentLinkedDeque; + + public final class Delayed8WayDistancePropagator2D { + +@@ -356,24 +357,24 @@ public final class Delayed8WayDistancePropagator2D { + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { + final WorkQueue queue = this.levelIncreaseWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); + + this.levelIncreaseWorkQueueBitset |= (1L << level); + } + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) { + final WorkQueue queue = this.levelIncreaseWorkQueues[index]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); + + this.levelIncreaseWorkQueueBitset |= (1L << index); + } + + protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { + final WorkQueue queue = this.levelRemoveWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); + + this.levelRemoveWorkQueueBitset |= (1L << level); + } +@@ -426,8 +427,8 @@ public final class Delayed8WayDistancePropagator2D { + + final WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); ++ byte level = queue.queuedLevels.removeFirst(); + + final boolean neighbourCheck = level < 0; + +@@ -492,8 +493,8 @@ public final class Delayed8WayDistancePropagator2D { + + final WorkQueue queue = this.levelRemoveWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- final byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); ++ final byte level = queue.queuedLevels.removeFirst(); + + final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); + if (currentLevel == 0) { +@@ -678,41 +679,8 @@ public final class Delayed8WayDistancePropagator2D { + } + + protected static final class WorkQueue { +- +- public final NoResizeLongArrayFIFODeque queuedCoordinates = new NoResizeLongArrayFIFODeque(); +- public final NoResizeByteArrayFIFODeque queuedLevels = new NoResizeByteArrayFIFODeque(); +- +- } +- +- protected static final class NoResizeLongArrayFIFODeque extends LongArrayFIFOQueue { +- +- /** +- * Assumes non-empty. If empty, undefined behaviour. +- */ +- public long removeFirstLong() { +- // copied from superclass +- long t = this.array[this.start]; +- if (++this.start == this.length) { +- this.start = 0; +- } +- +- return t; +- } ++ public final Deque queuedCoordinates = new ConcurrentLinkedDeque<>(); ++ public final Deque queuedLevels = new ConcurrentLinkedDeque<>(); + } + +- protected static final class NoResizeByteArrayFIFODeque extends ByteArrayFIFOQueue { +- +- /** +- * Assumes non-empty. If empty, undefined behaviour. +- */ +- public byte removeFirstByte() { +- // copied from superclass +- byte t = this.array[this.start]; +- if (++this.start == this.length) { +- this.start = 0; +- } +- +- return t; +- } +- } + } +diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java +index e642b4a83687d03e55feb340452d608c53ae7cce..4beaa69da4001fc2723e9628d64bd3de728d7213 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java ++++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +@@ -5,6 +5,7 @@ import com.mojang.datafixers.util.Pair; + import it.unimi.dsi.fastutil.shorts.ShortArraySet; + import it.unimi.dsi.fastutil.shorts.ShortSet; + import it.unimi.dsi.fastutil.shorts.ShortSets; ++import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentShortHashSet; + import net.minecraft.core.BlockPos; + import net.minecraft.core.SectionPos; + import net.minecraft.network.protocol.Packet; +@@ -222,7 +223,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] = ShortSets.synchronize(new ShortArraySet()); ++ this.changedBlocksPerSection[i] = new ConcurrentShortHashSet(); + } + + this.changedBlocksPerSection[i].add(SectionPos.sectionRelativePos(pos)); +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 81697ea6d00967852556c3bb741317db030c24db..85c03dc7c1e714fab281374a177cd4c54e97d939 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -788,7 +788,7 @@ public class ServerChunkCache extends ChunkSource { + //gameprofilerfiller.popPush("broadcast"); // Purpur + //this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur + if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { +- ObjectSet copy = new ObjectArraySet<>(this.chunkMap.needsChangeBroadcasting); ++ List copy = new ArrayList<>(this.chunkMap.needsChangeBroadcasting); + this.chunkMap.needsChangeBroadcasting.clear(); + for (ChunkHolder holder : copy) { + holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded +diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java +index 9f138bc471b5c2a4fa813ff943dbe34018b8df74..5c8a90f8536c9291df5891d8c75de963b75ec4bd 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java ++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java +@@ -7,6 +7,7 @@ import com.mojang.logging.LogUtils; + import com.mojang.serialization.Codec; + import com.mojang.serialization.codecs.RecordCodecBuilder; + import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; ++import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps; + import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; + import java.util.List; + import java.util.Map; +@@ -25,8 +26,9 @@ import org.slf4j.Logger; + + public class PoiSection { + private static final Logger LOGGER = LogUtils.getLogger(); +- private final Short2ObjectMap records = new Short2ObjectOpenHashMap<>(); +- private final Map, Set> byType = Maps.newHashMap(); public final Map, Set> getData() { return this.byType; } // Paper - public accessor ++ private final Short2ObjectMap records = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>()); ++ private final Map, Set> byType = Maps.newConcurrentMap(); ++ public final Map, Set> getData() { return this.byType; } // Paper - public accessor + private final Runnable setDirty; + private boolean isValid; + public final Optional noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system +diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java +index 8450a22b0fc6e8dc5cad0f61ac52a82b3cd3791e..c55f98fb2a6aade8e0a6f60248f4e29f66f0c193 100644 +--- a/src/main/java/net/minecraft/world/item/ItemStack.java ++++ b/src/main/java/net/minecraft/world/item/ItemStack.java +@@ -248,7 +248,9 @@ public final class ItemStack { + } + + private void updateEmptyCacheFlag() { +- if (this.emptyCacheFlag && this == ItemStack.EMPTY) throw new AssertionError("TRAP"); // CraftBukkit ++ if (this.emptyCacheFlag && this == ItemStack.EMPTY){ ++ return; ++ }//throw new AssertionError("TRAP"); // CraftBukkit + this.emptyCacheFlag = false; + this.emptyCacheFlag = this.isEmpty(); + } diff --git a/patches/server/0056-Hearse-I-am-an-idiot.patch b/patches/server/0056-Hearse-I-am-an-idiot.patch new file mode 100644 index 00000000..0815e853 --- /dev/null +++ b/patches/server/0056-Hearse-I-am-an-idiot.patch @@ -0,0 +1,3607 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Mon, 9 Jan 2023 21:11:42 +0800 +Subject: [PATCH] Hearse: I am an idiot + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3c29129dc02ddcfaad026d1f81e5da879a0d64cb +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentArrayDeque.java +@@ -0,0 +1,4 @@ ++package net.himeki.mcmtfabric.parallelised; ++ ++public class ConcurrentArrayDeque { ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java +new file mode 100644 +index 0000000000000000000000000000000000000000..67dd5fe624fe4428d8907000cb23a33485fd6bd9 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentCollections.java +@@ -0,0 +1,41 @@ ++package net.himeki.mcmtfabric.parallelised; ++ ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++ ++import java.util.*; ++import java.util.concurrent.ConcurrentHashMap; ++import java.util.concurrent.ConcurrentLinkedDeque; ++import java.util.concurrent.CopyOnWriteArrayList; ++import java.util.stream.Collector; ++import java.util.stream.Collectors; ++ ++public class ConcurrentCollections { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ public static Set newHashSet() { ++ //LOGGER.info("Concurrent hash set created"); ++ return Collections.newSetFromMap(new ConcurrentHashMap()); ++ } ++ ++ public static Map newHashMap() { ++ //LOGGER.info("Concurrent hash map created"); ++ return new ConcurrentHashMap(); ++ } ++ ++ public static List newLinkedList() { ++ LOGGER.info("Concurrent \"linked\" list created"); ++ return new CopyOnWriteArrayList(); ++ } ++ ++ public static Collector> toList() { ++ return Collectors.toCollection(CopyOnWriteArrayList::new); ++ } ++ ++ public static Queue newArrayDeque() { ++ LOGGER.info("Concurrent \"array\" deque created"); ++ return new ConcurrentLinkedDeque(); ++ } ++ ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java +index 22b9d217dc06caaf8fbec21f0e31aa1cd13144ee..fec1f280c72c5b519173017877812ec3f7149ec5 100644 +--- a/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/ConcurrentDoublyLinkedList.java +@@ -10,19 +10,11 @@ package net.himeki.mcmtfabric.parallelised; + * Modified to actually implement List + */ + +-import java.util.AbstractCollection; +-import java.util.ArrayList; +-import java.util.Collection; +-import java.util.ConcurrentModificationException; +-import java.util.Deque; +-import java.util.Iterator; +-import java.util.List; +-import java.util.ListIterator; +-import java.util.NoSuchElementException; +-import java.util.concurrent.atomic.AtomicReference; +- + import org.apache.commons.lang3.NotImplementedException; + ++import java.util.*; ++import java.util.concurrent.atomic.AtomicReference; ++ + /** + * A concurrent linked-list implementation of a {@link Deque} (double-ended + * queue). Concurrent insertion, removal, and access operations execute safely +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..586b3c87dcf010e2401933c5ec2338f59b9edad3 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongLinkedOpenHashSet.java +@@ -0,0 +1,230 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.longs.*; ++ ++import java.util.Collection; ++import java.util.Iterator; ++import java.util.concurrent.ConcurrentSkipListSet; ++ ++public class ConcurrentLongLinkedOpenHashSet extends LongLinkedOpenHashSet { ++ ++ private static final long serialVersionUID = -5532128240738069111L; ++ ++ private final ConcurrentSkipListSet backing; ++ ++ public ConcurrentLongLinkedOpenHashSet() { ++ //backing = new ConcurrentLinkedDeque(); ++ backing = new ConcurrentSkipListSet(); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final int initial) { ++ //backing = new ConcurrentLinkedDeque(); ++ backing = new ConcurrentSkipListSet(); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final int initial, final float dnc) { ++ this(initial); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final LongCollection c) { ++ this(c.size()); ++ addAll(c); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final LongCollection c, final float f) { ++ this(c.size(), f); ++ addAll(c); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final LongIterator i, final float f) { ++ this(16, f); ++ while (i.hasNext()) ++ add(i.nextLong()); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final LongIterator i) { ++ this(i, -1); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final Iterator i, final float f) { ++ this(LongIterators.asLongIterator(i), f); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final Iterator i) { ++ this(LongIterators.asLongIterator(i)); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length, final float f) { ++ this(length < 0 ? 0 : length, f); ++ LongArrays.ensureOffsetLength(a, offset, length); ++ for (int i = 0; i < length; i++) ++ add(a[offset + i]); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final long[] a, final int offset, final int length) { ++ this(a, offset, length, DEFAULT_LOAD_FACTOR); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final long[] a, final float f) { ++ this(a, 0, a.length, f); ++ } ++ ++ public ConcurrentLongLinkedOpenHashSet(final long[] a) { ++ this(a, -1); ++ } ++ ++ @Override ++ public boolean add(final long k) { ++ boolean out = backing.add(k); ++ /* ++ if (!firstDef) { ++ first = k; ++ firstDef = true; ++ } ++ last = k; ++ */ ++ return out; ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return addAll((Collection) c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean addAndMoveToFirst(final long k) { ++ boolean out = backing.add(k); ++ //first = k; ++ return out; ++ } ++ ++ @Override ++ public boolean addAndMoveToLast(final long k) { ++ boolean out = backing.add(k); ++ //last = k; ++ return out; ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public LongLinkedOpenHashSet clone() { ++ return new ConcurrentLongLinkedOpenHashSet(backing.iterator()); ++ } ++ ++ @Override ++ public LongComparator comparator() { ++ return null; ++ } ++ ++ @Override ++ public boolean contains(final long k) { ++ return backing.contains(k); ++ } ++ ++ @Override ++ public long firstLong() { ++ /* ++ if (backing.size() == 0) throw new NoSuchElementException(); ++ return first; ++ */ ++ return backing.first(); ++ } ++ ++ @Override ++ public int hashCode() { ++ return backing.hashCode(); ++ } ++ ++ @Override ++ public LongSortedSet headSet(long to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public LongListIterator iterator() { ++ return FastUtilHackUtil.wrap(backing.iterator()); ++ } ++ ++ @Override ++ public LongListIterator iterator(long from) { ++ throw new IllegalStateException(); ++ //return FastUtilHackUtil.wrap(backing.iterator()); ++ } ++ ++ @Override ++ public long lastLong() { ++ /* ++ if (backing.size() == 0) throw new NoSuchElementException(); ++ return last; ++ */ ++ return backing.last(); ++ } ++ ++ @Override ++ public boolean remove(final long k) { ++ /* ++ if (k == first) { ++ first = backing.iterator().next(); ++ } ++ if (k == last) { ++ last = backing.iterator().next(); ++ } ++ */ ++ return backing.remove(k); ++ } ++ ++ @Override ++ public long removeFirstLong() { ++ long fl = this.firstLong(); ++ this.remove(fl); ++ //first = backing.iterator().next(); ++ return fl; ++ } ++ ++ @Override ++ public long removeLastLong() { ++ long fl = this.lastLong(); ++ this.remove(fl); ++ //last = backing.iterator().next(); ++ return fl; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public LongSortedSet subSet(long from, long to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongSortedSet tailSet(long from) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public boolean trim() { ++ return true; ++ } ++ ++ @Override ++ public boolean trim(final int n) { ++ return true; ++ } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..93bd066ec2013e42a85fcf21344fe41f3ad69598 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentLongSortedSet.java +@@ -0,0 +1,144 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.longs.*; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.Collection; ++import java.util.concurrent.ConcurrentSkipListSet; ++ ++public class ConcurrentLongSortedSet implements LongSortedSet { ++ ++ ConcurrentSkipListSet back = new ConcurrentSkipListSet<>(); ++ ++ @Override ++ public LongBidirectionalIterator iterator(long fromElement) { ++ return null; ++ } ++ ++ @Override ++ public int size() { ++ return back.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return back.isEmpty(); ++ } ++ ++ @Override ++ public LongBidirectionalIterator iterator() { ++ return null; ++ } ++ ++ @NotNull ++ @Override ++ public Object[] toArray() { ++ return back.toArray(); ++ } ++ ++ @NotNull ++ @Override ++ public T[] toArray(@NotNull T[] ts) { ++ return null; ++ } ++ ++ @Override ++ public boolean containsAll(@NotNull Collection collection) { ++ return back.containsAll(collection); ++ } ++ ++ @Override ++ public boolean addAll(@NotNull Collection collection) { ++ return back.addAll(collection); ++ } ++ ++ @Override ++ public boolean removeAll(@NotNull Collection collection) { ++ return back.removeAll(collection); ++ } ++ ++ @Override ++ public boolean retainAll(@NotNull Collection collection) { ++ return back.retainAll(collection); ++ } ++ ++ @Override ++ public void clear() { ++ back.clear(); ++ } ++ ++ @Override ++ public boolean add(long key) { ++ return back.add(key); ++ } ++ ++ @Override ++ public boolean contains(long key) { ++ return back.contains(key); ++ } ++ ++ @Override ++ public long[] toLongArray() { ++ return new long[0]; ++ } ++ ++ @Override ++ public long[] toArray(long[] a) { ++ return new long[0]; ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return back.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(LongCollection c) { ++ return back.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(LongCollection c) { ++ return back.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(LongCollection c) { ++ return back.retainAll(c); ++ } ++ ++ @Override ++ public boolean remove(long k) { ++ return back.remove(k); ++ } ++ ++ @Override ++ public LongSortedSet subSet(long fromElement, long toElement) { ++ return new LongAVLTreeSet(back.subSet(fromElement,toElement)); ++ } ++ ++ @Override ++ public LongSortedSet headSet(long toElement) { ++ return new LongAVLTreeSet(back.headSet(toElement)); ++ } ++ ++ @Override ++ public LongSortedSet tailSet(long fromElement) { ++ return new LongAVLTreeSet(back.tailSet(fromElement)); ++ } ++ ++ @Override ++ public LongComparator comparator() { ++ return null; ++ } ++ ++ @Override ++ public long firstLong() { ++ return back.first(); ++ } ++ ++ @Override ++ public long lastLong() { ++ return back.last(); ++ } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ff1a4f87356459d3bc990a77c3081932046da5b1 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/ConcurrentShortHashSet.java +@@ -0,0 +1,112 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.shorts.ShortCollection; ++import it.unimi.dsi.fastutil.shorts.ShortIterator; ++import it.unimi.dsi.fastutil.shorts.ShortSet; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.Collection; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class ConcurrentShortHashSet implements ShortSet { ++ ++ ConcurrentHashMap.KeySetView backing = ConcurrentHashMap.newKeySet(); ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public ShortIterator iterator() { ++ return new FastUtilHackUtil.WrappingShortIterator(backing.iterator()); ++ } ++ ++ @NotNull ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @NotNull ++ @Override ++ public T[] toArray(@NotNull T[] ts) { ++ return (T[]) backing.toArray(); ++ } ++ ++ @Override ++ public boolean containsAll(@NotNull Collection collection) { ++ return backing.containsAll(collection); ++ } ++ ++ @Override ++ public boolean addAll(@NotNull Collection collection) { ++ return backing.addAll(collection); ++ } ++ ++ @Override ++ public boolean removeAll(@NotNull Collection collection) { ++ return backing.removeAll(collection); ++ } ++ ++ @Override ++ public boolean retainAll(@NotNull Collection collection) { ++ return backing.retainAll(collection); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ ++ } ++ ++ @Override ++ public boolean add(short key) { ++ return backing.add(key); ++ } ++ ++ @Override ++ public boolean contains(short key) { ++ return backing.contains(key); ++ } ++ ++ @Override ++ public short[] toShortArray() { ++ return new short[0]; ++ } ++ ++ @Override ++ public short[] toArray(short[] a) { ++ return new short[0]; ++ } ++ ++ @Override ++ public boolean addAll(ShortCollection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(ShortCollection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(ShortCollection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(ShortCollection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public boolean remove(short k) { ++ return backing.remove(k); ++ } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ca1fd9ca6f5c5d5d4a0f374440ccaf80347fb781 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/FastUtilHackUtil.java +@@ -0,0 +1,1678 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.bytes.ByteCollection; ++import it.unimi.dsi.fastutil.bytes.ByteIterator; ++import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; ++import it.unimi.dsi.fastutil.ints.IntCollection; ++import it.unimi.dsi.fastutil.ints.IntIterator; ++import it.unimi.dsi.fastutil.ints.IntSet; ++import it.unimi.dsi.fastutil.longs.*; ++import it.unimi.dsi.fastutil.objects.ObjectCollection; ++import it.unimi.dsi.fastutil.objects.ObjectIterator; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++import it.unimi.dsi.fastutil.shorts.ShortIterator; ++import org.apache.commons.lang3.ArrayUtils; ++ ++import java.util.*; ++import java.util.function.Function; ++import java.util.stream.Collectors; ++ ++public class FastUtilHackUtil { ++ ++ public static class ConvertingObjectSet implements ObjectSet { ++ ++ Set backing; ++ Function forward; ++ Function back; ++ ++ public ConvertingObjectSet(Set backing, Function forward, Function back) { ++ this.backing = backing; ++ this.forward = forward; ++ this.back = back; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean contains(Object o) { ++ try { ++ return backing.contains(back.apply((T) o)); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.stream().map(forward).toArray(); ++ } ++ ++ @Override ++ public R[] toArray(R[] a) { ++ return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); ++ } ++ ++ @Override ++ public boolean add(T e) { ++ return backing.add(back.apply(e)); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean remove(Object o) { ++ try { ++ return backing.remove(back.apply((T) o)); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean containsAll(Collection c) { ++ try { ++ return backing.containsAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c.stream().map(i -> back.apply(i)).collect(Collectors.toSet())); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean removeAll(Collection c) { ++ try { ++ return backing.removeAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean retainAll(Collection c) { ++ try { ++ return backing.retainAll(c.stream().map(i -> back.apply((T) i)).collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ ++ } ++ ++ @Override ++ public ObjectIterator iterator() { ++ final Iterator backg = backing.iterator(); ++ return new ObjectIterator() { ++ ++ @Override ++ public boolean hasNext() { ++ return backg.hasNext(); ++ } ++ ++ @Override ++ public T next() { ++ return forward.apply(backg.next()); ++ } ++ ++ @Override ++ public void remove() { ++ backg.remove(); ++ } ++ }; ++ } ++ ++ ++ } ++ ++ public static class ConvertingObjectSetFast implements Long2ObjectMap.FastEntrySet { ++ ++ Set backing; ++ Function> forward; ++ Function, E> back; ++ ++ public ConvertingObjectSetFast(Set backing, ++ Function> forward, ++ Function, E> back) { ++ this.backing = backing; ++ this.forward = forward; ++ this.back = back; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean contains(Object o) { ++ try { ++ return backing.contains(back.apply((Long2ObjectMap.Entry)o)); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.stream().map(forward).toArray(); ++ } ++ ++ @Override ++ public R[] toArray(R[] a) { ++ return backing.stream().map(forward).collect(Collectors.toSet()).toArray(a); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean remove(Object o) { ++ try { ++ return backing.remove(back.apply((Long2ObjectMap.Entry)o)); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean containsAll(Collection c) { ++ try { ++ return backing.containsAll(c.stream() ++ .map(i -> back.apply((Long2ObjectMap.Entry) i)) ++ .collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean removeAll(Collection c) { ++ try { ++ return backing.removeAll(c.stream().map(i -> back ++ .apply((Long2ObjectMap.Entry) i)) ++ .collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public boolean retainAll(Collection c) { ++ try { ++ return backing.retainAll(c.stream() ++ .map(i -> back.apply((Long2ObjectMap.Entry) i)) ++ .collect(Collectors.toSet())); ++ } catch (ClassCastException cce) { ++ return false; ++ } ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ ++ } ++ ++ @Override ++ public ObjectIterator> iterator() { ++ final Iterator backg = backing.iterator(); ++ return new ObjectIterator>() { ++ ++ @Override ++ public boolean hasNext() { ++ return backg.hasNext(); ++ } ++ ++ @Override ++ public Long2ObjectMap.Entry next() { ++ return forward.apply(backg.next()); ++ } ++ ++ @Override ++ public void remove() { ++ backg.remove(); ++ } ++ }; ++ } ++ ++ @Override ++ public boolean add(Long2ObjectMap.Entry e) { ++ return backing.add(back.apply(e)); ++ } ++ ++ @Override ++ public boolean addAll(Collection> c) { ++ return backing.addAll(c.stream().map(back).collect(Collectors.toList())); ++ } ++ ++ @Override ++ public ObjectIterator> fastIterator() { ++ return iterator(); ++ } ++ ++ ++ } ++ ++ private static Entry intEntryForwards(Map.Entry entry) { ++ return new Entry() { ++ ++ @Override ++ public T getValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public T setValue(T value) { ++ return entry.setValue(value); ++ } ++ ++ @Override ++ public int getIntKey() { ++ return entry.getKey(); ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (obj == entry) { ++ return true; ++ } ++ return super.equals(obj); ++ } ++ ++ @Override ++ public int hashCode() { ++ return entry.hashCode(); ++ } ++ }; ++ } ++ ++ private static Map.Entry intEntryBackwards(Entry entry) { ++ return entry; ++ } ++ ++ private static Long2ObjectMap.Entry longEntryForwards(Map.Entry entry) { ++ return new Long2ObjectMap.Entry() { ++ ++ @Override ++ public T getValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public T setValue(T value) { ++ return entry.setValue(value); ++ } ++ ++ @Override ++ public long getLongKey() { ++ return entry.getKey(); ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (obj == entry) { ++ return true; ++ } ++ return super.equals(obj); ++ } ++ ++ @Override ++ public int hashCode() { ++ return entry.hashCode(); ++ } ++ }; ++ } ++ ++ private static Map.Entry longEntryBackwards(Long2ObjectMap.Entry entry) { ++ return entry; ++ } ++ ++ private static Long2ByteMap.Entry longByteEntryForwards(Map.Entry entry) { ++ return new Long2ByteMap.Entry() { ++ ++ @Override ++ public Byte getValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public byte setValue(byte value) { ++ return entry.setValue(value); ++ } ++ ++ @Override ++ public byte getByteValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public long getLongKey() { ++ return entry.getKey(); ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (obj == entry) { ++ return true; ++ } ++ return super.equals(obj); ++ } ++ ++ @Override ++ public int hashCode() { ++ return entry.hashCode(); ++ } ++ ++ }; ++ } ++ ++ private static Map.Entry longByteEntryBackwards(Long2ByteMap.Entry entry) { ++ return entry; ++ } ++ ++ private static Long2LongMap.Entry longLongEntryForwards(Map.Entry entry) { ++ return new Long2LongMap.Entry() { ++ ++ @Override ++ public Long getValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public long setValue(long value) { ++ return entry.setValue(value); ++ } ++ ++ @Override ++ public long getLongValue() { ++ return entry.getValue(); ++ } ++ ++ @Override ++ public long getLongKey() { ++ return entry.getKey(); ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (obj == entry) { ++ return true; ++ } ++ return super.equals(obj); ++ } ++ ++ @Override ++ public int hashCode() { ++ return entry.hashCode(); ++ } ++ ++ }; ++ } ++ ++ private static Map.Entry longLongEntryBackwards(Long2LongMap.Entry entry) { ++ return entry; ++ } ++ ++ public static ObjectSet> entrySetIntWrap(Map map) { ++ return new ConvertingObjectSet, Entry>(map.entrySet(), FastUtilHackUtil::intEntryForwards, FastUtilHackUtil::intEntryBackwards); ++ } ++ ++ public static ObjectSet> entrySetLongWrap(Map map) { ++ return new ConvertingObjectSet, Long2ObjectMap.Entry>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); ++ } ++ ++ public static Long2ObjectMap.FastEntrySet entrySetLongWrapFast(Map map) { ++ return new ConvertingObjectSetFast, T>(map.entrySet(), FastUtilHackUtil::longEntryForwards, FastUtilHackUtil::longEntryBackwards); ++ } ++ ++ public static ObjectSet entrySetLongByteWrap(Map map) { ++ return new ConvertingObjectSet, Long2ByteMap.Entry>(map.entrySet(), FastUtilHackUtil::longByteEntryForwards, FastUtilHackUtil::longByteEntryBackwards); ++ } ++ ++ public static ObjectSet entrySetLongLongWrap(Map map) { ++ return new ConvertingObjectSet, Long2LongMap.Entry>(map.entrySet(), FastUtilHackUtil::longLongEntryForwards, FastUtilHackUtil::longLongEntryBackwards); ++ } ++ ++ ++ static class WrappingIntIterator implements IntIterator { ++ ++ Iterator backing; ++ ++ public WrappingIntIterator(Iterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public int nextInt() { ++ return backing.next(); ++ } ++ ++ @Override ++ public Integer next() { ++ return backing.next(); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ } ++ ++ static class WrappingLongIterator implements LongIterator { ++ ++ Iterator backing; ++ ++ public WrappingLongIterator(Iterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public long nextLong() { ++ return backing.next(); ++ } ++ ++ @Override ++ public Long next() { ++ return backing.next(); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ } ++ ++ static class WrappingShortIterator implements ShortIterator { ++ ++ Iterator backing; ++ ++ public WrappingShortIterator(Iterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public short nextShort() { ++ return backing.next(); ++ } ++ ++ @Override ++ public Short next() { ++ return backing.next(); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ } ++ ++ public static class WrappingIntSet implements IntSet { ++ ++ Set backing; ++ ++ public WrappingIntSet(Set backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean add(int key) { ++ return backing.add(key); ++ } ++ ++ @Override ++ public boolean contains(int key) { ++ return backing.contains(key); ++ } ++ ++ @Override ++ public int[] toIntArray() { ++ return backing.stream().mapToInt(i -> i).toArray(); ++ } ++ ++ @Override ++ public int[] toIntArray(int[] a) { ++ if (a.length >= size()) { ++ return null; ++ } else { ++ return toIntArray(); ++ } ++ } ++ ++ @Override ++ public int[] toArray(int[] a) { ++ return toIntArray(a); ++ } ++ ++ @Override ++ public boolean addAll(IntCollection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(IntCollection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(IntCollection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(IntCollection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public IntIterator iterator() { ++ return new WrappingIntIterator(backing.iterator()); ++ } ++ ++ @Override ++ public boolean remove(int k) { ++ return backing.remove(k); ++ } ++ ++ } ++ ++ public static LongSet wrapLongSet(Set longset) { ++ return new WrappingLongSet(longset); ++ } ++ ++ public static class WrappingLongSet implements LongSet { ++ ++ Set backing; ++ ++ public WrappingLongSet(Set backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean add(long key) { ++ return backing.add(key); ++ } ++ ++ @Override ++ public boolean contains(long key) { ++ return backing.contains(key); ++ } ++ ++ @Override ++ public long[] toLongArray() { ++ return backing.stream().mapToLong(i -> i).toArray(); ++ } ++ ++ @Override ++ public long[] toLongArray(long[] a) { ++ if (a.length >= size()) { ++ return null; ++ } else { ++ return toLongArray(); ++ } ++ } ++ ++ @Override ++ public long[] toArray(long[] a) { ++ return toLongArray(a); ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(LongCollection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(LongCollection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(LongCollection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public LongIterator iterator() { ++ return new WrappingLongIterator(backing.iterator()); ++ } ++ ++ @Override ++ public boolean remove(long k) { ++ return backing.remove(k); ++ } ++ ++ } ++ ++ public static LongSortedSet wrapLongSortedSet(Set longset) { ++ return new WrappingLongSortedSet(longset); ++ } ++ ++ public static class WrappingLongSortedSet implements LongSortedSet { ++ ++ Set backing; ++ ++ public WrappingLongSortedSet(Set backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public boolean add(long key) { ++ return backing.add(key); ++ } ++ ++ @Override ++ public boolean contains(long key) { ++ return backing.contains(key); ++ } ++ ++ @Override ++ public long[] toLongArray() { ++ return backing.stream().mapToLong(i -> i).toArray(); ++ } ++ ++ @Override ++ public long[] toLongArray(long[] a) { ++ if (a.length >= size()) { ++ return null; ++ } else { ++ return toLongArray(); ++ } ++ } ++ ++ @Override ++ public long[] toArray(long[] a) { ++ return toLongArray(a); ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean containsAll(LongCollection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(LongCollection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(LongCollection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public boolean remove(long k) { ++ return backing.remove(k); ++ } ++ ++ @Override ++ public LongBidirectionalIterator iterator(long fromElement) { ++ throw new UnsupportedOperationException(); ++ //return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); ++ } ++ ++ @Override ++ public LongBidirectionalIterator iterator() { ++ return FastUtilHackUtil.wrap(new LinkedList(backing).iterator()); ++ } ++ ++ @Override ++ public LongSortedSet subSet(long fromElement, long toElement) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongSortedSet headSet(long toElement) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongSortedSet tailSet(long fromElement) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongComparator comparator() { ++ return null; ++ } ++ ++ @Override ++ public long firstLong() { ++ return backing.stream().findAny().get(); ++ } ++ ++ @Override ++ public long lastLong() { ++ return backing.stream().findAny().get(); ++ } ++ ++ } ++ ++ public static IntSet wrapIntSet(Set intset) { ++ return new WrappingIntSet(intset); ++ } ++ ++ public static class WrappingObjectCollection implements ObjectCollection { ++ ++ Collection backing; ++ ++ public WrappingObjectCollection(Collection backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(Object o) { ++ return backing.contains(o); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean add(V e) { ++ return backing.add(e); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ return backing.remove(o); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public ObjectIterator iterator() { ++ return FastUtilHackUtil.itrWrap(backing); ++ } ++ ++ } ++ ++ public static ObjectCollection wrap(Collection c) { ++ return new WrappingObjectCollection(c); ++ } ++ ++ public static class WrappingByteCollection implements ByteCollection { ++ ++ Collection backing; ++ ++ public WrappingByteCollection(Collection backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(byte o) { ++ return backing.contains(o); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean add(byte e) { ++ return backing.add(e); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ return backing.remove(o); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public ByteIterator iterator() { ++ return FastUtilHackUtil.itrByteWrap(backing); ++ } ++ ++ @Override ++ public boolean rem(byte key) { ++ return this.remove(key); ++ } ++ ++ @Override ++ public byte[] toByteArray() { ++ return null; ++ } ++ ++ @Override ++ public byte[] toByteArray(byte[] a) { ++ return toArray(a); ++ } ++ ++ @Override ++ public byte[] toArray(byte[] a) { ++ return ArrayUtils.toPrimitive(backing.toArray(new Byte[0])); ++ } ++ ++ @Override ++ public boolean addAll(ByteCollection c) { ++ return addAll((Collection) c); ++ } ++ ++ @Override ++ public boolean containsAll(ByteCollection c) { ++ return containsAll((Collection) c); ++ } ++ ++ @Override ++ public boolean removeAll(ByteCollection c) { ++ return removeAll((Collection) c); ++ } ++ ++ @Override ++ public boolean retainAll(ByteCollection c) { ++ return retainAll((Collection) c); ++ } ++ ++ } ++ ++ public static ByteCollection wrapBytes(Collection c) { ++ return new WrappingByteCollection(c); ++ } ++ ++ public static class WrappingIntCollection implements IntCollection { ++ ++ Collection backing; ++ ++ public WrappingIntCollection(Collection backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(int o) { ++ return backing.contains(o); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean add(int e) { ++ return backing.add(e); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ return backing.remove(o); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public IntIterator iterator() { ++ return FastUtilHackUtil.itrIntWrap(backing); ++ } ++ ++ @Override ++ public boolean rem(int key) { ++ return this.remove(key); ++ } ++ ++ @Override ++ public int[] toIntArray() { ++ return null; ++ } ++ ++ @Override ++ public int[] toIntArray(int[] a) { ++ return toArray(a); ++ } ++ ++ @Override ++ public int[] toArray(int[] a) { ++ return ArrayUtils.toPrimitive(backing.toArray(new Integer[0])); ++ } ++ ++ @Override ++ public boolean addAll(IntCollection c) { ++ return addAll((Collection) c); ++ } ++ ++ @Override ++ public boolean containsAll(IntCollection c) { ++ return containsAll((Collection) c); ++ } ++ ++ @Override ++ public boolean removeAll(IntCollection c) { ++ return removeAll((Collection) c); ++ } ++ ++ @Override ++ public boolean retainAll(IntCollection c) { ++ return retainAll((Collection) c); ++ } ++ ++ } ++ ++ public static IntCollection wrapInts(Collection c) { ++ return new WrappingIntCollection(c); ++ } ++ ++ public static class WrappingLongCollection implements LongCollection { ++ ++ Collection backing; ++ ++ public WrappingLongCollection(Collection backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(long o) { ++ return backing.contains(o); ++ } ++ ++ @Override ++ public Object[] toArray() { ++ return backing.toArray(); ++ } ++ ++ @Override ++ public T[] toArray(T[] a) { ++ return backing.toArray(a); ++ } ++ ++ @Override ++ public boolean add(long e) { ++ return backing.add(e); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ return backing.remove(o); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ return backing.containsAll(c); ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ return backing.addAll(c); ++ } ++ ++ @Override ++ public boolean removeAll(Collection c) { ++ return backing.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(Collection c) { ++ return backing.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public LongIterator iterator() { ++ return FastUtilHackUtil.itrLongWrap(backing); ++ } ++ ++ @Override ++ public boolean rem(long key) { ++ return this.remove(key); ++ } ++ ++ @Override ++ public long[] toLongArray() { ++ return null; ++ } ++ ++ @Override ++ public long[] toLongArray(long[] a) { ++ return toArray(a); ++ } ++ ++ @Override ++ public long[] toArray(long[] a) { ++ return ArrayUtils.toPrimitive(backing.toArray(new Long[0])); ++ } ++ ++ @Override ++ public boolean addAll(LongCollection c) { ++ return addAll((Collection) c); ++ } ++ ++ @Override ++ public boolean containsAll(LongCollection c) { ++ return containsAll((Collection) c); ++ } ++ ++ @Override ++ public boolean removeAll(LongCollection c) { ++ return removeAll((Collection) c); ++ } ++ ++ @Override ++ public boolean retainAll(LongCollection c) { ++ return retainAll((Collection) c); ++ } ++ ++ } ++ ++ public static LongCollection wrapLongs(Collection c) { ++ return new WrappingLongCollection(c); ++ } ++ ++ ++ public static class WrappingLongListIterator implements LongListIterator { ++ ++ ListIterator backing; ++ ++ public WrappingLongListIterator(ListIterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public long previousLong() { ++ return backing.previous(); ++ } ++ ++ @Override ++ public long nextLong() { ++ return backing.next(); ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public boolean hasPrevious() { ++ return backing.hasPrevious(); ++ } ++ ++ @Override ++ public int nextIndex() { ++ return backing.nextIndex(); ++ } ++ ++ @Override ++ public int previousIndex() { ++ return backing.previousIndex(); ++ } ++ ++ @Override ++ public void add(long k) { ++ backing.add(k); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ @Override ++ public void set(long k) { ++ backing.set(k); ++ } ++ } ++ ++ public static class SlimWrappingLongListIterator implements LongListIterator { ++ ++ Iterator backing; ++ ++ public SlimWrappingLongListIterator(Iterator backing) { ++ this.backing = backing; ++ } ++ ++ @Override ++ public long previousLong() { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public long nextLong() { ++ return backing.next(); ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return backing.hasNext(); ++ } ++ ++ @Override ++ public boolean hasPrevious() { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public int nextIndex() { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public int previousIndex() { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public void add(long k) { ++ throw new IllegalStateException(); ++ } ++ ++ @Override ++ public void remove() { ++ backing.remove(); ++ } ++ ++ @Override ++ public void set(long k) { ++ throw new IllegalStateException(); ++ } ++ } ++ ++ public static LongListIterator wrap(ListIterator c) { ++ return new WrappingLongListIterator(c); ++ } ++ ++ public static LongListIterator wrap(Iterator c) { ++ return new SlimWrappingLongListIterator(c); ++ } ++ ++ public static class WrappingByteIterator implements ByteIterator { ++ ++ Iterator parent; ++ ++ public WrappingByteIterator(Iterator parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return parent.hasNext(); ++ } ++ ++ @Override ++ public Byte next() { ++ return parent.next(); ++ } ++ ++ @Override ++ public void remove() { ++ parent.remove(); ++ } ++ ++ @Override ++ public byte nextByte() { ++ return next(); ++ } ++ ++ } ++ ++ public static ByteIterator itrByteWrap(Iterator backing) { ++ return new WrappingByteIterator(backing); ++ } ++ ++ public static ByteIterator itrByteWrap(Iterable backing) { ++ return new WrappingByteIterator(backing.iterator()); ++ } ++ ++ public static IntIterator itrIntWrap(Iterator backing) { ++ return new WrappingIntIterator(backing); ++ } ++ ++ public static IntIterator itrIntWrap(Iterable backing) { ++ return new WrappingIntIterator(backing.iterator()); ++ } ++ ++ public static LongIterator itrLongWrap(Iterator backing) { ++ return new WrappingLongIterator(backing); ++ } ++ ++ public static LongIterator itrLongWrap(Iterable backing) { ++ return new WrappingLongIterator(backing.iterator()); ++ } ++ ++ public static ShortIterator itrShortWrap(Iterator backing) { ++ return new WrappingShortIterator(backing); ++ } ++ ++ public static ShortIterator itrShortWrap(Iterable backing) { ++ return new WrappingShortIterator(backing.iterator()); ++ } ++ ++ public static class WrapperObjectIterator implements ObjectIterator { ++ ++ Iterator parent; ++ ++ public WrapperObjectIterator(Iterator parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return parent.hasNext(); ++ } ++ ++ @Override ++ public T next() { ++ return parent.next(); ++ } ++ ++ @Override ++ public void remove() { ++ parent.remove(); ++ } ++ ++ } ++ ++ public static class IntWrapperEntry implements Entry { ++ ++ Map.Entry parent; ++ ++ public IntWrapperEntry(Map.Entry parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public T getValue() { ++ return parent.getValue(); ++ } ++ ++ @Override ++ public T setValue(T value) { ++ return parent.setValue(value); ++ } ++ ++ @Override ++ public int getIntKey() { ++ return parent.getKey(); ++ } ++ ++ @Override ++ public Integer getKey() { ++ return parent.getKey(); ++ } ++ ++ } ++ ++ public static class Long2IntWrapperEntry implements Long2IntMap.Entry { ++ ++ Map.Entry parent; ++ ++ public Long2IntWrapperEntry(Map.Entry parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public long getLongKey() { ++ return parent.getKey(); ++ } ++ ++ @Override ++ public int getIntValue() { ++ return parent.getValue(); ++ } ++ ++ @Override ++ public int setValue(int value) { ++ return parent.setValue(value); ++ } ++ ++ ++ } ++ ++ public static class WrapperIntEntryObjectIterator implements ObjectIterator> { ++ ++ Iterator> parent; ++ ++ public WrapperIntEntryObjectIterator(Iterator> parent) { ++ this.parent = parent; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return parent.hasNext(); ++ } ++ ++ @Override ++ public Entry next() { ++ Map.Entry val = parent.next(); ++ if (val == null) return null; ++ return new IntWrapperEntry(val); ++ } ++ ++ @Override ++ public void remove() { ++ parent.remove(); ++ } ++ ++ } ++ ++ public static ObjectIterator> intMapItrFake(Map in) { ++ return new WrapperIntEntryObjectIterator(in.entrySet().iterator()); ++ } ++ ++ public static ObjectIterator itrWrap(Iterator in) { ++ return new WrapperObjectIterator(in); ++ } ++ ++ public static ObjectIterator itrWrap(Iterable in) { ++ return new WrapperObjectIterator(in.iterator()); ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bb6c592fd34423fdd910feae83a058d288da537a +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Int2ObjectConcurrentHashMap.java +@@ -0,0 +1,93 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ++import it.unimi.dsi.fastutil.ints.IntSet; ++import it.unimi.dsi.fastutil.objects.ObjectCollection; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++import org.apache.commons.lang3.NotImplementedException; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class Int2ObjectConcurrentHashMap implements Int2ObjectMap { ++ ++ Map backing; ++ ++ public Int2ObjectConcurrentHashMap() { ++ backing = new ConcurrentHashMap(); ++ } ++ ++ @Override ++ public V get(int key) { ++ return backing.get(key); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean containsValue(Object value) { ++ return backing.containsValue(value); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(V rv) { ++ throw new NotImplementedException("MCMT - Not implemented"); ++ } ++ ++ @Override ++ public V defaultReturnValue() { ++ return null; ++ } ++ ++ @Override ++ public ObjectSet> int2ObjectEntrySet() { ++ return FastUtilHackUtil.entrySetIntWrap(backing); ++ } ++ ++ ++ @Override ++ public IntSet keySet() { ++ return FastUtilHackUtil.wrapIntSet(backing.keySet()); ++ } ++ ++ @Override ++ public ObjectCollection values() { ++ return FastUtilHackUtil.wrap(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(int key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public V put(int key, V value) { ++ return backing.put(key, value); ++ } ++ ++ @Override ++ public V put(Integer key, V value) { ++ return backing.put(key, value); ++ } ++ ++ @Override ++ public V remove(int key) { ++ return backing.remove(key); ++ } ++ ++ @Override ++ public void clear() { backing.clear(); } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e0fab16860e1be817fd10dbec684f295f2e291dd +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ByteConcurrentHashMap.java +@@ -0,0 +1,99 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.bytes.ByteCollection; ++import it.unimi.dsi.fastutil.longs.Long2ByteMap; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class Long2ByteConcurrentHashMap implements Long2ByteMap { ++ ++ Map backing; ++ byte defaultReturn = 0; ++ byte nullKey = 0; ++ ++ public Long2ByteConcurrentHashMap() { ++ backing = new ConcurrentHashMap<>(); ++ } ++ ++ public Long2ByteConcurrentHashMap(int initialCapacity, float loadFactor) { ++ backing = new ConcurrentHashMap<>(initialCapacity, loadFactor); ++ } ++ ++ @Override ++ public byte get(long key) { ++ Byte out = backing.get(key); ++ return out == null ? defaultReturn : out; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean containsValue(byte value) { ++ return backing.containsValue(value); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(byte rv) { ++ defaultReturn = rv; ++ } ++ ++ @Override ++ public byte defaultReturnValue() { ++ return defaultReturn; ++ } ++ ++ @Override ++ public ObjectSet long2ByteEntrySet() { ++ return FastUtilHackUtil.entrySetLongByteWrap(backing); ++ } ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public ByteCollection values() { ++ return FastUtilHackUtil.wrapBytes(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public byte put(long key, byte value) { ++ return put((Long) key, (Byte) value); ++ } ++ ++ @Override ++ public Byte put(Long key, Byte value) { ++ Byte out = backing.put(key, value); ++ return out == null ? Byte.valueOf(defaultReturn) : out; ++ } ++ ++ @Override ++ public byte remove(long key) { ++ Byte out = backing.remove(key); ++ return out == null ? defaultReturn : out; ++ } ++ ++ ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..261f06a88021a95b6a0500444665547aeb4ae2c1 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentHashMap.java +@@ -0,0 +1,74 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.ints.IntCollection; ++import it.unimi.dsi.fastutil.longs.Long2IntMap; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class Long2IntConcurrentHashMap implements Long2IntMap { ++ ++ public Map backing = new ConcurrentHashMap(); ++ int defaultRV = 0; ++ ++ @Override ++ public int get(long key) { ++ if (backing.containsKey(key)) { ++ return backing.get(key); ++ } else return defaultRV; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(int rv) { ++ defaultRV = rv; ++ } ++ ++ @Override ++ public int defaultReturnValue() { ++ return defaultRV; ++ } ++ ++ @Override ++ public ObjectSet long2IntEntrySet() { ++ return null; ++ } ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public IntCollection values() { ++ return FastUtilHackUtil.wrapInts(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public boolean containsValue(int value) { ++ return backing.containsValue(value); ++ } ++ ++ ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..dc2342486318721d399c7c60a0a859befb4d1c9f +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2IntConcurrentNonLinkedOpenMap.java +@@ -0,0 +1,375 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.Hash; ++import it.unimi.dsi.fastutil.ints.IntCollection; ++import it.unimi.dsi.fastutil.longs.*; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++@SuppressWarnings("deprecation") ++public class Long2IntConcurrentNonLinkedOpenMap extends Long2IntLinkedOpenHashMap { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -2082212127278131631L; ++ ++ public Map backing = new ConcurrentHashMap(); ++ ++ public Long2IntConcurrentNonLinkedOpenMap(final int expected, final float f) { ++ ++ } ++ ++ /** ++ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. ++ * ++ * @param expected the expected number of elements in the hash map. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final int expected) { ++ } ++ ++ /** ++ * Creates a new hash map with initial expected ++ * {@link Hash#DEFAULT_INITIAL_SIZE} entries and ++ * {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap() { ++ } ++ ++ /** ++ * Creates a new hash map copying a given one. ++ * ++ * @param m a {@link Map} to be copied into the new hash map. ++ * @param f the load factor. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final Map m, final float f) { ++ putAll(m); ++ } ++ ++ /** ++ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor ++ * copying a given one. ++ * ++ * @param m a {@link Map} to be copied into the new hash map. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final Map m) { ++ this(m, DEFAULT_LOAD_FACTOR); ++ } ++ ++ /** ++ * Creates a new hash map copying a given type-specific one. ++ * ++ * @param m a type-specific map to be copied into the new hash map. ++ * @param f the load factor. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m, final float f) { ++ this(m.size(), f); ++ putAll(m); ++ } ++ ++ /** ++ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor ++ * copying a given type-specific one. ++ * ++ * @param m a type-specific map to be copied into the new hash map. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final Long2IntMap m) { ++ this(m, DEFAULT_LOAD_FACTOR); ++ } ++ ++ /** ++ * Creates a new hash map using the elements of two parallel arrays. ++ * ++ * @param k the array of keys of the new hash map. ++ * @param v the array of corresponding values in the new hash map. ++ * @param f the load factor. ++ * @throws IllegalArgumentException if {@code k} and {@code v} have different ++ * lengths. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v, final float f) { ++ if (k.length != v.length) ++ throw new IllegalArgumentException( ++ "The key array and the value array have different lengths (" + k.length + " and " + v.length + ")"); ++ for (int i = 0; i < k.length; i++) ++ this.put(k[i], v[i]); ++ } ++ ++ /** ++ * Creates a new hash map with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor ++ * using the elements of two parallel arrays. ++ * ++ * @param k the array of keys of the new hash map. ++ * @param v the array of corresponding values in the new hash map. ++ * @throws IllegalArgumentException if {@code k} and {@code v} have different ++ * lengths. ++ */ ++ public Long2IntConcurrentNonLinkedOpenMap(final long[] k, final int[] v) { ++ this(k, v, DEFAULT_LOAD_FACTOR); ++ } ++ ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ public int put(final long k, final int v) { ++ Integer out = backing.put(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int addTo(final long k, final int incr) { ++ Integer out = backing.put(k, this.get(k)+incr); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int remove(final long k) { ++ Integer out = backing.remove(k); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int removeFirstInt() { ++ Integer out = this.remove(backing.keySet().stream().findAny().get()); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int removeLastInt() { ++ Integer out = this.remove(backing.keySet().stream().findAny().get()); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public int getAndMoveToFirst(final long k) { ++ Integer out = backing.get(k); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int getAndMoveToLast(final long k) { ++ Integer out = backing.get(k); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int putAndMoveToFirst(final long k, final int v) { ++ Integer out = backing.put(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int putAndMoveToLast(final long k, final int v) { ++ Integer out = backing.put(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int get(final long k) { ++ Integer out = backing.get(k); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public boolean containsKey(final long k) { ++ return backing.containsKey(k); ++ } ++ ++ public boolean containsValue(final int v) { ++ return backing.containsValue(v); ++ } ++ ++ public int getOrDefault(final long k, final int defaultValue) { ++ Integer out = backing.getOrDefault(k, defaultValue); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ public int putIfAbsent(final long k, final int v) { ++ Integer out = backing.putIfAbsent(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public boolean remove(final long k, final int v) { ++ return backing.remove(k, v); ++ } ++ ++ ++ public boolean replace(final long k, final int oldValue, final int v) { ++ return backing.replace(k, oldValue, v); ++ } ++ ++ ++ public int replace(final long k, final int v) { ++ Integer out = backing.replace(k, v); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public int computeIfAbsent(final long k, final java.util.function.LongToIntFunction mappingFunction) { ++ Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.applyAsInt(l)); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public int computeIfAbsentNullable(final long k, ++ final java.util.function.LongFunction mappingFunction) { ++ Integer out = backing.computeIfAbsent(k, (l) -> mappingFunction.apply(l)); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ ++ public int computeIfPresent(final long k, ++ final java.util.function.BiFunction remappingFunction) { ++ if (this.containsKey(k)) { ++ Integer out = backing.put(k, remappingFunction.apply(k, backing.get(k))); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ return defaultReturnValue(); ++ ++ } ++ ++ @Override ++ public int compute(final long k, ++ final java.util.function.BiFunction remappingFunction) { ++ Integer out = backing.compute(k, remappingFunction); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ @Override ++ public int merge(final long k, final int v, ++ final java.util.function.BiFunction remappingFunction) { ++ Integer out = backing.merge(k, v, remappingFunction); ++ if (out == null) { ++ return defRetValue; ++ } ++ return out; ++ } ++ ++ @Override ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public long firstLongKey() { ++ return backing.keySet().stream().findAny().get(); ++ } ++ ++ @Override ++ public long lastLongKey() { ++ return backing.keySet().stream().findAny().get(); ++ } ++ ++ @Override ++ public Long2IntSortedMap tailMap(long from) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public Long2IntSortedMap headMap(long to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public Long2IntSortedMap subMap(long from, long to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongComparator comparator() { ++ return null; ++ } ++ ++ ++ @Override ++ public FastSortedEntrySet long2IntEntrySet() { ++ //TODO implement ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public LongSortedSet keySet() { ++ return FastUtilHackUtil.wrapLongSortedSet(backing.keySet()); ++ } ++ ++ ++ @Override ++ public IntCollection values() { ++ return FastUtilHackUtil.wrapInts(backing.values()); ++ } ++ ++ public boolean trim() { ++ return true; ++ } ++ ++ public boolean trim(final int n) { ++ return true; ++ } ++ ++ ++ @Override ++ public Long2IntConcurrentNonLinkedOpenMap clone() { ++ return new Long2IntConcurrentNonLinkedOpenMap(backing); ++ } ++ ++ @Override ++ public int hashCode() { ++ return backing.hashCode(); ++ } ++ ++ ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3205d30a03f99caf7dfa05237b2bc31182b2db20 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2LongConcurrentHashMap.java +@@ -0,0 +1,97 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.longs.Long2LongMap; ++import it.unimi.dsi.fastutil.longs.LongCollection; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++ ++public class Long2LongConcurrentHashMap implements Long2LongMap { ++ ++ public Map backing = new ConcurrentHashMap(); ++ long defaultRV = 0; ++ ++ public Long2LongConcurrentHashMap(long defaultRV) { ++ this.defaultRV = defaultRV; ++ } ++ ++ @Override ++ public long get(long key) { ++ if (backing.containsKey(key)) { ++ return backing.get(key); ++ } else return defaultRV; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public long put(final long key, final long val) { ++ backing.put(key,val); ++ return val; ++ } ++ ++ @Override ++ public Long put(final Long key, final Long val) { ++ backing.put(key,val); ++ return val; ++ } ++ ++ @Override ++ public long remove(final long key) { ++ return backing.remove(key); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(long rv) { ++ defaultRV = rv; ++ } ++ ++ @Override ++ public long defaultReturnValue() { ++ return defaultRV; ++ } ++ ++ @Override ++ public ObjectSet long2LongEntrySet() { ++ return FastUtilHackUtil.entrySetLongLongWrap(backing); ++ } ++ ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public LongCollection values() { ++ return FastUtilHackUtil.wrapLongs(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public boolean containsValue(long value) { ++ return backing.containsValue(value); ++ } ++ ++ ++} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a5ed71564d4c9a986f77cbc0397130aa38f97a91 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectConcurrentHashMap.java +@@ -0,0 +1,93 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.longs.Long2ObjectMap; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectCollection; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class Long2ObjectConcurrentHashMap implements Long2ObjectMap { ++ ++ Map backing; ++ V defaultReturn = null; ++ ++ public Long2ObjectConcurrentHashMap() { ++ backing = new ConcurrentHashMap(); ++ } ++ ++ @Override ++ public V get(long key) { ++ V out = backing.get(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean containsValue(Object value) { ++ return backing.containsValue(value); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(V rv) { ++ defaultReturn = rv; ++ } ++ ++ @Override ++ public V defaultReturnValue() { ++ return defaultReturn; ++ } ++ ++ @Override ++ public ObjectSet> long2ObjectEntrySet() { ++ return FastUtilHackUtil.entrySetLongWrap(backing); ++ } ++ ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public ObjectCollection values() { ++ return FastUtilHackUtil.wrap(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public V put(long key, V value) { ++ return put((Long)key, value); ++ } ++ ++ @Override ++ public V put(Long key, V value) { ++ V out = backing.put(key, value); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); ++ } ++ ++ @Override ++ public V remove(long key) { ++ V out = backing.remove(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++} +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a7d6be048ab3b8bd38231fce16eca0ac78e24690 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/Long2ObjectOpenConcurrentHashMap.java +@@ -0,0 +1,233 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil; ++ ++import it.unimi.dsi.fastutil.longs.Long2ObjectFunction; ++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.objects.ObjectCollection; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++ ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++import java.util.function.Function; ++ ++public class Long2ObjectOpenConcurrentHashMap extends Long2ObjectOpenHashMap { ++ ++ /** ++ * ++ */ ++ private static final long serialVersionUID = -121514116954680057L; ++ ++ Map backing; ++ V defaultReturn = null; ++ ++ public Long2ObjectOpenConcurrentHashMap() { ++ backing = new ConcurrentHashMap(); ++ } ++ ++ @Override ++ public V get(long key) { ++ V out = backing.get(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++ ++ @Override ++ public V get(Object key) { ++ V out = backing.get(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return backing.isEmpty(); ++ } ++ ++ @Override ++ public boolean containsValue(Object value) { ++ return backing.containsValue(value); ++ } ++ ++ @Override ++ public void putAll(Map m) { ++ backing.putAll(m); ++ } ++ ++ @Override ++ public int size() { ++ return backing.size(); ++ } ++ ++ @Override ++ public void defaultReturnValue(V rv) { ++ defaultReturn = rv; ++ } ++ ++ @Override ++ public V defaultReturnValue() { ++ return defaultReturn; ++ } ++ ++ @Override ++ public FastEntrySet long2ObjectEntrySet() { ++ return FastUtilHackUtil.entrySetLongWrapFast(backing); ++ } ++ ++ ++ @Override ++ public LongSet keySet() { ++ return FastUtilHackUtil.wrapLongSet(backing.keySet()); ++ } ++ ++ @Override ++ public ObjectCollection values() { ++ return FastUtilHackUtil.wrap(backing.values()); ++ } ++ ++ @Override ++ public boolean containsKey(long key) { ++ return backing.containsKey(key); ++ } ++ ++ @Override ++ public V put(long key, V value) { ++ return put((Long)key, value); ++ } ++ ++ @Override ++ public V put(Long key, V value) { ++ V out = backing.put(key, value); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : backing.put(key, value); ++ } ++ ++ @Override ++ public V remove(long key) { ++ V out = backing.remove(key); ++ return (out == null && !backing.containsKey(key)) ? defaultReturn : out; ++ } ++ ++ @Override ++ public boolean trim() { return true; } ++ ++ @Override ++ public boolean trim(final int n) { return true; } ++ ++ @Override ++ public boolean replace(final long k, final V oldValue, final V v) { ++ return backing.replace(k, oldValue, v); ++ } ++ ++ @Override ++ public V replace(final long k, final V v) { ++ return backing.replace(k, v); ++ } ++ ++ @Override ++ public boolean replace(final Long k, final V oldValue, final V v) { ++ return backing.replace(k, oldValue, v); ++ } ++ ++ @Override ++ public V replace(final Long k, final V v) { ++ return backing.replace(k, v); ++ } ++ ++ @Override ++ public boolean remove(final long k, final Object v) { ++ return backing.remove(k, v); ++ } ++ ++ @Override ++ public V putIfAbsent(final long k, final V v) { ++ return backing.putIfAbsent(k, v); ++ } ++ ++ @Override ++ public V putIfAbsent(final Long k, final V v) { ++ return backing.putIfAbsent(k, v); ++ } ++ ++ @Override ++ public V merge(final long k, final V v, final java.util.function.BiFunction remappingFunction) { ++ return backing.merge(k, v, remappingFunction); ++ } ++ ++ @Override ++ public V merge(Long k, final V v, final java.util.function.BiFunction remappingFunction) { ++ return backing.merge(k, v, remappingFunction); ++ } ++ ++ @Override ++ public int hashCode() { ++ return backing.hashCode(); ++ } ++ ++ @Override ++ public V getOrDefault(final long k, final V defaultValue) { ++ return backing.getOrDefault(k, defaultValue); ++ } ++ ++ @Override ++ public V getOrDefault(Object k, final V defaultValue) { ++ return backing.getOrDefault(k, defaultValue); ++ } ++ ++ @Override ++ public V computeIfPresent(final long k, final java.util.function.BiFunction remappingFunction) { ++ return backing.computeIfPresent(k, remappingFunction); ++ } ++ ++ @Override ++ public V computeIfPresent(final Long k, final java.util.function.BiFunction remappingFunction) { ++ return backing.computeIfPresent(k, remappingFunction); ++ } ++ ++ @Override ++ public V computeIfAbsent(final long k, final java.util.function.LongFunction mappingFunction) { ++ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); ++ } ++ ++ public V computeIfAbsent(final Long k, final java.util.function.LongFunction mappingFunction) { ++ return backing.computeIfAbsent(k, (llong) -> mappingFunction.apply(llong)); ++ } ++ ++ @Override ++ public V computeIfAbsentPartial(final long key, final Long2ObjectFunction mappingFunction) { ++ if (!mappingFunction.containsKey(key)) ++ return defaultReturn; ++ return backing.computeIfAbsent(key, (llong) -> mappingFunction.apply(llong)); ++ } ++ ++ @Override ++ public V compute(final long k, final java.util.function.BiFunction remappingFunction) { ++ return backing.compute(k, remappingFunction); ++ } ++ ++ @Override ++ public V compute(final Long k, final java.util.function.BiFunction remappingFunction) { ++ return backing.compute(k, remappingFunction); ++ } ++ ++ @Override ++ public Long2ObjectOpenHashMap clone() { ++ throw new IllegalArgumentException(); ++ } ++ ++ public void clear() { ++ backing.clear(); ++ } ++ ++ @Override ++ public ObjectSet> entrySet() { ++ return new FastUtilHackUtil.ConvertingObjectSet, Map.Entry>(backing.entrySet(), Function.identity(), Function.identity()); ++ } ++ ++ @Override ++ public V remove(Object key) { ++ return backing.remove(key); ++ } ++ ++ @Override ++ public boolean remove(Object key, Object value) { ++ return backing.remove(key, value); ++ } ++ ++} +\ No newline at end of file +diff --git a/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1d65e9575389a170e8b8465b1221a6e319749eb1 +--- /dev/null ++++ b/src/main/java/net/himeki/mcmtfabric/parallelised/fastutil/sync/SyncLongLinkedOpenHashSet.java +@@ -0,0 +1,190 @@ ++package net.himeki.mcmtfabric.parallelised.fastutil.sync; ++ ++import it.unimi.dsi.fastutil.longs.*; ++ ++import java.util.Collection; ++import java.util.Iterator; ++ ++public class SyncLongLinkedOpenHashSet extends LongLinkedOpenHashSet { ++ ++ private static final long serialVersionUID = -5532128240738069111L; ++ ++ public SyncLongLinkedOpenHashSet() { ++ super(); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final int initial) { ++ super(initial); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final int initial, final float dnc) { ++ this(initial); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final LongCollection c) { ++ this(c.size()); ++ addAll(c); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final LongCollection c, final float f) { ++ this(c.size(), f); ++ addAll(c); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final LongIterator i, final float f) { ++ this(16, f); ++ while (i.hasNext()) ++ add(i.nextLong()); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final LongIterator i) { ++ this(i, -1); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final Iterator i, final float f) { ++ this(LongIterators.asLongIterator(i), f); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final Iterator i) { ++ this(LongIterators.asLongIterator(i)); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final long[] a, final int offset, final int length, final float f) { ++ this(length < 0 ? 0 : length, f); ++ LongArrays.ensureOffsetLength(a, offset, length); ++ for (int i = 0; i < length; i++) ++ add(a[offset + i]); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final long[] a, final int offset, final int length) { ++ this(a, offset, length, DEFAULT_LOAD_FACTOR); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final long[] a, final float f) { ++ this(a, 0, a.length, f); ++ } ++ ++ public SyncLongLinkedOpenHashSet(final long[] a) { ++ this(a, -1); ++ } ++ ++ @Override ++ public synchronized boolean add(final long k) { ++ return super.add(k); ++ } ++ ++ @Override ++ public synchronized boolean addAll(LongCollection c) { ++ return super.addAll(c); ++ } ++ ++ @Override ++ public synchronized boolean addAll(Collection c) { ++ return super.addAll(c); ++ } ++ ++ @Override ++ public synchronized boolean addAndMoveToFirst(final long k) { ++ return super.addAndMoveToFirst(k); ++ } ++ ++ @Override ++ public synchronized boolean addAndMoveToLast(final long k) { ++ return super.addAndMoveToFirst(k); ++ } ++ ++ @Override ++ public synchronized void clear() { ++ super.clear(); ++ } ++ ++ @Override ++ public synchronized LongLinkedOpenHashSet clone() { ++ return new SyncLongLinkedOpenHashSet(this); ++ } ++ ++ @Override ++ public synchronized LongComparator comparator() { ++ return super.comparator(); ++ } ++ ++ @Override ++ public synchronized boolean contains(final long k) { ++ return super.contains(k); ++ } ++ ++ @Override ++ public synchronized long firstLong() { ++ return super.firstLong(); ++ } ++ ++ @Override ++ public synchronized int hashCode() { ++ return super.hashCode(); ++ } ++ ++ @Override ++ public synchronized LongSortedSet headSet(long to) { ++ return super.headSet(to); ++ } ++ ++ @Override ++ public synchronized boolean isEmpty() { ++ return super.isEmpty(); ++ } ++ ++ @Override ++ public synchronized LongListIterator iterator() { ++ return super.iterator(); ++ } ++ ++ @Override ++ public synchronized LongListIterator iterator(long from) { ++ return super.iterator(from); ++ } ++ ++ @Override ++ public synchronized long lastLong() { ++ return super.lastLong(); ++ } ++ ++ @Override ++ public synchronized boolean remove(final long k) { ++ return super.remove(k); ++ } ++ ++ @Override ++ public synchronized long removeFirstLong() { ++ return super.removeFirstLong(); ++ } ++ ++ @Override ++ public synchronized long removeLastLong() { ++ return super.removeLastLong(); ++ } ++ ++ @Override ++ public synchronized int size() { ++ return super.size(); ++ } ++ ++ @Override ++ public synchronized LongSortedSet subSet(long from, long to) { ++ return super.subSet(from, to); ++ } ++ ++ @Override ++ public synchronized LongSortedSet tailSet(long from) { ++ return super.tailSet(from); ++ } ++ ++ @Override ++ public synchronized boolean trim() { ++ return super.trim(); ++ } ++ ++ @Override ++ public synchronized boolean trim(final int n) { ++ return super.trim(n); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java +index 4beaa69da4001fc2723e9628d64bd3de728d7213..ebb1c69f905ef3c948a65c9f095244b7e663b3a9 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java ++++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +@@ -2,9 +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.ShortSet; +-import it.unimi.dsi.fastutil.shorts.ShortSets; + import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentShortHashSet; + import net.minecraft.core.BlockPos; + import net.minecraft.core.SectionPos; +@@ -22,6 +20,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; + import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.level.chunk.*; + import net.minecraft.world.level.lighting.LevelLightEngine; ++ + import javax.annotation.Nullable; + import java.util.BitSet; + import java.util.List; diff --git a/patches/server/0057-Hearse-Fix-some-concurrent-problems-in-Explosion.patch b/patches/server/0057-Hearse-Fix-some-concurrent-problems-in-Explosion.patch new file mode 100644 index 00000000..3a42d904 --- /dev/null +++ b/patches/server/0057-Hearse-Fix-some-concurrent-problems-in-Explosion.patch @@ -0,0 +1,89 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Tue, 10 Jan 2023 12:50:27 +0800 +Subject: [PATCH] Hearse: Fix some concurrent problems in Explosion + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java +index 5ef58831a857fd8aa4ac30147762dc17d773a53e..2a8590d46bab64fe27e8dadf80f91ab0662a4352 100644 +--- a/src/main/java/net/minecraft/Util.java ++++ b/src/main/java/net/minecraft/Util.java +@@ -65,6 +65,8 @@ import java.util.stream.Collectors; + import java.util.stream.IntStream; + import java.util.stream.Stream; + import javax.annotation.Nullable; ++ ++import it.unimi.dsi.fastutil.objects.ObjectList; + import net.minecraft.resources.ResourceLocation; + import net.minecraft.server.Bootstrap; + import net.minecraft.util.Mth; +@@ -793,7 +795,7 @@ public class Util { + return objectArrayList; + } + +- public static void shuffle(ObjectArrayList list, RandomSource random) { ++ public static void shuffle(ObjectList list, RandomSource random) { + int i = list.size(); + + for(int j = i; j > 1; --j) { +diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java +index c0d39afe5b80159ed9aaca4ddd4763d707882f2e..2f53f8f2695231179ff16bb014cd990e94f9ec79 100644 +--- a/src/main/java/net/minecraft/world/level/Explosion.java ++++ b/src/main/java/net/minecraft/world/level/Explosion.java +@@ -4,12 +4,15 @@ import com.google.common.collect.Maps; + import com.google.common.collect.Sets; + import com.mojang.datafixers.util.Pair; + import it.unimi.dsi.fastutil.objects.ObjectArrayList; ++import it.unimi.dsi.fastutil.objects.ObjectList; + import it.unimi.dsi.fastutil.objects.ObjectListIterator; + import java.util.List; + import java.util.Map; + import java.util.Optional; + import java.util.Set; + import javax.annotation.Nullable; ++ ++import it.unimi.dsi.fastutil.objects.ObjectLists; + import net.minecraft.Util; + import net.minecraft.core.BlockPos; + import net.minecraft.core.particles.ParticleTypes; +@@ -63,7 +66,7 @@ public class Explosion { + private final float radius; + private final DamageSource damageSource; + private final ExplosionDamageCalculator damageCalculator; +- private final ObjectArrayList toBlow; ++ private final ObjectList toBlow; + private final Map hitPlayers; + public boolean wasCanceled = false; // CraftBukkit - add field + +@@ -81,9 +84,9 @@ public class Explosion { + } + + public Explosion(Level world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, double x, double y, double z, float power, boolean createFire, Explosion.BlockInteraction destructionType) { +- this.random = RandomSource.create(); +- this.toBlow = new ObjectArrayList(); +- this.hitPlayers = Maps.newHashMap(); ++ this.random = RandomSource.createThreadSafe(); ++ this.toBlow = ObjectLists.synchronize(new ObjectArrayList<>()); ++ this.hitPlayers = Maps.newConcurrentMap(); + this.level = world; + this.source = entity; + this.radius = (float) Math.max(power, 0.0); // CraftBukkit - clamp bad values +@@ -396,14 +399,10 @@ public class Explosion { + } + + if (this.fire) { +- ObjectListIterator objectlistiterator1 = this.toBlow.iterator(); +- +- while (objectlistiterator1.hasNext()) { +- BlockPos blockposition2 = (BlockPos) objectlistiterator1.next(); +- ++ for (BlockPos blockposition2 : this.toBlow) { + if (this.random.nextInt(3) == 0 && this.level.getBlockState(blockposition2).isAir() && this.level.getBlockState(blockposition2.below()).isSolidRender(this.level, blockposition2.below())) { + // CraftBukkit start - Ignition by explosion +- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), this).isCancelled()) { ++ if (!CraftEventFactory.callBlockIgniteEvent(this.level, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), this).isCancelled()) { + this.level.setBlockAndUpdate(blockposition2, BaseFireBlock.getState(this.level, blockposition2)); + } + // CraftBukkit end diff --git a/patches/server/0058-Hearse-Fix-some-problems-and-update-workerpool.patch b/patches/server/0058-Hearse-Fix-some-problems-and-update-workerpool.patch new file mode 100644 index 00000000..94827b6a --- /dev/null +++ b/patches/server/0058-Hearse-Fix-some-problems-and-update-workerpool.patch @@ -0,0 +1,187 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Tue, 10 Jan 2023 16:42:02 +0800 +Subject: [PATCH] Hearse: Fix some problems and update workerpool + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +index 783c8ea55095dbabe594a9fe3dc604515bd0c2f1..421d4926ac674b5eb12d9613ceb6d20185ea557d 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java ++++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java +@@ -6,14 +6,10 @@ public class WorkerThread extends TickThread { + + public WorkerThread(String name) { + super(name); +- this.setDaemon(true); +- this.setPriority(Thread.NORM_PRIORITY - 2); + } + + public WorkerThread(Runnable run, String name) { + super(run, name); +- this.setDaemon(true); +- this.setPriority(Thread.NORM_PRIORITY - 2); + } + + public static boolean isWorker(){ +diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java +index 8899c02a2242b51097a03c7e3ca03b8768c60117..7e010bf23c9fc26284212a4388172f5d7d5a4b99 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java ++++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java +@@ -17,6 +17,14 @@ public class WorkerThreadPoolExecutor extends ThreadPoolExecutor { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, workerThreadFactory::getNewThread, handler); + } + ++ public int getCurrentNotProcessingTasks(){ ++ return this.getQueue().size(); ++ } ++ ++ public void clearAllTasks(){ ++ this.getQueue().clear(); ++ } ++ + public void executeWithSubTask(Runnable mainTask,Runnable subTask){ + final TaskEntry wrapped = new TaskEntry(subTask,mainTask); + this.taskEntries.offer(wrapped); +diff --git a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3e3ae10fcc54b80ff4ec433f136d15d3b9fa4fe4 +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java +@@ -0,0 +1,22 @@ ++package co.earthme.hearse.concurrent.threadfactory; ++ ++import co.earthme.hearse.concurrent.WorkerThread; ++import co.earthme.hearse.concurrent.WorkerThreadFactory; ++import net.minecraft.server.MinecraftServer; ++import java.util.concurrent.atomic.AtomicInteger; ++ ++public class DefaultWorkerFactory implements WorkerThreadFactory { ++ private static final AtomicInteger poolId = new AtomicInteger(); ++ private final AtomicInteger threadId = new AtomicInteger(); ++ ++ @Override ++ public WorkerThread getNewThread(Runnable task) { ++ final WorkerThread workerThread = new WorkerThread(task,"pool-"+poolId.getAndIncrement()+"-worker-"+threadId.getAndIncrement()); ++ if (workerThread.isDaemon()){ ++ workerThread.setDaemon(false); ++ } ++ workerThread.setPriority(Thread.NORM_PRIORITY - 2); ++ workerThread.setContextClassLoader(MinecraftServer.class.getClassLoader()); ++ return workerThread; ++ } ++} +diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +index 2c2f752e1ae846c2b24c2d46a13473836c5feac6..8605d867bea87095dcf43e1c1ebedbb9180c4480 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +@@ -2,7 +2,9 @@ package co.earthme.hearse.server; + + import co.earthme.hearse.HearseConfig; + import co.earthme.hearse.concurrent.WorkerThread; ++import co.earthme.hearse.concurrent.WorkerThreadFactory; + import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; ++import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.entity.Entity; +@@ -13,6 +15,7 @@ import java.util.concurrent.atomic.AtomicInteger; + + public class ServerEntityTickHook { + private static volatile boolean firstTick = false; ++ private static final WorkerThreadFactory defFactory = new DefaultWorkerFactory(); + private static final AtomicInteger threadId = new AtomicInteger(); + private static WorkerThreadPoolExecutor worker; + private static boolean asyncEntityEnabled; +@@ -24,19 +27,25 @@ public class ServerEntityTickHook { + worker.execute(task); + } + ++ public static void onServerStop() throws InterruptedException { ++ if (!asyncEntityEnabled){ ++ return; ++ } ++ worker.shutdown(); ++ while (!worker.awaitTermination(100,TimeUnit.MILLISECONDS)); ++ } ++ + public static void init(){ +- final boolean asyncEntityEnabled1 = HearseConfig.getBoolean("enable-async-entity",true); ++ boolean asyncEntityEnabled1 = HearseConfig.getBoolean("enable-async-entity",true); + final int workerCount = HearseConfig.getInt("async-entity-worker-count",Runtime.getRuntime().availableProcessors()); + if (asyncEntityEnabled1){ + worker = new WorkerThreadPoolExecutor( + workerCount, + workerCount, +- 100, ++ 0L, + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), +- task -> { +- return new WorkerThread(task,"Hearse-Worker-Thread # "+threadId.getAndIncrement()); +- } ++ defFactory + ); + } + asyncEntityEnabled = asyncEntityEnabled1; +diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java +index 2a8590d46bab64fe27e8dadf80f91ab0662a4352..5ef58831a857fd8aa4ac30147762dc17d773a53e 100644 +--- a/src/main/java/net/minecraft/Util.java ++++ b/src/main/java/net/minecraft/Util.java +@@ -65,8 +65,6 @@ import java.util.stream.Collectors; + import java.util.stream.IntStream; + import java.util.stream.Stream; + import javax.annotation.Nullable; +- +-import it.unimi.dsi.fastutil.objects.ObjectList; + import net.minecraft.resources.ResourceLocation; + import net.minecraft.server.Bootstrap; + import net.minecraft.util.Mth; +@@ -795,7 +793,7 @@ public class Util { + return objectArrayList; + } + +- public static void shuffle(ObjectList list, RandomSource random) { ++ public static void shuffle(ObjectArrayList list, RandomSource random) { + int i = list.size(); + + for(int j = i; j > 1; --j) { +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 3a7435aabbd759222fbac41ab4ca304bd112a0ac..9c4811a9936f819e201b93e13519abf10d02ac8e 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -914,6 +914,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop toBlow; ++ private final ObjectArrayList toBlow; + private final Map hitPlayers; + public boolean wasCanceled = false; // CraftBukkit - add field + +@@ -85,7 +85,7 @@ public class Explosion { + + public Explosion(Level world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, double x, double y, double z, float power, boolean createFire, Explosion.BlockInteraction destructionType) { + this.random = RandomSource.createThreadSafe(); +- this.toBlow = ObjectLists.synchronize(new ObjectArrayList<>()); ++ this.toBlow = new ObjectArrayList<>(); + this.hitPlayers = Maps.newConcurrentMap(); + this.level = world; + this.source = entity; diff --git a/patches/server/0059-Hearse-Change-some-config-key-name.patch b/patches/server/0059-Hearse-Change-some-config-key-name.patch new file mode 100644 index 00000000..88c9e120 --- /dev/null +++ b/patches/server/0059-Hearse-Change-some-config-key-name.patch @@ -0,0 +1,113 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Tue, 10 Jan 2023 18:52:00 +0800 +Subject: [PATCH] Hearse: Change some config key name + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +index 8605d867bea87095dcf43e1c1ebedbb9180c4480..cf7ee6fda90fa0f6827dc2d1c584151e3b99fb38 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +@@ -36,8 +36,8 @@ public class ServerEntityTickHook { + } + + public static void init(){ +- boolean asyncEntityEnabled1 = HearseConfig.getBoolean("enable-async-entity",true); +- final int workerCount = HearseConfig.getInt("async-entity-worker-count",Runtime.getRuntime().availableProcessors()); ++ boolean asyncEntityEnabled1 = HearseConfig.getBoolean("optimizations.enable-async-entity",true); ++ final int workerCount = HearseConfig.getInt("workers.async-entity-worker-count",Runtime.getRuntime().availableProcessors()); + if (asyncEntityEnabled1){ + worker = new WorkerThreadPoolExecutor( + workerCount, +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 68523cb53573baa8ca98177f40acac3745cd625a..ac92d1b36590bcc491d56a1eb442477c8f6e2d11 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -1126,13 +1126,10 @@ public class ServerLevel extends Level implements WorldGenLevel { + } else { entity.inactiveTick(); } // Paper - EAR 2 + //this.getProfiler().pop(); // Purpur + //} finally { timer.stopTiming(); } // Paper - timings // Purpur +- Iterator iterator = entity.getPassengers().iterator(); + +- while (iterator.hasNext()) { +- Entity entity1 = (Entity) iterator.next(); +- +- this.tickPassenger(entity, entity1); +- } ++ for (Entity entity1 : entity.getPassengers()) { ++ this.tickPassenger(entity, entity1); ++ } + // } finally { timer.stopTiming(); } // Paper - timings - move up + // Paper start - log detailed entity tick information + } finally { +@@ -1171,11 +1168,8 @@ public class ServerLevel extends Level implements WorldGenLevel { + } + // Paper end - EAR 2 + //gameprofilerfiller.pop(); // Purpur +- Iterator iterator = passenger.getPassengers().iterator(); +- +- while (iterator.hasNext()) { +- Entity entity2 = (Entity) iterator.next(); + ++ for (Entity entity2 : passenger.getPassengers()) { + this.tickPassenger(passenger, entity2); + } + +diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +index dca7c7f83043452b5fef3c1d24a99f08dfaf242a..2a14b665437336aa32ca14fb2137d5bb400e2e42 100644 +--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java ++++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +@@ -1,28 +1,25 @@ + package net.minecraft.world.level.entity; + +-import java.util.*; +-import java.util.function.Consumer; +- + import com.google.common.collect.Lists; +-import it.unimi.dsi.fastutil.objects.ObjectArraySet; +-import it.unimi.dsi.fastutil.objects.ObjectSets; + import net.minecraft.world.entity.Entity; ++import java.util.List; ++import java.util.function.Consumer; + + public class EntityTickList { + public final List entities = Lists.newCopyOnWriteArrayList(); + + public void add(Entity entity) { + io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper +- this.entities.add(entity); // Paper - replace with better logic, do not delay removals/additions ++ this.entities.add(entity); + } + + public void remove(Entity entity) { + io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist removal"); // Paper +- this.entities.remove(entity); // Paper - replace with better logic, do not delay removals/additions ++ this.entities.remove(entity); + } + + public boolean contains(Entity entity) { +- return this.entities.contains(entity); // Paper - replace with better logic, do not delay removals/additions ++ return this.entities.contains(entity); + } + + public void forEach(Consumer action) { +@@ -30,6 +27,5 @@ public class EntityTickList { + for (Entity entity : this.entities) { + action.accept(entity); + } +- // Paper end - replace with better logic, do not delay removals/additions + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 96cde1f86ca073e7e9e5799bcb12a10adf9230b2..c0ce5a75923e9482d3bd76fbcf730e6c6cd1bbda 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1,5 +1,6 @@ + package org.bukkit.craftbukkit; + ++import co.earthme.hearse.HearseConfig; + import com.google.common.base.Charsets; + import com.google.common.base.Function; + import com.google.common.base.Preconditions; diff --git a/patches/server/0060-Hearse-Add-worker-command.patch b/patches/server/0060-Hearse-Add-worker-command.patch new file mode 100644 index 00000000..4071d25c --- /dev/null +++ b/patches/server/0060-Hearse-Add-worker-command.patch @@ -0,0 +1,302 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Wed, 11 Jan 2023 10:10:12 +0800 +Subject: [PATCH] Hearse: Add worker command + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/Hearse.java b/src/main/java/co/earthme/hearse/Hearse.java +new file mode 100644 +index 0000000000000000000000000000000000000000..79116449c221e0748e938f40366af03f93a4ab9f +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/Hearse.java +@@ -0,0 +1,25 @@ ++package co.earthme.hearse; ++ ++import co.earthme.hearse.commands.WorkerCommand; ++import co.earthme.hearse.server.ServerEntityTickHook; ++import co.earthme.hearse.workers.WorkerThreadPoolManager; ++import net.minecraft.server.MinecraftServer; ++ ++public class Hearse { ++ private static final WorkerThreadPoolManager workerManager = new WorkerThreadPoolManager(); ++ ++ public static void initAll(){ ++ HearseConfig.init(); ++ ServerEntityTickHook.init(); ++ MinecraftServer.getServer().server.getCommandMap().register("workers","hearse",new WorkerCommand()); ++ } ++ ++ public static void onServerStop(){ ++ HearseConfig.save(); ++ workerManager.shutdownAllNow(); ++ } ++ ++ public static WorkerThreadPoolManager getWorkerManager() { ++ return workerManager; ++ } ++} +diff --git a/src/main/java/co/earthme/hearse/commands/WorkerCommand.java b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9bf8e0bdfed9a30a302c6369a727e8bb394b4670 +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java +@@ -0,0 +1,72 @@ ++package co.earthme.hearse.commands; ++ ++import co.earthme.hearse.Hearse; ++import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; ++import org.bukkit.ChatColor; ++import org.bukkit.command.Command; ++import org.bukkit.command.CommandSender; ++import org.jetbrains.annotations.NotNull; ++import java.util.ArrayList; ++import java.util.List; ++import java.util.Map; ++ ++public class WorkerCommand extends Command { ++ public WorkerCommand() { ++ super("workers"); ++ this.setPermission("hearse.commands.workers"); ++ this.setDescription("You can see or edit the server workers by using this command"); ++ this.setUsage("/workers "); ++ } ++ ++ @Override ++ public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException { ++ final List ret = new ArrayList<>(); ++ if (args.length == 1){ ++ ret.add("status"); ++ ret.add("setThreadCount"); ++ ret.add("forceStop"); ++ } ++ if (args.length == 2){ ++ for (Map.Entry entry : Hearse.getWorkerManager().getManagedWorkers().entrySet()){ ++ ret.add(entry.getKey()); ++ } ++ } ++ return ret; ++ } ++ ++ @Override ++ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { ++ if (args.length >= 2){ ++ final String action = args[0]; ++ final String workerName = args[1]; ++ final WorkerThreadPoolExecutor searchedWorker = Hearse.getWorkerManager().getTargetWorker(workerName); ++ if (searchedWorker == null){ ++ sender.sendMessage(ChatColor.RED+"Target worker not found!"); ++ return true; ++ } ++ switch (action){ ++ case "status": ++ sender.sendMessage(ChatColor.GREEN+"Worker: "+workerName+" Status:"+ searchedWorker); ++ break; ++ case "setThreadCount": ++ if (args.length == 3){ ++ try { ++ searchedWorker.setCorePoolSize(Integer.parseInt(args[2])); ++ sender.sendMessage(ChatColor.GREEN+"Finished!"); ++ }catch (NumberFormatException e){ ++ sender.sendMessage(ChatColor.RED+"Please supply a integer!"); ++ } ++ }else{ ++ sender.sendMessage(ChatColor.RED+"Please supply a integer!"); ++ } ++ break; ++ case "forceStop": ++ searchedWorker.shutdownNow(); ++ sender.sendMessage(ChatColor.YELLOW+"Worker "+workerName+" has been stopped!"); ++ break; ++ } ++ return true; ++ } ++ return false; ++ } ++} +diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +index cf7ee6fda90fa0f6827dc2d1c584151e3b99fb38..18c1f6ee4d4fc422fb2aa41483ce145d34fa39b1 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +@@ -1,14 +1,13 @@ + package co.earthme.hearse.server; + ++import co.earthme.hearse.Hearse; + import co.earthme.hearse.HearseConfig; +-import co.earthme.hearse.concurrent.WorkerThread; + import co.earthme.hearse.concurrent.WorkerThreadFactory; + import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; + import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.entity.Entity; +- + import java.util.concurrent.LinkedBlockingQueue; + import java.util.concurrent.TimeUnit; + import java.util.concurrent.atomic.AtomicInteger; +@@ -27,14 +26,6 @@ public class ServerEntityTickHook { + worker.execute(task); + } + +- public static void onServerStop() throws InterruptedException { +- if (!asyncEntityEnabled){ +- return; +- } +- worker.shutdown(); +- while (!worker.awaitTermination(100,TimeUnit.MILLISECONDS)); +- } +- + public static void init(){ + boolean asyncEntityEnabled1 = HearseConfig.getBoolean("optimizations.enable-async-entity",true); + final int workerCount = HearseConfig.getInt("workers.async-entity-worker-count",Runtime.getRuntime().availableProcessors()); +@@ -47,6 +38,7 @@ public class ServerEntityTickHook { + new LinkedBlockingQueue<>(), + defFactory + ); ++ Hearse.getWorkerManager().addWorker("entity",worker); + } + asyncEntityEnabled = asyncEntityEnabled1; + } +diff --git a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java +new file mode 100644 +index 0000000000000000000000000000000000000000..90dd97491c0313bee031b81aa43fe6df3dda5b4f +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java +@@ -0,0 +1,84 @@ ++package co.earthme.hearse.workers; ++ ++import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; ++import com.google.common.collect.Maps; ++import java.util.List; ++import java.util.Map; ++import java.util.concurrent.TimeUnit; ++ ++public class WorkerThreadPoolManager { ++ private final Map managedWorkers = Maps.newConcurrentMap(); ++ ++ public void addWorker(String bound,WorkerThreadPoolExecutor worker){ ++ this.managedWorkers.put(bound,worker); ++ } ++ ++ public void shutdownAll() throws InterruptedException { ++ for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){ ++ worker.shutdown(); ++ while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {} ++ } ++ } ++ ++ public Map getManagedWorkers() { ++ return Maps.newHashMap(this.managedWorkers); ++ } ++ ++ public WorkerThreadPoolExecutor getTargetWorker(String bound){ ++ return this.managedWorkers.get(bound); ++ } ++ ++ public Map> shutdownAllNow(){ ++ final Map> ret = Maps.newHashMap(); ++ for (Map.Entry entry : this.managedWorkers.entrySet()){ ++ final String workerName = entry.getKey(); ++ final WorkerThreadPoolExecutor worker = entry.getValue(); ++ try { ++ final List taskNotRunned = worker.shutdownNow(); ++ while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){ ++ ++ } ++ ret.put(workerName,taskNotRunned); ++ }catch (Exception e){ ++ e.printStackTrace(); ++ } ++ } ++ return ret; ++ } ++ ++ public Map> shutdownAllNow(long timeOutCount){ ++ final Map> ret = Maps.newHashMap(); ++ for (Map.Entry entry : this.managedWorkers.entrySet()){ ++ final String workerName = entry.getKey(); ++ final WorkerThreadPoolExecutor worker = entry.getValue(); ++ try { ++ long timeCounter = timeOutCount; ++ final List taskNotRunned = worker.shutdownNow(); ++ while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){ ++ if (timeCounter == 0){ ++ break; ++ } ++ timeCounter--; ++ } ++ ret.put(workerName,taskNotRunned); ++ }catch (Exception e){ ++ e.printStackTrace(); ++ } ++ } ++ return ret; ++ } ++ ++ public void shutdownAll(long singleWorkerAwaitTimeOutCount) throws InterruptedException { ++ long counter = singleWorkerAwaitTimeOutCount; ++ for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){ ++ worker.shutdown(); ++ while (worker.awaitTermination(1, TimeUnit.MILLISECONDS)) { ++ if (counter == 0){ ++ break; ++ } ++ counter--; ++ } ++ counter = singleWorkerAwaitTimeOutCount; ++ } ++ } ++} +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 9c4811a9936f819e201b93e13519abf10d02ac8e..fe47aff8654283aa6a17a36226aa9fc7f18f9886 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1,5 +1,6 @@ + package net.minecraft.server; + ++import co.earthme.hearse.Hearse; + import co.earthme.hearse.HearseConfig; + import co.earthme.hearse.server.ServerEntityTickHook; + import com.google.common.base.Splitter; +@@ -399,8 +400,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop +Date: Wed, 11 Jan 2023 10:43:46 +0800 +Subject: [PATCH] Hearse: Fix some hearse bugs + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/Hearse.java b/src/main/java/co/earthme/hearse/Hearse.java +index 79116449c221e0748e938f40366af03f93a4ab9f..f8fcb07bc54f4b150dacba325d0a47f0dc7687bc 100644 +--- a/src/main/java/co/earthme/hearse/Hearse.java ++++ b/src/main/java/co/earthme/hearse/Hearse.java +@@ -16,7 +16,11 @@ public class Hearse { + + public static void onServerStop(){ + HearseConfig.save(); +- workerManager.shutdownAllNow(); ++ try { ++ workerManager.shutdownAll(); ++ } catch (InterruptedException e) { ++ e.printStackTrace(); ++ } + } + + public static WorkerThreadPoolManager getWorkerManager() { +diff --git a/src/main/java/co/earthme/hearse/commands/WorkerCommand.java b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java +index 9bf8e0bdfed9a30a302c6369a727e8bb394b4670..1a4a6869a7278beadd97af006f4b5fae578b83ed 100644 +--- a/src/main/java/co/earthme/hearse/commands/WorkerCommand.java ++++ b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java +@@ -54,10 +54,10 @@ public class WorkerCommand extends Command { + searchedWorker.setCorePoolSize(Integer.parseInt(args[2])); + sender.sendMessage(ChatColor.GREEN+"Finished!"); + }catch (NumberFormatException e){ +- sender.sendMessage(ChatColor.RED+"Please supply a integer!"); ++ sender.sendMessage(ChatColor.RED+"Please supply an integer!"); + } + }else{ +- sender.sendMessage(ChatColor.RED+"Please supply a integer!"); ++ sender.sendMessage(ChatColor.RED+"Please supply an integer!"); + } + break; + case "forceStop": +diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +index 18c1f6ee4d4fc422fb2aa41483ce145d34fa39b1..fd7912df03ae39347206fe8db2efa7a8a0e516c8 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +@@ -8,11 +8,15 @@ import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.entity.Entity; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; + import java.util.concurrent.LinkedBlockingQueue; ++import java.util.concurrent.RejectedExecutionException; + import java.util.concurrent.TimeUnit; + import java.util.concurrent.atomic.AtomicInteger; + + public class ServerEntityTickHook { ++ private static final Logger logger = LogManager.getLogger(); + private static volatile boolean firstTick = false; + private static final WorkerThreadFactory defFactory = new DefaultWorkerFactory(); + private static final AtomicInteger threadId = new AtomicInteger(); +@@ -21,7 +25,7 @@ public class ServerEntityTickHook { + + public static void executeAsyncTask(Runnable task){ + if (!asyncEntityEnabled){ +- throw new IllegalStateException(); ++ throw new RejectedExecutionException(); + } + worker.execute(task); + } +@@ -87,6 +91,11 @@ public class ServerEntityTickHook { + task.run(); + return; + } +- worker.execute(task); ++ try { ++ worker.execute(task); ++ }catch (RejectedExecutionException e){ ++ logger.warn("Worker rejected our task.Falling back to sync entity updating"); ++ asyncEntityEnabled = false; ++ } + } + } +diff --git a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java +index 90dd97491c0313bee031b81aa43fe6df3dda5b4f..b1eb341728f41c5a62e35944c4b0222758ae8eef 100644 +--- a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java ++++ b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java +@@ -15,15 +15,19 @@ public class WorkerThreadPoolManager { + + public void shutdownAll() throws InterruptedException { + for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){ +- worker.shutdown(); +- while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {} ++ if (!worker.isShutdown()){ ++ worker.shutdown(); ++ while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {} ++ } + } + } + ++ @Deprecated + public Map getManagedWorkers() { + return Maps.newHashMap(this.managedWorkers); + } + ++ @Deprecated + public WorkerThreadPoolExecutor getTargetWorker(String bound){ + return this.managedWorkers.get(bound); + } +@@ -33,14 +37,16 @@ public class WorkerThreadPoolManager { + for (Map.Entry entry : this.managedWorkers.entrySet()){ + final String workerName = entry.getKey(); + final WorkerThreadPoolExecutor worker = entry.getValue(); +- try { +- final List taskNotRunned = worker.shutdownNow(); +- while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){ ++ if (!worker.isShutdown()){ ++ try { ++ final List taskNotRunned = worker.shutdownNow(); ++ while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){ + ++ } ++ ret.put(workerName,taskNotRunned); ++ }catch (Exception e){ ++ e.printStackTrace(); + } +- ret.put(workerName,taskNotRunned); +- }catch (Exception e){ +- e.printStackTrace(); + } + } + return ret; +@@ -51,18 +57,20 @@ public class WorkerThreadPoolManager { + for (Map.Entry entry : this.managedWorkers.entrySet()){ + final String workerName = entry.getKey(); + final WorkerThreadPoolExecutor worker = entry.getValue(); +- try { +- long timeCounter = timeOutCount; +- final List taskNotRunned = worker.shutdownNow(); +- while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){ +- if (timeCounter == 0){ +- break; ++ if (!worker.isShutdown()){ ++ try { ++ long timeCounter = timeOutCount; ++ final List taskNotRunned = worker.shutdownNow(); ++ while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){ ++ if (timeCounter == 0){ ++ break; ++ } ++ timeCounter--; + } +- timeCounter--; ++ ret.put(workerName,taskNotRunned); ++ }catch (Exception e){ ++ e.printStackTrace(); + } +- ret.put(workerName,taskNotRunned); +- }catch (Exception e){ +- e.printStackTrace(); + } + } + return ret; +@@ -71,14 +79,16 @@ public class WorkerThreadPoolManager { + public void shutdownAll(long singleWorkerAwaitTimeOutCount) throws InterruptedException { + long counter = singleWorkerAwaitTimeOutCount; + for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){ +- worker.shutdown(); +- while (worker.awaitTermination(1, TimeUnit.MILLISECONDS)) { +- if (counter == 0){ +- break; ++ if (!worker.isShutdown()){ ++ worker.shutdown(); ++ while (worker.awaitTermination(1, TimeUnit.MILLISECONDS)) { ++ if (counter == 0){ ++ break; ++ } ++ counter--; + } +- counter--; ++ counter = singleWorkerAwaitTimeOutCount; + } +- counter = singleWorkerAwaitTimeOutCount; + } + } + } diff --git a/patches/server/0062-Hearse-Fix-some-concurrent-problems.patch b/patches/server/0062-Hearse-Fix-some-concurrent-problems.patch new file mode 100644 index 00000000..c181c6d4 --- /dev/null +++ b/patches/server/0062-Hearse-Fix-some-concurrent-problems.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Wed, 11 Jan 2023 11:00:38 +0800 +Subject: [PATCH] Hearse: Fix some concurrent problems + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +index 99142f749371828f6f55e4fbab03b22eb519ec1e..fc26edc5082f701e6450ca9abf78423840cd773c 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +@@ -111,14 +111,7 @@ public class GoalSelector { + } + } + +- Iterator> iterator = this.lockedFlags.entrySet().iterator(); +- +- while(iterator.hasNext()) { +- Map.Entry entry = iterator.next(); +- if (!entry.getValue().isRunning()) { +- iterator.remove(); +- } +- } ++ this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning()); + + //profilerFiller.pop(); // Purpur + //profilerFiller.push("goalUpdate"); // Purpur diff --git a/patches/server/0063-Hearse-Fix-some-concurrent-problems.patch b/patches/server/0063-Hearse-Fix-some-concurrent-problems.patch new file mode 100644 index 00000000..f44f80d4 --- /dev/null +++ b/patches/server/0063-Hearse-Fix-some-concurrent-problems.patch @@ -0,0 +1,67 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Wed, 11 Jan 2023 16:58:19 +0800 +Subject: [PATCH] Hearse: Fix some concurrent problems + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java +index 26e1b4060f2a93cb659170f83e6ce64086e0eb0c..d6951b05128fea7eb5f1b40837cea77e0c209165 100644 +--- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java ++++ b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java +@@ -423,8 +423,10 @@ public final class EntityLookup implements LevelEntityGetter { + final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ); + final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ); + +- if (!old.removeEntity(entity, entity.sectionY)) { +- LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section"); ++ if (old!=null){ ++ if (!old.removeEntity(entity, entity.sectionY)) { ++ LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section"); ++ } + } + + if (!slices.addEntity(entity, newSectionY)) { +diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java +index 8db20db72cd51046213625fac46c35854c59ec5d..4d40526cc90c19ff5a1569c8d6d828a0d0b73ccb 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java ++++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java +@@ -3,6 +3,7 @@ package net.minecraft.world.entity.ai.sensing; + import com.google.common.collect.ImmutableSet; + import com.mojang.datafixers.util.Pair; + import it.unimi.dsi.fastutil.longs.Long2LongMap; ++import it.unimi.dsi.fastutil.longs.Long2LongMaps; + import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; + import java.util.Optional; + import java.util.Set; +@@ -23,7 +24,7 @@ public class NearestBedSensor extends Sensor { + private static final int CACHE_TIMEOUT = 40; + private static final int BATCH_SIZE = 5; + private static final int RATE = 20; +- private final Long2LongMap batchCache = new Long2LongOpenHashMap(); ++ private final Long2LongMap batchCache = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); + private int triedCount; + private long lastUpdate; + +diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java +index 9babe636176da3c40598eb5bdac0919a1704eaa0..58c6b1f67aedf5ab2167fd070604fc0d8f710435 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java ++++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java +@@ -2,13 +2,14 @@ package net.minecraft.world.entity.ai.sensing; + + import it.unimi.dsi.fastutil.ints.IntOpenHashSet; + import it.unimi.dsi.fastutil.ints.IntSet; ++import it.unimi.dsi.fastutil.ints.IntSets; + import net.minecraft.world.entity.Entity; + import net.minecraft.world.entity.Mob; + + public class Sensing { + private final Mob mob; +- private final IntSet seen = new IntOpenHashSet(); +- private final IntSet unseen = new IntOpenHashSet(); ++ private final IntSet seen = IntSets.synchronize(new IntOpenHashSet()); ++ private final IntSet unseen = IntSets.synchronize(new IntOpenHashSet()); + + public Sensing(Mob owner) { + this.mob = owner; diff --git a/patches/server/0064-Hearse-Fix-some-concurrent-problems-in-LivingEntity.patch b/patches/server/0064-Hearse-Fix-some-concurrent-problems-in-LivingEntity.patch new file mode 100644 index 00000000..325301ee --- /dev/null +++ b/patches/server/0064-Hearse-Fix-some-concurrent-problems-in-LivingEntity.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Wed, 11 Jan 2023 20:24:40 +0800 +Subject: [PATCH] Hearse: Fix some concurrent problems in LivingEntity + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 93c32dd39693b37efaa05af0486e1bdd298661f3..cdb33a430d0d1671899ab8bb0911193a5688af23 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -17,6 +17,7 @@ import java.util.List; + import java.util.Map; + import java.util.Optional; + import java.util.UUID; ++import java.util.concurrent.CopyOnWriteArrayList; + import java.util.function.Predicate; + import javax.annotation.Nullable; + import net.minecraft.BlockUtil; +@@ -181,7 +182,7 @@ public abstract class LivingEntity extends Entity { + public static final float EXTRA_RENDER_CULLING_SIZE_WITH_BIG_HAT = 0.5F; + private final AttributeMap attributes; + public CombatTracker combatTracker = new CombatTracker(this); +- public final Map activeEffects = Maps.newHashMap(); ++ public final Map activeEffects = Maps.newConcurrentMap(); + private final NonNullList lastHandItemStacks; + private final NonNullList lastArmorItemStacks; + public boolean swinging; +@@ -257,7 +258,7 @@ public abstract class LivingEntity extends Entity { + // CraftBukkit start + public int expToDrop; + public boolean forceDrops; +- public ArrayList drops = new ArrayList(); ++ public List drops = new CopyOnWriteArrayList<>(); + public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes; + public boolean collides = true; + public Set collidableExemptions = new HashSet<>(); +@@ -875,7 +876,7 @@ public abstract class LivingEntity extends Entity { + + // CraftBukkit start + private boolean isTickingEffects = false; +- private List effectsToProcess = Lists.newArrayList(); ++ private List effectsToProcess = Lists.newCopyOnWriteArrayList(); + + private static class ProcessableEffect { + +@@ -1779,7 +1780,7 @@ public abstract class LivingEntity extends Entity { + } + }); // Paper end + this.postDeathDropItems(deathEvent); // Paper +- this.drops = new ArrayList<>(); ++ this.drops = new CopyOnWriteArrayList<>(); + // CraftBukkit end + + // this.dropInventory();// CraftBukkit - moved up diff --git a/patches/server/0065-Hearse-Don-t-wait-async-tasks-when-stopping-and-chan.patch b/patches/server/0065-Hearse-Don-t-wait-async-tasks-when-stopping-and-chan.patch new file mode 100644 index 00000000..d7651515 --- /dev/null +++ b/patches/server/0065-Hearse-Don-t-wait-async-tasks-when-stopping-and-chan.patch @@ -0,0 +1,126 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Wed, 11 Jan 2023 21:55:57 +0800 +Subject: [PATCH] Hearse: Don't wait async tasks when stopping and change + something in CraftEventFactory + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/Hearse.java b/src/main/java/co/earthme/hearse/Hearse.java +index f8fcb07bc54f4b150dacba325d0a47f0dc7687bc..79116449c221e0748e938f40366af03f93a4ab9f 100644 +--- a/src/main/java/co/earthme/hearse/Hearse.java ++++ b/src/main/java/co/earthme/hearse/Hearse.java +@@ -16,11 +16,7 @@ public class Hearse { + + public static void onServerStop(){ + HearseConfig.save(); +- try { +- workerManager.shutdownAll(); +- } catch (InterruptedException e) { +- e.printStackTrace(); +- } ++ workerManager.shutdownAllNow(); + } + + public static WorkerThreadPoolManager getWorkerManager() { +diff --git a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java +index 3e3ae10fcc54b80ff4ec433f136d15d3b9fa4fe4..443ac5267245c20830692b37802afd6ebdf8813b 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java ++++ b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java +@@ -9,12 +9,17 @@ public class DefaultWorkerFactory implements WorkerThreadFactory { + private static final AtomicInteger poolId = new AtomicInteger(); + private final AtomicInteger threadId = new AtomicInteger(); + ++ public DefaultWorkerFactory(){ ++ poolId.getAndIncrement(); ++ } ++ + @Override + public WorkerThread getNewThread(Runnable task) { +- final WorkerThread workerThread = new WorkerThread(task,"pool-"+poolId.getAndIncrement()+"-worker-"+threadId.getAndIncrement()); ++ final WorkerThread workerThread = new WorkerThread(task,"pool-"+poolId.get()+"-worker-"+threadId.getAndIncrement()); + if (workerThread.isDaemon()){ + workerThread.setDaemon(false); + } ++ workerThread.setDaemon(true); + workerThread.setPriority(Thread.NORM_PRIORITY - 2); + workerThread.setContextClassLoader(MinecraftServer.class.getClassLoader()); + return workerThread; +diff --git a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java +index b1eb341728f41c5a62e35944c4b0222758ae8eef..527dba288e1988773fd5a89f076f92084034f421 100644 +--- a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java ++++ b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java +@@ -16,6 +16,7 @@ public class WorkerThreadPoolManager { + public void shutdownAll() throws InterruptedException { + for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){ + if (!worker.isShutdown()){ ++ worker.getQueue().clear(); //Clear the tasks.We don't need wait them + worker.shutdown(); + while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {} + } +@@ -40,33 +41,6 @@ public class WorkerThreadPoolManager { + if (!worker.isShutdown()){ + try { + final List taskNotRunned = worker.shutdownNow(); +- while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){ +- +- } +- ret.put(workerName,taskNotRunned); +- }catch (Exception e){ +- e.printStackTrace(); +- } +- } +- } +- return ret; +- } +- +- public Map> shutdownAllNow(long timeOutCount){ +- final Map> ret = Maps.newHashMap(); +- for (Map.Entry entry : this.managedWorkers.entrySet()){ +- final String workerName = entry.getKey(); +- final WorkerThreadPoolExecutor worker = entry.getValue(); +- if (!worker.isShutdown()){ +- try { +- long timeCounter = timeOutCount; +- final List taskNotRunned = worker.shutdownNow(); +- while (worker.awaitTermination(1,TimeUnit.MILLISECONDS)){ +- if (timeCounter == 0){ +- break; +- } +- timeCounter--; +- } + ret.put(workerName,taskNotRunned); + }catch (Exception e){ + e.printStackTrace(); +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 6a52ae70b5f7fd9953b6b2605cae722f606e7fec..af5956bd57141cae08fe839bb2176988a48cd9b8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -57,6 +57,7 @@ import net.minecraft.world.phys.BlockHitResult; + import net.minecraft.world.phys.EntityHitResult; + import net.minecraft.world.phys.HitResult; + import net.minecraft.world.phys.Vec3; ++import org.apache.logging.log4j.LogManager; + import org.bukkit.Bukkit; + import org.bukkit.Location; // Paper + import org.bukkit.Material; +@@ -229,7 +230,7 @@ public class CraftEventFactory { + public static final DamageSource MELTING = CraftDamageSource.copyOf(DamageSource.ON_FIRE); + public static final DamageSource POISON = CraftDamageSource.copyOf(DamageSource.MAGIC); + public static org.bukkit.block.Block blockDamage; // For use in EntityDamageByBlockEvent +- public static Entity entityDamage; // For use in EntityDamageByEntityEvent ++ public static volatile Entity entityDamage; // For use in EntityDamageByEntityEvent + + // helper methods + private static boolean canBuild(ServerLevel world, Player player, int x, int z) { +@@ -1078,7 +1079,8 @@ public class CraftEventFactory { + } else if (source == DamageSource.MAGIC) { + cause = DamageCause.MAGIC; + } else { +- throw new IllegalStateException(String.format("Unhandled damage of %s by %s from %s", entity, damager.getHandle(), source.msgId)); ++ LogManager.getLogger().error(String.format("Unhandled damage of %s by %s from %s", entity, damager.getHandle(), source.msgId)); ++ cause = DamageCause.CUSTOM; + } + EntityDamageEvent event = new EntityDamageByEntityEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions, source.isCritical()); // Paper - add critical damage API + event.setCancelled(cancelled); diff --git a/patches/server/0066-Hearse-Parallel-world-ticking.patch b/patches/server/0066-Hearse-Parallel-world-ticking.patch new file mode 100644 index 00000000..5a776ea3 --- /dev/null +++ b/patches/server/0066-Hearse-Parallel-world-ticking.patch @@ -0,0 +1,223 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Thu, 12 Jan 2023 13:04:01 +0800 +Subject: [PATCH] Hearse: Parallel world ticking + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java +index 443ac5267245c20830692b37802afd6ebdf8813b..c26511c26bd02320a55a01168f342b4b051ffdfd 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java ++++ b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java +@@ -8,14 +8,16 @@ import java.util.concurrent.atomic.AtomicInteger; + public class DefaultWorkerFactory implements WorkerThreadFactory { + private static final AtomicInteger poolId = new AtomicInteger(); + private final AtomicInteger threadId = new AtomicInteger(); ++ private final String bound; + +- public DefaultWorkerFactory(){ ++ public DefaultWorkerFactory(String bound){ + poolId.getAndIncrement(); ++ this.bound = bound; + } + + @Override + public WorkerThread getNewThread(Runnable task) { +- final WorkerThread workerThread = new WorkerThread(task,"pool-"+poolId.get()+"-worker-"+threadId.getAndIncrement()); ++ final WorkerThread workerThread = new WorkerThread(task,"pool-"+poolId.get()+"-worker-"+threadId.getAndIncrement()+"-bound-"+this.bound); + if (workerThread.isDaemon()){ + workerThread.setDaemon(false); + } +diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +index fd7912df03ae39347206fe8db2efa7a8a0e516c8..9d26ff7d07f1e972f1720f5b2d0e66d4c9c3f1e5 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +@@ -18,7 +18,7 @@ import java.util.concurrent.atomic.AtomicInteger; + public class ServerEntityTickHook { + private static final Logger logger = LogManager.getLogger(); + private static volatile boolean firstTick = false; +- private static final WorkerThreadFactory defFactory = new DefaultWorkerFactory(); ++ private static final WorkerThreadFactory defFactory = new DefaultWorkerFactory("entity"); + private static final AtomicInteger threadId = new AtomicInteger(); + private static WorkerThreadPoolExecutor worker; + private static boolean asyncEntityEnabled; +diff --git a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5670fdae5d16cbbdf605df048ae253208e49a82c +--- /dev/null ++++ b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java +@@ -0,0 +1,70 @@ ++package co.earthme.hearse.server; ++ ++import co.earthme.hearse.Hearse; ++import co.earthme.hearse.HearseConfig; ++import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; ++import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory; ++import net.minecraft.CrashReport; ++import net.minecraft.ReportedException; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.level.ServerLevel; ++ ++import java.util.concurrent.LinkedBlockingQueue; ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.atomic.AtomicInteger; ++import java.util.concurrent.locks.LockSupport; ++import java.util.function.BooleanSupplier; ++ ++public class ServerLevelTickHook { ++ private static final DefaultWorkerFactory workerFactory = new DefaultWorkerFactory("world"); ++ private static WorkerThreadPoolExecutor worker; ++ private static boolean enabledParaWorld; ++ private static volatile boolean inited = false; ++ private static final AtomicInteger activeTaskCount = new AtomicInteger(); ++ ++ public static void initWorker(){ ++ enabledParaWorld = HearseConfig.getBoolean("optimizations.enableparallelworldtick",true); ++ if (enabledParaWorld){ ++ worker = new WorkerThreadPoolExecutor( ++ MinecraftServer.getServer().levels.size(), ++ MinecraftServer.getServer().levels.size(), ++ 0, ++ TimeUnit.MILLISECONDS, ++ new LinkedBlockingQueue<>(), ++ workerFactory ++ ); ++ worker.prestartAllCoreThreads(); ++ Hearse.getWorkerManager().addWorker("world",worker); ++ } ++ inited = true; ++ } ++ ++ public static boolean isInited(){ ++ return inited; ++ } ++ ++ public static void callWorldTick(ServerLevel worldserver, BooleanSupplier shouldKeepTicking){ ++ activeTaskCount.getAndIncrement(); ++ worker.execute(()->{ ++ try { ++ try { ++ worldserver.tick(shouldKeepTicking); ++ for (final io.papermc.paper.chunk.SingleThreadChunkRegionManager regionManager : worldserver.getChunkSource().chunkMap.regionManagers) { ++ regionManager.recalculateRegions(); ++ } ++ } catch (Throwable throwable) { ++ throwable.printStackTrace(); ++ } ++ worldserver.explosionDensityCache.clear(); ++ }finally { ++ activeTaskCount.getAndDecrement(); ++ } ++ }); ++ } ++ ++ public static void awaitWorldTicKTasks(){ ++ while (activeTaskCount.get() > 0){ ++ LockSupport.parkNanos("Await world ticking",1000000); ++ } ++ } ++} +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index fe47aff8654283aa6a17a36226aa9fc7f18f9886..57bae261ee1d6db734b38dd5f67dbce98c41fc1c 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -3,6 +3,7 @@ package net.minecraft.server; + import co.earthme.hearse.Hearse; + import co.earthme.hearse.HearseConfig; + import co.earthme.hearse.server.ServerEntityTickHook; ++import co.earthme.hearse.server.ServerLevelTickHook; + import com.google.common.base.Splitter; + import com.google.common.collect.ImmutableList; + import com.google.common.collect.Lists; +@@ -223,7 +224,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop registries; +- private Map, ServerLevel> levels; ++ public Map, ServerLevel> levels; + private PlayerList playerList; + private volatile boolean running; + private volatile boolean isRestarting = false; // Paper - flag to signify we're attempting to restart +@@ -1121,6 +1122,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper +- worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper ++ for (ServerLevel worldserver : this.getAllLevels()) { ++ worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper ++ worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper + net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper + + /*this.profiler.push(() -> { // Purpur +@@ -1534,35 +1534,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions ++ public final Map explosionDensityCache = Maps.newConcurrentMap(); // Paper - Optimize explosions + public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here + + // Paper start - fix and optimise world upgrading diff --git a/patches/server/0067-Hearse-Fix-some-concurrent-problems-in-Entity.java.patch b/patches/server/0067-Hearse-Fix-some-concurrent-problems-in-Entity.java.patch new file mode 100644 index 00000000..c2608dce --- /dev/null +++ b/patches/server/0067-Hearse-Fix-some-concurrent-problems-in-Entity.java.patch @@ -0,0 +1,39 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Thu, 12 Jan 2023 13:57:52 +0800 +Subject: [PATCH] Hearse: Fix some concurrent problems in Entity.java + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index f33476a35706d7a236fe3bb178d166d568c07674..483c19ac24b074cbdb924d684c0892ddc546af3e 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -24,6 +24,8 @@ import java.util.function.BiConsumer; + import java.util.function.Predicate; + import java.util.stream.Stream; + import javax.annotation.Nullable; ++ ++import it.unimi.dsi.fastutil.objects.Object2DoubleMaps; + import net.minecraft.BlockUtil; + import net.minecraft.CrashReport; + import net.minecraft.CrashReportCategory; +@@ -511,14 +513,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + this.nextStep = 1.0F; + this.random = SHARED_RANDOM; // Paper + this.remainingFireTicks = -this.getFireImmuneTicks(); +- this.fluidHeight = new Object2DoubleArrayMap(2); +- this.fluidOnEyes = new HashSet(); ++ this.fluidHeight = Object2DoubleMaps.synchronize(new Object2DoubleArrayMap(2)); ++ this.fluidOnEyes = Sets.newConcurrentHashSet(); + this.firstTick = true; + this.levelCallback = EntityInLevelCallback.NULL; + this.packetPositionCodec = new VecDeltaCodec(); + this.uuid = Mth.createInsecureUUID(this.random); + this.stringUUID = this.uuid.toString(); +- this.tags = Sets.newHashSet(); ++ this.tags = Sets.newConcurrentHashSet(); + this.pistonDeltas = new double[]{0.0D, 0.0D, 0.0D}; + this.feetBlockState = null; + this.type = type; diff --git a/patches/server/0068-Hearse-Print-world-worker-thread-names-when-server-s.patch b/patches/server/0068-Hearse-Print-world-worker-thread-names-when-server-s.patch new file mode 100644 index 00000000..6a36fa67 --- /dev/null +++ b/patches/server/0068-Hearse-Print-world-worker-thread-names-when-server-s.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Thu, 12 Jan 2023 13:58:20 +0800 +Subject: [PATCH] Hearse: Print world worker thread names when server started + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java +index c26511c26bd02320a55a01168f342b4b051ffdfd..03a29509821a17faac2dc8ab810a2693b03bfbc6 100644 +--- a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java ++++ b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java +@@ -2,25 +2,38 @@ package co.earthme.hearse.concurrent.threadfactory; + + import co.earthme.hearse.concurrent.WorkerThread; + import co.earthme.hearse.concurrent.WorkerThreadFactory; ++import it.unimi.dsi.fastutil.objects.ObjectArrayList; ++import it.unimi.dsi.fastutil.objects.ObjectLists; + import net.minecraft.server.MinecraftServer; ++ ++import java.util.List; + import java.util.concurrent.atomic.AtomicInteger; + + public class DefaultWorkerFactory implements WorkerThreadFactory { + private static final AtomicInteger poolId = new AtomicInteger(); + private final AtomicInteger threadId = new AtomicInteger(); + private final String bound; ++ private final List createdThreads = ObjectLists.synchronize(new ObjectArrayList<>()); + + public DefaultWorkerFactory(String bound){ + poolId.getAndIncrement(); + this.bound = bound; + } + ++ public List getCreatedThreads() { ++ return this.createdThreads; ++ } ++ + @Override + public WorkerThread getNewThread(Runnable task) { +- final WorkerThread workerThread = new WorkerThread(task,"pool-"+poolId.get()+"-worker-"+threadId.getAndIncrement()+"-bound-"+this.bound); +- if (workerThread.isDaemon()){ +- workerThread.setDaemon(false); +- } ++ final WorkerThread workerThread = new WorkerThread(()->{ ++ try { ++ task.run(); ++ }finally { ++ this.createdThreads.remove(Thread.currentThread()); ++ } ++ },"pool-"+poolId.get()+"-worker-"+threadId.getAndIncrement()+"-bound-"+this.bound); ++ this.createdThreads.add(workerThread); + workerThread.setDaemon(true); + workerThread.setPriority(Thread.NORM_PRIORITY - 2); + workerThread.setContextClassLoader(MinecraftServer.class.getClassLoader()); +diff --git a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java +index 5670fdae5d16cbbdf605df048ae253208e49a82c..8085eb700d8e5c20ebb5bfeceb78198c6e973019 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java +@@ -2,12 +2,15 @@ package co.earthme.hearse.server; + + import co.earthme.hearse.Hearse; + import co.earthme.hearse.HearseConfig; ++import co.earthme.hearse.concurrent.WorkerThread; + import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; + import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory; + import net.minecraft.CrashReport; + import net.minecraft.ReportedException; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerLevel; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; + + import java.util.concurrent.LinkedBlockingQueue; + import java.util.concurrent.TimeUnit; +@@ -21,6 +24,7 @@ public class ServerLevelTickHook { + private static boolean enabledParaWorld; + private static volatile boolean inited = false; + private static final AtomicInteger activeTaskCount = new AtomicInteger(); ++ private static final Logger logger = LogManager.getLogger(); + + public static void initWorker(){ + enabledParaWorld = HearseConfig.getBoolean("optimizations.enableparallelworldtick",true); +@@ -28,13 +32,17 @@ public class ServerLevelTickHook { + worker = new WorkerThreadPoolExecutor( + MinecraftServer.getServer().levels.size(), + MinecraftServer.getServer().levels.size(), +- 0, ++ Long.MAX_VALUE, + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + workerFactory + ); ++ worker.allowCoreThreadTimeOut(true); + worker.prestartAllCoreThreads(); + Hearse.getWorkerManager().addWorker("world",worker); ++ for (Thread worker : workerFactory.getCreatedThreads()){ ++ logger.warn("World worker name:{}.This can help you to slove the lag problems when you using parallel world ticking",worker.getName()); ++ } + } + inited = true; + } diff --git a/patches/server/0069-Hearse-Fix-some-NPE-errors.patch b/patches/server/0069-Hearse-Fix-some-NPE-errors.patch new file mode 100644 index 00000000..dd8d87df --- /dev/null +++ b/patches/server/0069-Hearse-Fix-some-NPE-errors.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Thu, 12 Jan 2023 16:08:30 +0800 +Subject: [PATCH] Hearse: Fix some NPE errors + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index cdb33a430d0d1671899ab8bb0911193a5688af23..c72920a17178059a29d21e96aeef398f6e0bbbdc 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -1964,6 +1964,10 @@ public abstract class LivingEntity extends Entity { + BlockPos blockposition = this.blockPosition(); + BlockState iblockdata = this.getFeetBlockState(); + ++ if (iblockdata == null){ ++ return false; ++ } ++ + if (iblockdata.is(BlockTags.CLIMBABLE)) { + this.lastClimbablePos = Optional.of(blockposition); + return true; +diff --git a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java +index 0c308e12f9b590fa169babac487c8adc7e3f823c..b61ed4d03848f86ca5e93b0374bbf4ca05369ad2 100644 +--- a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java ++++ b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java +@@ -5,12 +5,16 @@ import com.google.common.collect.Sets; + + import java.util.*; + ++import it.unimi.dsi.fastutil.objects.ObjectArrayList; ++import it.unimi.dsi.fastutil.objects.ObjectList; ++import it.unimi.dsi.fastutil.objects.ObjectLists; ++import net.himeki.mcmtfabric.parallelised.ConcurrentDoublyLinkedList; + import net.minecraft.network.protocol.game.DebugPackets; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.phys.Vec3; + + public class EuclideanGameEventListenerRegistry implements GameEventListenerRegistry { +- private final List listeners = Collections.synchronizedList(Lists.newArrayList()); ++ private final List listeners = new ConcurrentDoublyLinkedList<>(); + private final Set listenersToRemove = Sets.newConcurrentHashSet(); + private final List listenersToAdd = Lists.newCopyOnWriteArrayList(); + private boolean processing; diff --git a/patches/server/0070-Hearse-Move-player-ticking-to-main-thread-and-add-lo.patch b/patches/server/0070-Hearse-Move-player-ticking-to-main-thread-and-add-lo.patch new file mode 100644 index 00000000..48f00062 --- /dev/null +++ b/patches/server/0070-Hearse-Move-player-ticking-to-main-thread-and-add-lo.patch @@ -0,0 +1,585 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Thu, 12 Jan 2023 19:43:48 +0800 +Subject: [PATCH] Hearse: Move player ticking to main thread and add lock in + ChunkEntitySlices + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +index 9d26ff7d07f1e972f1720f5b2d0e66d4c9c3f1e5..86f8afd54c0cbb449403c3f43e6880ade13cfecc 100644 +--- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java ++++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java +@@ -8,6 +8,7 @@ import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.entity.Entity; ++import net.minecraft.world.entity.player.Player; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + import java.util.concurrent.LinkedBlockingQueue; +@@ -87,7 +88,7 @@ public class ServerEntityTickHook { + } + } + }; +- if (!asyncEntityEnabled){ ++ if (!asyncEntityEnabled || entity instanceof Player){ + task.run(); + return; + } +diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +index ae22ca9ea5fd3d78d8c5bf9f1ab96f1129fddc11..1012b8d1d192a946b0982c88c12a0fc0e6051972 100644 +--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java ++++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +@@ -33,7 +33,7 @@ public final class ChunkEntitySlices { + public final int chunkX; + public final int chunkZ; + protected final ServerLevel world; +- ++ protected final StampedLock accessLock = new StampedLock(); //Hearse -- fix some entity can't be removed + protected final EntityCollectionBySection allEntities; + protected final EntityCollectionBySection hardCollidingEntities; + protected final Reference2ObjectMap, EntityCollectionBySection> entitiesByClass; +@@ -70,68 +70,82 @@ public final class ChunkEntitySlices { + + // Paper start - optimise CraftChunk#getEntities + public org.bukkit.entity.Entity[] getChunkEntities() { +- List ret = new java.util.ArrayList<>(); +- final Entity[] entities = this.entities.getRawData(); +- for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { +- final Entity entity = entities[i]; +- if (entity == null) { +- continue; +- } +- final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity(); +- if (bukkit != null && bukkit.isValid()) { +- ret.add(bukkit); ++ final long id = this.accessLock.readLock(); ++ try { ++ List ret = new java.util.ArrayList<>(); ++ final Entity[] entities = this.entities.getRawData(); ++ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { ++ final Entity entity = entities[i]; ++ if (entity == null) { ++ continue; ++ } ++ final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity(); ++ if (bukkit != null && bukkit.isValid()) { ++ ret.add(bukkit); ++ } + } ++ return ret.toArray(new org.bukkit.entity.Entity[0]); ++ } finally { ++ this.accessLock.unlockRead(id); + } +- +- return ret.toArray(new org.bukkit.entity.Entity[0]); + } + + public CompoundTag save() { +- final int len = this.entities.size(); +- if (len == 0) { +- return null; +- } ++ final long id = this.accessLock.readLock(); ++ try { ++ final int len = this.entities.size(); ++ if (len == 0) { ++ return null; ++ } + +- final Entity[] rawData = this.entities.getRawData(); +- final List collectedEntities = new ArrayList<>(len); +- for (int i = 0; i < len; ++i) { +- final Entity entity = rawData[i]; +- if (entity.shouldBeSaved()) { +- collectedEntities.add(entity); ++ final Entity[] rawData = this.entities.getRawData(); ++ final List collectedEntities = new ArrayList<>(len); ++ for (int i = 0; i < len; ++i) { ++ final Entity entity = rawData[i]; ++ if (entity.shouldBeSaved()) { ++ collectedEntities.add(entity); ++ } + } +- } + +- if (collectedEntities.isEmpty()) { +- return null; +- } ++ if (collectedEntities.isEmpty()) { ++ return null; ++ } + +- return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world); ++ return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + } + + // returns true if this chunk has transient entities remaining + public boolean unload() { +- final int len = this.entities.size(); +- final Entity[] collectedEntities = Arrays.copyOf(this.entities.getRawData(), len); +- +- for (int i = 0; i < len; ++i) { +- final Entity entity = collectedEntities[i]; +- if (entity.isRemoved()) { +- // removed by us below +- continue; +- } +- if (entity.shouldBeSaved()) { +- entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK); +- if (entity.isVehicle()) { +- // we cannot assume that these entities are contained within this chunk, because entities can +- // desync - so we need to remove them all +- for (final Entity passenger : entity.getIndirectPassengers()) { +- passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK); ++ long id = this.accessLock.readLock(); ++ try { ++ final int len = this.entities.size(); ++ final Entity[] collectedEntities = Arrays.copyOf(this.entities.getRawData(), len); ++ ++ for (int i = 0; i < len; ++i) { ++ final Entity entity = collectedEntities[i]; ++ if (entity.isRemoved()) { ++ // removed by us below ++ continue; ++ } ++ if (entity.shouldBeSaved()) { ++ entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK); ++ if (entity.isVehicle()) { ++ // we cannot assume that these entities are contained within this chunk, because entities can ++ // desync - so we need to remove them all ++ for (final Entity passenger : entity.getIndirectPassengers()) { ++ passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK); ++ } + } + } + } +- } + +- return this.entities.size() != 0; ++ return this.entities.size() != 0; ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + } + + private List getAllEntities() { +@@ -142,42 +156,65 @@ public final class ChunkEntitySlices { + + final Entity[] rawData = this.entities.getRawData(); + final List collectedEntities = new ArrayList<>(len); +- for (int i = 0; i < len; ++i) { +- collectedEntities.add(rawData[i]); +- } ++ collectedEntities.addAll(Arrays.asList(rawData).subList(0, len)); + + return collectedEntities; + } + + public void callEntitiesLoadEvent() { +- CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities()); ++ final long id = this.accessLock.readLock(); ++ try { ++ CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities()); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + } + + public void callEntitiesUnloadEvent() { +- CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities()); ++ final long id = this.accessLock.readLock(); ++ try { ++ CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities()); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + } + // Paper end - optimise CraftChunk#getEntities + + public boolean isEmpty() { +- return this.entities.size() == 0; ++ final long id = this.accessLock.readLock(); ++ try { ++ return this.entities.size() == 0; ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + } + + public void mergeInto(final ChunkEntitySlices slices) { +- final Entity[] entities = this.entities.getRawData(); +- for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { +- final Entity entity = entities[i]; +- slices.addEntity(entity, entity.sectionY); ++ final long id = this.accessLock.readLock(); ++ try { ++ final Entity[] entities = this.entities.getRawData(); ++ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { ++ final Entity entity = entities[i]; ++ slices.addEntity(entity, entity.sectionY); ++ } ++ } finally { ++ this.accessLock.unlockRead(id); + } + } + + public void updateStatus(final ChunkHolder.FullChunkStatus status, final EntityLookup lookup) { + this.status = status; + +- final Entity[] entities = this.entities.getRawData(); ++ Entity[] entities; + +- for (int i = 0, size = this.entities.size(); i < size; ++i) { +- final Entity entity = entities[i]; ++ final long id = this.accessLock.readLock(); ++ try { ++ entities = Arrays.copyOf(this.entities.getRawData(), this.entities.getRawData().length); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + ++ for (final Entity entity : entities) { + final Visibility oldVisibility = EntityLookup.getEntityStatus(entity); + entity.chunkStatus = status; + final Visibility newVisibility = EntityLookup.getEntityStatus(entity); +@@ -187,70 +224,95 @@ public final class ChunkEntitySlices { + } + + public boolean addEntity(final Entity entity, final int chunkSection) { +- if (!this.entities.add(entity)) { +- return false; +- } +- entity.chunkStatus = this.status; +- final int sectionIndex = chunkSection - this.minSection; +- +- this.allEntities.addEntity(entity, sectionIndex); ++ long id = this.accessLock.writeLock(); ++ try { ++ if (!this.entities.add(entity)) { ++ return false; ++ } ++ entity.chunkStatus = this.status; ++ final int sectionIndex = chunkSection - this.minSection; + +- if (entity.hardCollides()) { +- this.hardCollidingEntities.addEntity(entity, sectionIndex); +- } ++ this.allEntities.addEntity(entity, sectionIndex); + +- for (final Iterator, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { +- final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); ++ if (entity.hardCollides()) { ++ this.hardCollidingEntities.addEntity(entity, sectionIndex); ++ } + +- if (entry.getKey().isInstance(entity)) { +- entry.getValue().addEntity(entity, sectionIndex); ++ for (final Reference2ObjectMap.Entry, EntityCollectionBySection> entry : this.entitiesByClass.reference2ObjectEntrySet()) { ++ if (entry.getKey().isInstance(entity)) { ++ entry.getValue().addEntity(entity, sectionIndex); ++ } + } ++ } finally { ++ this.accessLock.unlockWrite(id); + } +- + return true; + } + + public boolean removeEntity(final Entity entity, final int chunkSection) { +- if (!this.entities.remove(entity)) { +- return false; +- } +- entity.chunkStatus = null; +- final int sectionIndex = chunkSection - this.minSection; +- +- this.allEntities.removeEntity(entity, sectionIndex); ++ long id = this.accessLock.writeLock(); ++ try { ++ if (!this.entities.remove(entity)) { ++ return false; ++ } ++ entity.chunkStatus = null; ++ final int sectionIndex = chunkSection - this.minSection; + +- if (entity.hardCollides()) { +- this.hardCollidingEntities.removeEntity(entity, sectionIndex); +- } ++ this.allEntities.removeEntity(entity, sectionIndex); + +- for (final Iterator, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { +- final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); ++ if (entity.hardCollides()) { ++ this.hardCollidingEntities.removeEntity(entity, sectionIndex); ++ } + +- if (entry.getKey().isInstance(entity)) { +- entry.getValue().removeEntity(entity, sectionIndex); ++ for (final Reference2ObjectMap.Entry, EntityCollectionBySection> entry : this.entitiesByClass.reference2ObjectEntrySet()) { ++ if (entry.getKey().isInstance(entity)) { ++ entry.getValue().removeEntity(entity, sectionIndex); ++ } + } ++ } finally { ++ this.accessLock.unlockWrite(id); + } +- + return true; + } + + public void getHardCollidingEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { +- this.hardCollidingEntities.getEntities(except, box, into, predicate); ++ final long id = this.accessLock.readLock(); ++ try { ++ this.hardCollidingEntities.getEntities(except, box, into, predicate); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + } + + public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { +- this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate); ++ final long id = this.accessLock.readLock(); ++ try { ++ this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } ++ + } + + public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List into, final Predicate predicate) { +- this.allEntities.getEntities(except, box, into, predicate); ++ final long id = this.accessLock.readLock(); ++ try { ++ this.allEntities.getEntities(except, box, into, predicate); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } ++ + } + + public void getEntities(final EntityType type, final AABB box, final List into, + final Predicate predicate) { +- this.allEntities.getEntities(type, box, (List)into, (Predicate)predicate); ++ final long id = this.accessLock.readLock(); ++ try { ++ this.allEntities.getEntities(type, box, (List) into, (Predicate) predicate); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } ++ + } + + protected EntityCollectionBySection initClass(final Class clazz) { +@@ -278,12 +340,17 @@ public final class ChunkEntitySlices { + + public void getEntities(final Class clazz, final Entity except, final AABB box, final List into, + final Predicate predicate) { +- EntityCollectionBySection collection = this.entitiesByClass.get(clazz); +- if (collection != null) { +- collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate); +- } else { +- this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz)); +- collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate); ++ final long id = this.accessLock.readLock(); ++ try { ++ EntityCollectionBySection collection = this.entitiesByClass.get(clazz); ++ if (collection != null) { ++ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate); ++ } else { ++ this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz)); ++ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate); ++ } ++ } finally { ++ this.accessLock.unlockRead(id); + } + } + +@@ -300,7 +367,7 @@ public final class ChunkEntitySlices { + } + + public BasicEntityList(final int cap) { +- this.storage = (E[])(cap <= 0 ? EMPTY : new Entity[cap]); ++ this.storage = (E[]) (cap <= 0 ? EMPTY : new Entity[cap]); + } + + public synchronized boolean isEmpty() { +@@ -313,7 +380,7 @@ public final class ChunkEntitySlices { + + private void resize() { + if (this.storage == EMPTY) { +- this.storage = (E[])new Entity[DEFAULT_CAPACITY]; ++ this.storage = (E[]) new Entity[DEFAULT_CAPACITY]; + } else { + this.storage = Arrays.copyOf(this.storage, this.storage.length * 2); + } +@@ -396,7 +463,7 @@ public final class ChunkEntitySlices { + + list.add(entity); + ++this.count; +- }finally { ++ } finally { + this.listLock.unlockWrite(id); + } + } +@@ -416,7 +483,7 @@ public final class ChunkEntitySlices { + this.entitiesBySection[sectionIndex] = null; + this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1))); + } +- }finally { ++ } finally { + this.listLock.unlockWrite(id); + } + } +@@ -459,65 +526,65 @@ public final class ChunkEntitySlices { + into.add(entity); + } + } +- }finally { ++ } finally { + this.listLock.unlockRead(id); + } + } + + public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List into, + final Predicate predicate) { +- final long id = this.listLock.readLock(); +- try { +- if (this.count == 0) { +- return; +- } ++ final long id = this.listLock.readLock(); ++ try { ++ if (this.count == 0) { ++ return; ++ } + +- final int minSection = this.manager.minSection; +- final int maxSection = this.manager.maxSection; ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; + +- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); +- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + +- final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; + +- for (int section = min; section <= max; ++section) { +- final BasicEntityList list = entitiesBySection[section - minSection]; ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; + +- if (list == null) { +- continue; +- } ++ if (list == null) { ++ continue; ++ } + +- final Entity[] storage = list.storage; ++ final Entity[] storage = list.storage; + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; + +- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { +- continue; +- } ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- if (predicate == null || predicate.test(entity)) { +- into.add(entity); +- } // else: continue to test the ender dragon parts ++ if (predicate == null || predicate.test(entity)) { ++ into.add(entity); ++ } // else: continue to test the ender dragon parts + +- if (entity instanceof EnderDragon) { +- for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) { +- if (part == except || !part.getBoundingBox().intersects(box)) { +- continue; +- } ++ if (entity instanceof EnderDragon) { ++ for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) { ++ if (part == except || !part.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- if (predicate != null && !predicate.test(part)) { +- continue; +- } ++ if (predicate != null && !predicate.test(part)) { ++ continue; ++ } + +- into.add(part); +- } +- } +- } +- } +- }finally { +- this.listLock.unlockRead(id); +- } ++ into.add(part); ++ } ++ } ++ } ++ } ++ } finally { ++ this.listLock.unlockRead(id); ++ } + } + + public void getEntitiesWithEnderDragonParts(final Entity except, final Class clazz, final AABB box, final List into, +@@ -557,7 +624,7 @@ public final class ChunkEntitySlices { + } // else: continue to test the ender dragon parts + + if (entity instanceof EnderDragon) { +- for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) { ++ for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) { + if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) { + continue; + } +@@ -571,7 +638,7 @@ public final class ChunkEntitySlices { + } + } + } +- }finally { ++ } finally { + this.listLock.unlockRead(id); + } + } +@@ -608,14 +675,14 @@ public final class ChunkEntitySlices { + continue; + } + +- if (predicate != null && !predicate.test((T)entity)) { ++ if (predicate != null && !predicate.test((T) entity)) { + continue; + } + +- into.add((T)entity); ++ into.add((T) entity); + } + } +- }finally { ++ } finally { + this.listLock.unlockRead(id); + } + } diff --git a/patches/server/0071-Hearse-Remove-some-lock-in-ChunkEntitySlices.patch b/patches/server/0071-Hearse-Remove-some-lock-in-ChunkEntitySlices.patch new file mode 100644 index 00000000..7618712b --- /dev/null +++ b/patches/server/0071-Hearse-Remove-some-lock-in-ChunkEntitySlices.patch @@ -0,0 +1,460 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Thu, 12 Jan 2023 20:18:22 +0800 +Subject: [PATCH] Hearse: Remove some lock in ChunkEntitySlices + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +index 1012b8d1d192a946b0982c88c12a0fc0e6051972..b2eb6feffb2191c450175547c1371623ce5185eb 100644 +--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java ++++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +@@ -191,15 +191,19 @@ public final class ChunkEntitySlices { + + public void mergeInto(final ChunkEntitySlices slices) { + final long id = this.accessLock.readLock(); ++ final List cop = new ArrayList<>(); + try { + final Entity[] entities = this.entities.getRawData(); + for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { + final Entity entity = entities[i]; +- slices.addEntity(entity, entity.sectionY); ++ cop.add(entity); + } + } finally { + this.accessLock.unlockRead(id); + } ++ for (Entity entity : cop){ ++ slices.addEntity(entity, entity.sectionY); ++ } + } + + public void updateStatus(final ChunkHolder.FullChunkStatus status, final EntityLookup lookup) { +@@ -370,11 +374,11 @@ public final class ChunkEntitySlices { + this.storage = (E[]) (cap <= 0 ? EMPTY : new Entity[cap]); + } + +- public synchronized boolean isEmpty() { ++ public boolean isEmpty() { + return this.size == 0; + } + +- public synchronized int size() { ++ public int size() { + return this.size; + } + +@@ -386,7 +390,7 @@ public final class ChunkEntitySlices { + } + } + +- public synchronized void add(final E entity) { ++ public void add(final E entity) { + final int idx = this.size++; + if (idx >= this.storage.length) { + this.resize(); +@@ -396,7 +400,7 @@ public final class ChunkEntitySlices { + } + } + +- public synchronized int indexOf(final E entity) { ++ public int indexOf(final E entity) { + final E[] storage = this.storage; + + for (int i = 0, len = Math.min(this.storage.length, this.size); i < len; ++i) { +@@ -408,7 +412,7 @@ public final class ChunkEntitySlices { + return -1; + } + +- public synchronized boolean remove(final E entity) { ++ public boolean remove(final E entity) { + final int idx = this.indexOf(entity); + if (idx == -1) { + return false; +@@ -425,7 +429,7 @@ public final class ChunkEntitySlices { + return true; + } + +- public synchronized boolean has(final E entity) { ++ public boolean has(final E entity) { + return this.indexOf(entity) != -1; + } + } +@@ -436,7 +440,6 @@ public final class ChunkEntitySlices { + protected final long[] nonEmptyBitset; + protected final BasicEntityList[] entitiesBySection; + protected int count; +- private final StampedLock listLock = new StampedLock();//Hearse + + public EntityCollectionBySection(final ChunkEntitySlices manager) { + this.manager = manager; +@@ -448,242 +451,212 @@ public final class ChunkEntitySlices { + } + + public void addEntity(final Entity entity, final int sectionIndex) { +- final long id = this.listLock.writeLock(); +- try { +- BasicEntityList list = this.entitiesBySection[sectionIndex]; +- +- if (list != null && list.has(entity)) { +- return; +- } ++ BasicEntityList list = this.entitiesBySection[sectionIndex]; + +- if (list == null) { +- this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>(); +- this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1))); +- } ++ if (list != null && list.has(entity)) { ++ return; ++ } + +- list.add(entity); +- ++this.count; +- } finally { +- this.listLock.unlockWrite(id); ++ if (list == null) { ++ this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>(); ++ this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1))); + } ++ ++ list.add(entity); ++ ++this.count; + } + + public void removeEntity(final Entity entity, final int sectionIndex) { +- final long id = this.listLock.writeLock(); +- try { +- final BasicEntityList list = this.entitiesBySection[sectionIndex]; ++ final BasicEntityList list = this.entitiesBySection[sectionIndex]; + +- if (list == null || !list.remove(entity)) { +- return; +- } ++ if (list == null || !list.remove(entity)) { ++ return; ++ } + +- --this.count; ++ --this.count; + +- if (list.isEmpty()) { +- this.entitiesBySection[sectionIndex] = null; +- this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1))); +- } +- } finally { +- this.listLock.unlockWrite(id); ++ if (list.isEmpty()) { ++ this.entitiesBySection[sectionIndex] = null; ++ this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1))); + } + } + + public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { +- final long id = this.listLock.readLock(); +- try { +- if (this.count == 0) { +- return; +- } +- +- final int minSection = this.manager.minSection; +- final int maxSection = this.manager.maxSection; ++ if (this.count == 0) { ++ return; ++ } + +- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); +- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; + +- final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + +- for (int section = min; section <= max; ++section) { +- final BasicEntityList list = entitiesBySection[section - minSection]; ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; + +- if (list == null) { +- continue; +- } ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; + +- final Entity[] storage = list.storage; ++ if (list == null) { ++ continue; ++ } + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ final Entity[] storage = list.storage; + +- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { +- continue; +- } ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; + +- if (predicate != null && !predicate.test(entity)) { +- continue; +- } ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- into.add(entity); ++ if (predicate != null && !predicate.test(entity)) { ++ continue; + } ++ ++ into.add(entity); + } +- } finally { +- this.listLock.unlockRead(id); + } + } + + public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List into, + final Predicate predicate) { +- final long id = this.listLock.readLock(); +- try { +- if (this.count == 0) { +- return; +- } +- +- final int minSection = this.manager.minSection; +- final int maxSection = this.manager.maxSection; ++ if (this.count == 0) { ++ return; ++ } + +- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); +- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; + +- final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + +- for (int section = min; section <= max; ++section) { +- final BasicEntityList list = entitiesBySection[section - minSection]; ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; + +- if (list == null) { +- continue; +- } ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; + +- final Entity[] storage = list.storage; ++ if (list == null) { ++ continue; ++ } + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ final Entity[] storage = list.storage; + +- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { +- continue; +- } ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; + +- if (predicate == null || predicate.test(entity)) { +- into.add(entity); +- } // else: continue to test the ender dragon parts ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- if (entity instanceof EnderDragon) { +- for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) { +- if (part == except || !part.getBoundingBox().intersects(box)) { +- continue; +- } ++ if (predicate == null || predicate.test(entity)) { ++ into.add(entity); ++ } // else: continue to test the ender dragon parts + +- if (predicate != null && !predicate.test(part)) { +- continue; +- } ++ if (entity instanceof EnderDragon) { ++ for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) { ++ if (part == except || !part.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- into.add(part); ++ if (predicate != null && !predicate.test(part)) { ++ continue; + } ++ ++ into.add(part); + } + } + } +- } finally { +- this.listLock.unlockRead(id); + } + } + + public void getEntitiesWithEnderDragonParts(final Entity except, final Class clazz, final AABB box, final List into, + final Predicate predicate) { +- final long id = this.listLock.readLock(); +- try { +- if (this.count == 0) { +- return; +- } ++ if (this.count == 0) { ++ return; ++ } + +- final int minSection = this.manager.minSection; +- final int maxSection = this.manager.maxSection; ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; + +- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); +- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + +- final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; + +- for (int section = min; section <= max; ++section) { +- final BasicEntityList list = entitiesBySection[section - minSection]; ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; + +- if (list == null) { +- continue; +- } +- +- final Entity[] storage = list.storage; ++ if (list == null) { ++ continue; ++ } + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ final Entity[] storage = list.storage; + +- if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { +- continue; +- } ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; + +- if (predicate == null || predicate.test(entity)) { +- into.add(entity); +- } // else: continue to test the ender dragon parts ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- if (entity instanceof EnderDragon) { +- for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) { +- if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) { +- continue; +- } ++ if (predicate == null || predicate.test(entity)) { ++ into.add(entity); ++ } // else: continue to test the ender dragon parts + +- if (predicate != null && !predicate.test(part)) { +- continue; +- } ++ if (entity instanceof EnderDragon) { ++ for (final EnderDragonPart part : ((EnderDragon) entity).subEntities) { ++ if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) { ++ continue; ++ } + +- into.add(part); ++ if (predicate != null && !predicate.test(part)) { ++ continue; + } ++ ++ into.add(part); + } + } + } +- } finally { +- this.listLock.unlockRead(id); + } + } + + public void getEntities(final EntityType type, final AABB box, final List into, + final Predicate predicate) { +- final long id = this.listLock.readLock(); +- try { +- if (this.count == 0) { +- return; +- } +- +- final int minSection = this.manager.minSection; +- final int maxSection = this.manager.maxSection; ++ if (this.count == 0) { ++ return; ++ } + +- final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); +- final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; + +- final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + +- for (int section = min; section <= max; ++section) { +- final BasicEntityList list = entitiesBySection[section - minSection]; ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; + +- if (list == null) { +- continue; +- } ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; + +- final Entity[] storage = list.storage; ++ if (list == null) { ++ continue; ++ } + +- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { +- final Entity entity = storage[i]; ++ final Entity[] storage = list.storage; + +- if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) { +- continue; +- } ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; + +- if (predicate != null && !predicate.test((T) entity)) { +- continue; +- } ++ if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } + +- into.add((T) entity); ++ if (predicate != null && !predicate.test((T) entity)) { ++ continue; + } ++ ++ into.add((T) entity); + } +- } finally { +- this.listLock.unlockRead(id); + } + } + }