mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
678 lines
39 KiB
Diff
678 lines
39 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: peaches94 <peachescu94@gmail.com>
|
|
Date: Sat, 2 Jul 2022 00:35:56 -0500
|
|
Subject: [PATCH] Multithreaded Tracker
|
|
|
|
Original license: GPL v3
|
|
Original project: https://github.com/Bloom-host/Petal
|
|
|
|
Original license: GPL v3
|
|
Original project: https://github.com/TECHNOVE/Airplane-Experimental
|
|
|
|
Co-authored-by: Paul Sauve <paul@technove.co>
|
|
Co-authored-by: Kevin Raneri <kevin.raneri@gmail.com>
|
|
Co-authored-by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
|
|
|
|
This patch refactored from original multithreaded tracker (Petal version),
|
|
and is derived from the Airplane fork by Paul Sauve, the tree is like:
|
|
Airplane -> Pufferfish? -> Petal -> Leaf
|
|
|
|
We made much of tracking logic asynchronously, and fixed visible issue
|
|
for the case of some NPC plugins which using real entity type, e.g. Citizens.
|
|
|
|
But it is still recommending to use those packet based, virtual entity
|
|
based NPC plugins, e.g. ZNPC Plus, Adyeshach, Fancy NPC, etc.
|
|
|
|
diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
|
|
index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..4200d22606c6a3dbdf282792a4007a51df66963b 100644
|
|
--- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
|
|
+++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
|
|
@@ -60,7 +60,16 @@ public final class NearbyPlayers {
|
|
|
|
private final ServerLevel world;
|
|
private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
|
|
- private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap<TrackedChunk> byChunk;
|
|
+ {
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
|
|
+ byChunk = it.unimi.dsi.fastutil.longs.Long2ReferenceMaps.synchronize(new Long2ReferenceOpenHashMap<>());
|
|
+ } else {
|
|
+ byChunk = new Long2ReferenceOpenHashMap<>();
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES];
|
|
{
|
|
for (int i = 0; i < this.directByChunk.length; ++i) {
|
|
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
index 02a9ef1694c796584c29430d27f0a09047368835..32608df3da169159c070f37cb55407f4f6187744 100644
|
|
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
@@ -340,7 +340,7 @@ public final class RegionizedPlayerChunkLoader {
|
|
private boolean canGenerateChunks = true;
|
|
|
|
private final ArrayDeque<ChunkHolderManager.TicketOperation<?, ?>> delayedTicketOps = new ArrayDeque<>();
|
|
- private final LongOpenHashSet sentChunks = new LongOpenHashSet();
|
|
+ private final LongOpenHashSet sentChunks = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled && !org.dreeam.leaf.config.modules.async.MultithreadedTracker.compatModeEnabled ? new org.dreeam.leaf.util.map.ConcurrentLongHashSet() : new LongOpenHashSet(); // Leaf - Multithreaded tracker
|
|
|
|
private static final byte CHUNK_TICKET_STAGE_NONE = 0;
|
|
private static final byte CHUNK_TICKET_STAGE_LOADING = 1;
|
|
diff --git a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
|
|
index 9c0c99b936b4a82ebfe924866e53ec71f7bbe9ad..2ccff968cb2065d34fad4d27573f9e3081edb2f2 100644
|
|
--- a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
|
|
+++ b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
|
|
@@ -32,6 +32,7 @@ public class ClientboundUpdateAttributesPacket implements Packet<ClientGamePacke
|
|
this.attributes = Lists.newArrayList();
|
|
|
|
for (AttributeInstance attributeInstance : attributes) {
|
|
+ if (attributeInstance == null) continue; // Leaf - Multithreaded tracker
|
|
this.attributes
|
|
.add(
|
|
new ClientboundUpdateAttributesPacket.AttributeSnapshot(
|
|
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
|
|
index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe29a2d6f8b 100644
|
|
--- a/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/net/minecraft/server/level/ChunkMap.java
|
|
@@ -255,6 +255,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
|
|
final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled)
|
|
+ for (int i = 0, len = inRange.size(); i < len; i++) {
|
|
+ final ServerPlayer player = backingSet[i];
|
|
+ if (player == null) continue;
|
|
+ ++(player.mobCounts[index]);
|
|
+ }
|
|
+ else
|
|
+ // Leaf end - Multithreaded tracker
|
|
for (int i = 0, len = inRange.size(); i < len; i++) {
|
|
++(backingSet[i].mobCounts[index]);
|
|
}
|
|
@@ -1013,6 +1022,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// Paper end - optimise entity tracker
|
|
|
|
protected void tick() {
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
|
|
+ final ServerLevel level = this.level;
|
|
+ org.dreeam.leaf.async.tracker.MultithreadedTracker.tick(level);
|
|
+ return;
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
// Paper start - optimise entity tracker
|
|
if (true) {
|
|
this.newTrackerTick();
|
|
@@ -1135,7 +1151,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
final Entity entity;
|
|
private final int range;
|
|
SectionPos lastSectionPos;
|
|
- public final Set<ServerPlayerConnection> seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ public static final ServerPlayerConnection[] EMPTY_OBJECT_ARRAY = new ServerPlayerConnection[0];
|
|
+ public final Set<ServerPlayerConnection> seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>()) : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl
|
|
+ private volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY;
|
|
+ public ServerPlayerConnection[] seenBy() {
|
|
+ return seenByArray;
|
|
+ }
|
|
+ public void seenByUpdated() {
|
|
+ this.seenByArray = this.seenBy.toArray(EMPTY_OBJECT_ARRAY);
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
|
|
// Paper start - optimise entity tracker
|
|
private long lastChunkUpdate = -1L;
|
|
@@ -1162,27 +1188,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
this.lastTrackedChunk = chunk;
|
|
|
|
final ServerPlayer[] playersRaw = players.getRawDataUnchecked();
|
|
-
|
|
- for (int i = 0, len = players.size(); i < len; ++i) {
|
|
+ final int playersLength = Math.min(playersRaw.length, players.size()); // Leaf - Multithreaded tracker
|
|
+ for (int i = 0; i < playersLength; ++i) { // Leaf - Multithreaded tracker
|
|
final ServerPlayer player = playersRaw[i];
|
|
this.updatePlayer(player);
|
|
}
|
|
|
|
if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) {
|
|
// need to purge any players possible not in the chunk list
|
|
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ boolean removed = false;
|
|
+ for (final ServerPlayerConnection conn : this.seenBy()) {
|
|
final ServerPlayer player = conn.getPlayer();
|
|
if (!players.contains(player)) {
|
|
- this.removePlayer(player);
|
|
+ removed |= this.removePlayerMulti(player);
|
|
}
|
|
}
|
|
+ if (removed) {
|
|
+ this.seenByUpdated();
|
|
+ }
|
|
}
|
|
}
|
|
+ // Leaf end - Multithreaded tracker
|
|
+
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ public final @Nullable Runnable leafTickCompact(final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) {
|
|
+ if (chunk == null) {
|
|
+ this.moonrise$clearPlayers();
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<ServerPlayer> players = chunk.getPlayers(ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.VIEW_DISTANCE);
|
|
+
|
|
+ if (players == null) {
|
|
+ this.moonrise$clearPlayers();
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ final long lastChunkUpdate = this.lastChunkUpdate;
|
|
+ final long currChunkUpdate = chunk.getUpdateCount();
|
|
+ final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk lastTrackedChunk = this.lastTrackedChunk;
|
|
+ this.lastChunkUpdate = currChunkUpdate;
|
|
+ this.lastTrackedChunk = chunk;
|
|
+
|
|
+ final ServerPlayer[] playersRaw = players.getRawDataUnchecked();
|
|
+ final int playersLen = players.size(); // Ensure length won't change in the future tasks
|
|
+
|
|
+ if (!org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled || !org.dreeam.leaf.config.modules.async.MultithreadedTracker.compatModeEnabled) {
|
|
+ throw new IllegalStateException();
|
|
+ }
|
|
+ final boolean isServerPlayer = this.entity instanceof ServerPlayer;
|
|
+ final boolean isRealPlayer = isServerPlayer && ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer) this.entity).moonrise$isRealPlayer();
|
|
+ Runnable updatePlayerTasks = () -> {
|
|
+ for (int i = 0; i < playersLen; ++i) {
|
|
+ final ServerPlayer player = playersRaw[i];
|
|
+ this.updatePlayer(player);
|
|
+ }
|
|
+
|
|
+ if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) {
|
|
+ // need to purge any players possible not in the chunk list
|
|
+ boolean removed = false;
|
|
+ for (final ServerPlayerConnection conn : this.seenBy()) {
|
|
+ final ServerPlayer player = conn.getPlayer();
|
|
+ if (!players.contains(player)) {
|
|
+ removed |= this.removePlayerMulti(player);
|
|
+ }
|
|
+ }
|
|
+ if (removed) {
|
|
+ this.seenByUpdated();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+
|
|
+ // Only update asynchronously for real player, and sync update for fake players
|
|
+ // This can fix compatibility issue with NPC plugins using real entity type, like Citizens
|
|
+ // To prevent visible issue with player type NPCs
|
|
+ // btw, still recommend to use packet based NPC plugins, like ZNPC Plus, Adyeshach, Fancy NPC, etc.
|
|
+ if (isRealPlayer || !isServerPlayer) {
|
|
+ return updatePlayerTasks;
|
|
+ } else {
|
|
+ updatePlayerTasks.run();
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
|
|
@Override
|
|
public final void moonrise$removeNonTickThreadPlayers() {
|
|
boolean foundToRemove = false;
|
|
- for (final ServerPlayerConnection conn : this.seenBy) {
|
|
+ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf - Multithreaded tracker
|
|
if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(conn.getPlayer())) {
|
|
foundToRemove = true;
|
|
break;
|
|
@@ -1193,12 +1287,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
return;
|
|
}
|
|
|
|
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
|
|
+ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf - Multithreaded tracker
|
|
ServerPlayer player = conn.getPlayer();
|
|
if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(player)) {
|
|
- this.removePlayer(player);
|
|
+ this.removePlayerMulti(player); // Leaf - Multithreaded tracker
|
|
}
|
|
}
|
|
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
|
|
}
|
|
|
|
@Override
|
|
@@ -1208,10 +1303,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
if (this.seenBy.isEmpty()) {
|
|
return;
|
|
}
|
|
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
|
|
+ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf - Multithreaded tracker
|
|
ServerPlayer player = conn.getPlayer();
|
|
- this.removePlayer(player);
|
|
+ this.removePlayerMulti(player); // Leaf - Multithreaded tracker
|
|
}
|
|
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
|
|
}
|
|
|
|
@Override
|
|
@@ -1238,7 +1334,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
|
|
public void broadcast(Packet<?> packet) {
|
|
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
|
|
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // Leaf - petal - Multithreaded tracker
|
|
serverPlayerConnection.send(packet);
|
|
}
|
|
}
|
|
@@ -1259,21 +1355,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
|
|
public void broadcastRemoved() {
|
|
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
|
|
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // Leaf - petal - Multithreaded tracker
|
|
this.serverEntity.removePairing(serverPlayerConnection.getPlayer());
|
|
}
|
|
}
|
|
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ public boolean removePlayerMulti(ServerPlayer player) {
|
|
+ if (this.seenBy.remove(player.connection)) {
|
|
+ this.serverEntity.removePairing(player);
|
|
+ return true;
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
+
|
|
public void removePlayer(ServerPlayer player) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
|
|
+ //org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot // Leaf - petal - Multithreaded tracker - We can remove async too
|
|
if (this.seenBy.remove(player.connection)) {
|
|
this.serverEntity.removePairing(player);
|
|
}
|
|
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
|
|
}
|
|
|
|
public void updatePlayer(ServerPlayer player) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
|
|
+ //org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot // Leaf - petal - Multithreaded tracker - We can update async
|
|
if (player != this.entity) {
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled && player == null) return; // Leaf - Multithreaded tracker
|
|
// Paper start - remove allocation of Vec3D here
|
|
// Vec3 vec3 = player.position().subtract(this.entity.position());
|
|
double vec3_dx = player.getX() - this.entity.getX();
|
|
@@ -1301,6 +1410,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// CraftBukkit end
|
|
if (flag) {
|
|
if (this.seenBy.add(player.connection)) {
|
|
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
|
|
// Paper start - entity tracking events
|
|
if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) {
|
|
this.serverEntity.addPairing(player);
|
|
@@ -1309,6 +1419,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
this.serverEntity.onPlayerAdd(); // Paper - fix desync when a player is added to the tracker
|
|
}
|
|
} else if (this.seenBy.remove(player.connection)) {
|
|
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
|
|
this.serverEntity.removePairing(player);
|
|
}
|
|
}
|
|
diff --git a/net/minecraft/server/level/ServerBossEvent.java b/net/minecraft/server/level/ServerBossEvent.java
|
|
index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca675930ff2 100644
|
|
--- a/net/minecraft/server/level/ServerBossEvent.java
|
|
+++ b/net/minecraft/server/level/ServerBossEvent.java
|
|
@@ -13,7 +13,7 @@ import net.minecraft.util.Mth;
|
|
import net.minecraft.world.BossEvent;
|
|
|
|
public class ServerBossEvent extends BossEvent {
|
|
- private final Set<ServerPlayer> players = Sets.newHashSet();
|
|
+ private final Set<ServerPlayer> players = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? Sets.newConcurrentHashSet() : Sets.newHashSet(); // Leaf - petal - Multithreaded tracker - players can be removed in async tracking
|
|
private final Set<ServerPlayer> unmodifiablePlayers = Collections.unmodifiableSet(this.players);
|
|
public boolean visible = true;
|
|
|
|
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
|
|
index 1a9601aee097b6c10cf2ae1c52fddf45da85f60f..16b2ca8c96e9561aa57e0903d1e98e6441044b6d 100644
|
|
--- a/net/minecraft/server/level/ServerEntity.java
|
|
+++ b/net/minecraft/server/level/ServerEntity.java
|
|
@@ -146,7 +146,7 @@ public class ServerEntity {
|
|
MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames
|
|
MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level);
|
|
if (savedData != null) {
|
|
- for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers) { // Paper
|
|
+ for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers.toArray(ChunkMap.TrackedEntity.EMPTY_OBJECT_ARRAY)) { // Paper // Leaf - Multithreaded tracker
|
|
final ServerPlayer serverPlayer = connection.getPlayer(); // Paper
|
|
savedData.tickCarriedBy(serverPlayer, item);
|
|
Packet<?> updatePacket = savedData.getUpdatePacket(mapId, serverPlayer);
|
|
@@ -468,7 +468,7 @@ public class ServerEntity {
|
|
this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
|
|
}
|
|
|
|
- attributesToSync.clear();
|
|
+ // attributesToSync.clear(); // Leaf - Multithreaded tracker
|
|
}
|
|
}
|
|
|
|
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
|
index 16cafa0acb7b2972ce08ab56921e73eb44eff6fa..bbccf0c8aef3792bb7b7cb0070e48bca4c274a2c 100644
|
|
--- a/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
|
@@ -2505,7 +2505,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
|
|
@Override
|
|
public LevelEntityGetter<Entity> getEntities() {
|
|
- org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot
|
|
+ //org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot // Leaf - Multithreaded tracker
|
|
return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system
|
|
}
|
|
|
|
@@ -2746,7 +2746,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
}
|
|
|
|
map.carriedByPlayers.remove(player);
|
|
- if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer.player == player)) {
|
|
+ if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer != null && holdingPlayer.player == player)) { // Leaf - Multithreaded tracker
|
|
map.decorations.remove(player.getName().getString());
|
|
}
|
|
}
|
|
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
index 155e2047659111f68e27d3517e5178afa233dfe4..c91348eea1350728d8f6de7b8c3613cb44b5cdec 100644
|
|
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
@@ -1881,7 +1881,7 @@ public class ServerGamePacketListenerImpl
|
|
}
|
|
|
|
public void internalTeleport(PositionMoveRotation posMoveRotation, Set<Relative> relatives) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper
|
|
+ //org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper // Leaf - Multithreaded tracker
|
|
// Paper start - Prevent teleporting dead entities
|
|
if (this.player.isRemoved()) {
|
|
LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
|
|
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
|
index 4c3eadc2d8480b2a2c2c08e58620544d403d3adc..68241d1d488bc2848e3c0d167270c1788e573c37 100644
|
|
--- a/net/minecraft/world/entity/LivingEntity.java
|
|
+++ b/net/minecraft/world/entity/LivingEntity.java
|
|
@@ -1317,13 +1317,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
|
|
private void refreshDirtyAttributes() {
|
|
- Set<AttributeInstance> attributesToUpdate = this.getAttributes().getAttributesToUpdate();
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ int[] attributesToUpdate = this.getAttributes().getAttributesToUpdateIds();
|
|
|
|
- for (AttributeInstance attributeInstance : attributesToUpdate) {
|
|
- this.onAttributeUpdated(attributeInstance.getAttribute());
|
|
+ for (int attribute : attributesToUpdate) {
|
|
+ this.onAttributeUpdated(net.minecraft.core.registries.BuiltInRegistries.ATTRIBUTE.get(attribute).orElseThrow());
|
|
}
|
|
-
|
|
- attributesToUpdate.clear();
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
protected void onAttributeUpdated(Holder<Attribute> attribute) {
|
|
diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java
|
|
index f8419dde44ebc7324e783f8bee42132d5ec973c3..406767c60ec1a324faaf5d3658b161647497f99b 100644
|
|
--- a/net/minecraft/world/entity/ai/attributes/Attribute.java
|
|
+++ b/net/minecraft/world/entity/ai/attributes/Attribute.java
|
|
@@ -16,10 +16,15 @@ public class Attribute {
|
|
private boolean syncable;
|
|
private final String descriptionId;
|
|
private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE;
|
|
+ // Leaf start - Optimize AttributeMap
|
|
+ public final int uid;
|
|
+ private static final java.util.concurrent.atomic.AtomicInteger SIZE = new java.util.concurrent.atomic.AtomicInteger();
|
|
+ // Leaf end - Optimize AttributeMap
|
|
|
|
protected Attribute(String descriptionId, double defaultValue) {
|
|
this.defaultValue = defaultValue;
|
|
this.descriptionId = descriptionId;
|
|
+ this.uid = SIZE.getAndAdd(1); // Leaf - Optimize AttributeMap
|
|
}
|
|
|
|
public double getDefaultValue() {
|
|
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
|
index 3ac9f36eae87369354e992a1d9b5c5b2d87d17cb..9f09d78a7dac12c7f1b06029d32ad93fae0c2aec 100644
|
|
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
|
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
|
@@ -26,8 +26,24 @@ public class AttributeInstance {
|
|
private final Map<AttributeModifier.Operation, Map<ResourceLocation, AttributeModifier>> modifiersByOperation = Maps.newEnumMap(
|
|
AttributeModifier.Operation.class
|
|
);
|
|
- private final Map<ResourceLocation, AttributeModifier> modifierById = new Object2ObjectArrayMap<>();
|
|
- private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>();
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ private final Map<ResourceLocation, AttributeModifier> modifierById;
|
|
+ {
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
|
|
+ modifierById = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
|
|
+ } else {
|
|
+ modifierById = new Object2ObjectArrayMap<>();
|
|
+ }
|
|
+ }
|
|
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers;
|
|
+ {
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
|
|
+ permanentModifiers = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
|
|
+ } else {
|
|
+ permanentModifiers = new Object2ObjectArrayMap<>();
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
private double baseValue;
|
|
private boolean dirty = true;
|
|
private double cachedValue;
|
|
@@ -56,7 +72,13 @@ public class AttributeInstance {
|
|
|
|
@VisibleForTesting
|
|
Map<ResourceLocation, AttributeModifier> getModifiers(AttributeModifier.Operation operation) {
|
|
- return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> new Object2ObjectOpenHashMap<>());
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> {
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled)
|
|
+ return it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
|
|
+ else return new Object2ObjectArrayMap<>();
|
|
+ });
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
public Set<AttributeModifier> getModifiers() {
|
|
@@ -144,8 +166,12 @@ public class AttributeInstance {
|
|
|
|
public double getValue() {
|
|
if (this.dirty) {
|
|
- this.cachedValue = this.calculateValue();
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ double value = this.calculateValue();
|
|
+ this.cachedValue = value;
|
|
this.dirty = false;
|
|
+ return value;
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
return this.cachedValue;
|
|
@@ -192,7 +218,15 @@ public class AttributeInstance {
|
|
compoundTag.store("id", TYPE_CODEC, this.attribute);
|
|
compoundTag.putDouble("base", this.baseValue);
|
|
if (!this.permanentModifiers.isEmpty()) {
|
|
- compoundTag.store("modifiers", AttributeModifier.CODEC.listOf(), List.copyOf(this.permanentModifiers.values()));
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
|
|
+ synchronized (this) {
|
|
+ compoundTag.store("modifiers", AttributeModifier.CODEC.listOf(), List.copyOf(this.permanentModifiers.values()));
|
|
+ }
|
|
+ } else {
|
|
+ compoundTag.store("modifiers", AttributeModifier.CODEC.listOf(), List.copyOf(this.permanentModifiers.values()));
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
return compoundTag;
|
|
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
index 701025715e0aca3c1f920a66f9b3d03ec08eaf02..778872cdd1717f1ad52de32faf43847a08c55269 100644
|
|
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
@@ -15,12 +15,14 @@ import net.minecraft.resources.ResourceLocation;
|
|
|
|
public class AttributeMap {
|
|
// Gale start - Lithium - replace AI attributes with optimized collections
|
|
- private final Map<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
|
|
- private final Set<AttributeInstance> attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
|
|
- private final Set<AttributeInstance> attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap();
|
|
+ private final org.dreeam.leaf.util.map.AttributeInstanceSet attributesToSync = new org.dreeam.leaf.util.map.AttributeInstanceSet((org.dreeam.leaf.util.map.AttributeInstanceArrayMap) attributes);
|
|
+ private final org.dreeam.leaf.util.map.AttributeInstanceSet attributesToUpdate = new org.dreeam.leaf.util.map.AttributeInstanceSet((org.dreeam.leaf.util.map.AttributeInstanceArrayMap) attributes);
|
|
+ // Leaf end - Multithreaded tracker
|
|
// Gale end - Lithium - replace AI attributes with optimized collections
|
|
private final AttributeSupplier supplier;
|
|
- private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations
|
|
+ //private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap
|
|
private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
|
|
|
|
public AttributeMap(AttributeSupplier supplier) {
|
|
@@ -31,31 +33,54 @@ public class AttributeMap {
|
|
this.entity = entity;
|
|
// Purpur end - Ridables
|
|
this.supplier = defaultAttributes;
|
|
- this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations
|
|
+ //this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap
|
|
}
|
|
|
|
- private void onAttributeModified(AttributeInstance instance) {
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ private synchronized void onAttributeModified(AttributeInstance instance) {
|
|
this.attributesToUpdate.add(instance);
|
|
if (instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))) { // Purpur - Ridables
|
|
this.attributesToSync.add(instance);
|
|
}
|
|
}
|
|
|
|
- public Set<AttributeInstance> getAttributesToSync() {
|
|
- return this.attributesToSync;
|
|
+ private static final AttributeInstance[] EMPTY_ATTRIBUTE_INSTANCE = new AttributeInstance[0];
|
|
+ public synchronized Set<AttributeInstance> getAttributesToSync() {
|
|
+ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToSync.toArray(EMPTY_ATTRIBUTE_INSTANCE));
|
|
+ this.attributesToSync.clear();
|
|
+ return clone;
|
|
}
|
|
|
|
- public Set<AttributeInstance> getAttributesToUpdate() {
|
|
- return this.attributesToUpdate;
|
|
+ public synchronized Set<AttributeInstance> getAttributesToUpdate() {
|
|
+ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToUpdate.toArray(EMPTY_ATTRIBUTE_INSTANCE));
|
|
+ this.attributesToUpdate.clear();
|
|
+ return clone;
|
|
}
|
|
|
|
+ public synchronized int[] getAttributesToUpdateIds() {
|
|
+ int[] clone = attributesToUpdate.inner.toIntArray();
|
|
+ this.attributesToUpdate.clear();
|
|
+ return clone;
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
+
|
|
public Collection<AttributeInstance> getSyncableAttributes() {
|
|
return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))).collect(Collectors.toList()); // Purpur - Ridables
|
|
}
|
|
|
|
@Nullable
|
|
public AttributeInstance getInstance(Holder<Attribute> attribute) {
|
|
- return this.attributes.computeIfAbsent(attribute, this.createInstance); // Gale - Airplane - reduce entity allocations - cache lambda, as for some reason java allocates it anyways
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ AttributeInstance v;
|
|
+ if ((v = this.attributes.get(attribute)) == null) {
|
|
+ AttributeInstance newValue;
|
|
+ if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) {
|
|
+ attributes.put(attribute, newValue);
|
|
+ return newValue;
|
|
+ }
|
|
+ }
|
|
+ return v;
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
public boolean hasAttribute(Holder<Attribute> attribute) {
|
|
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
|
|
index 24710041ccbc70e5506d8d89ae34f0141977f209..05de8a77b389691dd6986f36b4cb8cc0935e21e4 100644
|
|
--- a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
|
|
+++ b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
|
|
@@ -11,7 +11,7 @@ public class AttributeSupplier {
|
|
private final Map<Holder<Attribute>, AttributeInstance> instances;
|
|
|
|
AttributeSupplier(Map<Holder<Attribute>, AttributeInstance> instances) {
|
|
- this.instances = instances;
|
|
+ this.instances = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(instances); // Leaf - Optimize AttributeMap
|
|
}
|
|
|
|
public AttributeInstance getAttributeInstance(Holder<Attribute> attribute) {
|
|
@@ -41,7 +41,7 @@ public class AttributeSupplier {
|
|
}
|
|
|
|
@Nullable
|
|
- public AttributeInstance createInstance(Consumer<AttributeInstance> onDirty, Holder<Attribute> attribute) {
|
|
+ public AttributeInstance createInstance(Consumer<AttributeInstance> onDirty, Holder<Attribute> attribute) { // Leaf - Multithreaded tracker
|
|
AttributeInstance attributeInstance = this.instances.get(attribute);
|
|
if (attributeInstance == null) {
|
|
return null;
|
|
diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java
|
|
index 36e3937c9e09852937c94c268c877a15337835c5..8aedc3ca463745fe32cac977208b23dc0b8e73b6 100644
|
|
--- a/net/minecraft/world/entity/item/PrimedTnt.java
|
|
+++ b/net/minecraft/world/entity/item/PrimedTnt.java
|
|
@@ -145,12 +145,14 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
|
net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket velocityPacket = new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this);
|
|
net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket positionPacket = net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket.teleport(this.getId(), net.minecraft.world.entity.PositionMoveRotation.of(this), java.util.Set.of(), this.onGround);
|
|
|
|
- ete.seenBy.stream()
|
|
- .filter(viewer -> (viewer.getPlayer().getX() - this.getX()) * (viewer.getPlayer().getY() - this.getY()) * (viewer.getPlayer().getZ() - this.getZ()) < 16 * 16)
|
|
- .forEach(viewer -> {
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ for (var viewer : ete.seenBy()) {
|
|
+ if ((viewer.getPlayer().getX() - this.getX()) * (viewer.getPlayer().getY() - this.getY()) * (viewer.getPlayer().getZ() - this.getZ()) < 16 * 16) {
|
|
viewer.send(velocityPacket);
|
|
viewer.send(positionPacket);
|
|
- });
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
}
|
|
// Paper end - Option to prevent TNT from moving in water
|
|
diff --git a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
|
|
index 325ec57df2885f5e81b8a6b61e3a9fed9484b30f..abc5c097861d0decf49d0d3970ab48f1cf8b1cf1 100644
|
|
--- a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
|
|
+++ b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
|
|
@@ -35,13 +35,20 @@ public class NewMinecartBehavior extends MinecartBehavior {
|
|
private int cachedLerpDelay;
|
|
private float cachedPartialTick;
|
|
private int lerpDelay = 0;
|
|
- public final List<NewMinecartBehavior.MinecartStep> lerpSteps = new LinkedList<>();
|
|
+ public final List<NewMinecartBehavior.MinecartStep> lerpSteps; // Leaf - Multithreaded tracker
|
|
public final List<NewMinecartBehavior.MinecartStep> currentLerpSteps = new LinkedList<>();
|
|
public double currentLerpStepsTotalWeight = 0.0;
|
|
public NewMinecartBehavior.MinecartStep oldLerp = NewMinecartBehavior.MinecartStep.ZERO;
|
|
|
|
public NewMinecartBehavior(AbstractMinecart minecart) {
|
|
super(minecart);
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
|
|
+ lerpSteps = it.unimi.dsi.fastutil.objects.ObjectLists.synchronize(new it.unimi.dsi.fastutil.objects.ObjectArrayList<>());
|
|
+ } else {
|
|
+ lerpSteps = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
@Override
|
|
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
|
index 7bbeed6c998c91e68376d3f17a510d68e3cd0b27..d62ff9ebd4b55e1a9a0b51e84be868d844e5a954 100644
|
|
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
|
+++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
|
@@ -211,6 +211,7 @@ public class MapItemSavedData extends SavedData {
|
|
|
|
for (int i = 0; i < this.carriedBy.size(); i++) {
|
|
MapItemSavedData.HoldingPlayer holdingPlayer1 = this.carriedBy.get(i);
|
|
+ if (holdingPlayer1 == null) continue; // Leaf - Multithreaded tracker
|
|
Player player1 = holdingPlayer1.player;
|
|
String string = player1.getName().getString();
|
|
if (!player1.isRemoved() && (player1.getInventory().contains(predicate) || mapStack.isFramed())) {
|