mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-25 18:09:17 +00:00
optimize tracker (#323)
* optimize tracker * optimize scaledRange * cleanup * fix loop * fix loop * optimize AttributeMap * optimize TrackedEntity#seenBy * revert packDirty * cleanup
This commit is contained in:
@@ -2,12 +2,14 @@ package org.dreeam.leaf.async.tracker;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup;
|
||||
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.ServerEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -49,11 +51,7 @@ public class MultithreadedTracker {
|
||||
}
|
||||
}
|
||||
|
||||
public static Executor getTrackerExecutor() {
|
||||
return TRACKER_EXECUTOR;
|
||||
}
|
||||
|
||||
public static void tick(ChunkSystemServerLevel level) {
|
||||
public static void tick(ServerLevel level) {
|
||||
try {
|
||||
if (!org.dreeam.leaf.config.modules.async.MultithreadedTracker.compatModeEnabled) {
|
||||
tickAsync(level);
|
||||
@@ -65,7 +63,7 @@ public class MultithreadedTracker {
|
||||
}
|
||||
}
|
||||
|
||||
private static void tickAsync(ChunkSystemServerLevel level) {
|
||||
private static void tickAsync(ServerLevel level) {
|
||||
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
|
||||
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
|
||||
|
||||
@@ -74,6 +72,7 @@ public class MultithreadedTracker {
|
||||
|
||||
// Move tracking to off-main
|
||||
TRACKER_EXECUTOR.execute(() -> {
|
||||
ReferenceArrayList<ServerEntity> sendDirty = new ReferenceArrayList<>();
|
||||
for (final Entity entity : trackerEntitiesRaw) {
|
||||
if (entity == null) continue;
|
||||
|
||||
@@ -85,18 +84,26 @@ public class MultithreadedTracker {
|
||||
synchronized (tracker.sync) {
|
||||
tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
|
||||
tracker.serverEntity.sendChanges();
|
||||
if (tracker.serverEntity.wantSendDirtyEntityData) {
|
||||
tracker.serverEntity.wantSendDirtyEntityData = false;
|
||||
sendDirty.add(tracker.serverEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sendDirty.isEmpty()) {
|
||||
level.getServer().execute(() -> sendDirty.forEach(ServerEntity::sendDirtyEntityData));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void tickAsyncWithCompatMode(ChunkSystemServerLevel level) {
|
||||
private static void tickAsyncWithCompatMode(ServerLevel level) {
|
||||
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
|
||||
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
|
||||
|
||||
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
|
||||
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
|
||||
final Runnable[] sendChangesTasks = new Runnable[trackerEntitiesRaw.length];
|
||||
final Runnable[] tickTask = new Runnable[trackerEntitiesRaw.length];
|
||||
int index = 0;
|
||||
|
||||
for (final Entity entity : trackerEntitiesRaw) {
|
||||
@@ -106,17 +113,40 @@ public class MultithreadedTracker {
|
||||
|
||||
if (tracker == null) continue;
|
||||
|
||||
tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
|
||||
sendChangesTasks[index++] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array
|
||||
synchronized (tracker.sync) {
|
||||
tickTask[index] = tracker.leafTickCompact(nearbyPlayers.getChunk(entity.chunkPosition()));
|
||||
sendChangesTasks[index] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// batch submit tasks
|
||||
TRACKER_EXECUTOR.execute(() -> {
|
||||
for (final Runnable tick : tickTask) {
|
||||
if (tick == null) continue;
|
||||
|
||||
tick.run();
|
||||
}
|
||||
for (final Runnable sendChanges : sendChangesTasks) {
|
||||
if (sendChanges == null) continue;
|
||||
|
||||
sendChanges.run();
|
||||
}
|
||||
ReferenceArrayList<ServerEntity> sendDirty = new ReferenceArrayList<>();
|
||||
for (final Entity entity : trackerEntitiesRaw) {
|
||||
if (entity == null) continue;
|
||||
|
||||
final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity();
|
||||
|
||||
if (tracker == null) continue;
|
||||
if (tracker.serverEntity.wantSendDirtyEntityData) {
|
||||
tracker.serverEntity.wantSendDirtyEntityData = false;
|
||||
sendDirty.add(tracker.serverEntity);
|
||||
}
|
||||
}
|
||||
if (!sendDirty.isEmpty()) {
|
||||
level.getServer().execute(() -> sendDirty.forEach(ServerEntity::sendDirtyEntityData));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -161,10 +191,11 @@ public class MultithreadedTracker {
|
||||
|
||||
private static @NotNull ThreadFactory getThreadFactory() {
|
||||
return new ThreadFactoryBuilder()
|
||||
.setThreadFactory(MultithreadedTrackerThread::new)
|
||||
.setNameFormat(THREAD_PREFIX + " Thread - %d")
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.build();
|
||||
.setThreadFactory(MultithreadedTrackerThread::new)
|
||||
.setNameFormat(THREAD_PREFIX + " Thread - %d")
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.setUncaughtExceptionHandler(Util::onThreadException)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static @NotNull RejectedExecutionHandler getRejectedPolicy() {
|
||||
|
||||
@@ -151,7 +151,7 @@ public class DoABarrelRollProtocol implements Protocol {
|
||||
}
|
||||
var payload = new RollSyncS2CPacket(player.getId(), isRolling, roll);
|
||||
var packet = Protocols.createPacket(payload);
|
||||
for (ServerPlayerConnection seenBy : player.moonrise$getTrackedEntity().seenBy.toArray(new ServerPlayerConnection[0])) {
|
||||
for (ServerPlayerConnection seenBy : player.moonrise$getTrackedEntity().seenBy()) {
|
||||
if (seenBy instanceof ServerGamePacketListenerImpl conn
|
||||
&& getHandshakeState(conn).state == HandshakeState.ACCEPTED) {
|
||||
seenBy.send(packet);
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
package org.dreeam.leaf.util.map;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
|
||||
/// fast array backend map with O(1) get & put & remove
|
||||
public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, AttributeInstance>, Cloneable {
|
||||
private int size = 0;
|
||||
private transient AttributeInstance[] a = new AttributeInstance[32];
|
||||
private transient KeySet keys;
|
||||
private transient Values values;
|
||||
private transient EntrySet entries;
|
||||
|
||||
public AttributeInstanceArrayMap() {
|
||||
if (BuiltInRegistries.ATTRIBUTE.size() != 32) {
|
||||
throw new IllegalStateException("Registered custom attribute");
|
||||
}
|
||||
}
|
||||
|
||||
public AttributeInstanceArrayMap(final @NotNull Map<Holder<Attribute>, AttributeInstance> m) {
|
||||
this();
|
||||
for (AttributeInstance e : m.values()) {
|
||||
setByIndex(e.getAttribute().value().uid, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setByIndex(int index, @Nullable AttributeInstance instance) {
|
||||
boolean empty = a[index] == null;
|
||||
if (instance == null) {
|
||||
if (!empty) {
|
||||
size--;
|
||||
a[index] = null;
|
||||
}
|
||||
} else {
|
||||
if (empty) {
|
||||
size++;
|
||||
}
|
||||
a[index] = instance;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean containsKey(Object key) {
|
||||
if (key instanceof Holder<?> holder && holder.value() instanceof Attribute attribute) {
|
||||
int uid = attribute.uid;
|
||||
return uid >= 0 && uid < a.length && a[uid] != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean containsValue(Object value) {
|
||||
for (final AttributeInstance instance : a) {
|
||||
if (Objects.equals(value, instance)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AttributeInstance get(Object key) {
|
||||
return key instanceof Holder<?> holder && holder.value() instanceof Attribute attribute ? a[attribute.uid] : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AttributeInstance put(@NotNull Holder<Attribute> key, AttributeInstance value) {
|
||||
int uid = key.value().uid;
|
||||
AttributeInstance prev = a[uid];
|
||||
setByIndex(uid, value);
|
||||
return prev;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AttributeInstance remove(Object key) {
|
||||
if (!(key instanceof Holder<?> holder) || !(holder.value() instanceof Attribute attribute)) return null;
|
||||
int uid = attribute.uid;
|
||||
AttributeInstance prev = a[uid];
|
||||
setByIndex(uid, null);
|
||||
return prev;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void putAll(@NotNull Map<? extends Holder<Attribute>, ? extends AttributeInstance> m) {
|
||||
m.forEach(this::put);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void clear() {
|
||||
Arrays.fill(a, null);
|
||||
size = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final @NotNull Set<Holder<Attribute>> keySet() {
|
||||
if (keys == null) {
|
||||
keys = new KeySet();
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final @NotNull Collection<AttributeInstance> values() {
|
||||
if (values == null) {
|
||||
values = new Values();
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final @NotNull Set<Entry<Holder<Attribute>, AttributeInstance>> entrySet() {
|
||||
if (entries == null) {
|
||||
entries = new EntrySet();
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Map<?, ?> other)) return false;
|
||||
return entrySet().equals(other.entrySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return entrySet().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeInstanceArrayMap clone() {
|
||||
AttributeInstanceArrayMap c;
|
||||
try {
|
||||
c = (AttributeInstanceArrayMap) super.clone();
|
||||
} catch (CloneNotSupportedException cantHappen) {
|
||||
throw new InternalError();
|
||||
}
|
||||
c.a = a.clone();
|
||||
c.entries = null;
|
||||
c.keys = null;
|
||||
c.values = null;
|
||||
return c;
|
||||
}
|
||||
|
||||
private final class KeySet extends AbstractSet<Holder<Attribute>> {
|
||||
@Override
|
||||
public @NotNull Iterator<Holder<Attribute>> iterator() {
|
||||
return new KeyIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return AttributeInstanceArrayMap.this.containsKey(o);
|
||||
}
|
||||
}
|
||||
|
||||
private final class KeyIterator implements Iterator<Holder<Attribute>> {
|
||||
private int currentIndex = -1;
|
||||
private int nextIndex = findNextOccupied(0);
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextIndex != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Attribute> next() {
|
||||
if (!hasNext()) throw new NoSuchElementException();
|
||||
currentIndex = nextIndex;
|
||||
nextIndex = findNextOccupied(nextIndex + 1);
|
||||
return BuiltInRegistries.ATTRIBUTE.asHolderIdMap().byIdOrThrow(currentIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (currentIndex == -1) throw new IllegalStateException();
|
||||
setByIndex(currentIndex, null);
|
||||
currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private final class Values extends AbstractCollection<AttributeInstance> {
|
||||
@Override
|
||||
public @NotNull Iterator<AttributeInstance> iterator() {
|
||||
return new ValueIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return containsValue(o);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ValueIterator implements Iterator<AttributeInstance> {
|
||||
private int currentIndex = -1;
|
||||
private int nextIndex = findNextOccupied(0);
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextIndex != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeInstance next() {
|
||||
if (!hasNext()) throw new NoSuchElementException();
|
||||
currentIndex = nextIndex;
|
||||
AttributeInstance value = a[nextIndex];
|
||||
nextIndex = findNextOccupied(nextIndex + 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (currentIndex == -1) throw new IllegalStateException();
|
||||
setByIndex(currentIndex, null);
|
||||
currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private final class EntrySet extends AbstractSet<Entry<Holder<Attribute>, AttributeInstance>> {
|
||||
@Override
|
||||
public @NotNull Iterator<Entry<Holder<Attribute>, AttributeInstance>> iterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
if (!(o instanceof Entry<?, ?> e)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(get(e.getKey()), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private final class EntryIterator implements Iterator<Entry<Holder<Attribute>, AttributeInstance>> {
|
||||
private int currentIndex = -1;
|
||||
private int nextIndex = findNextOccupied(0);
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextIndex != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<Holder<Attribute>, AttributeInstance> next() {
|
||||
if (!hasNext()) throw new NoSuchElementException();
|
||||
currentIndex = nextIndex;
|
||||
Holder<Attribute> key = BuiltInRegistries.ATTRIBUTE.asHolderIdMap().byIdOrThrow(nextIndex);
|
||||
AttributeInstance value = a[nextIndex];
|
||||
nextIndex = findNextOccupied(nextIndex + 1);
|
||||
return new SimpleEntry<>(key, value) {
|
||||
@Override
|
||||
public AttributeInstance setValue(AttributeInstance newValue) {
|
||||
AttributeInstance old = put(key, newValue);
|
||||
super.setValue(newValue);
|
||||
return old;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (currentIndex == -1) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
setByIndex(currentIndex, null);
|
||||
currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private int findNextOccupied(int start) {
|
||||
for (int i = start; i < a.length; i++) {
|
||||
if (a[i] != null) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user