9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-19 14:59:25 +00:00

update multithreaded tracker and pwt, some fixes

This commit is contained in:
NONPLAYT
2025-06-29 00:10:19 +03:00
parent 9feb5e9577
commit 5c7a5a8cf4
15 changed files with 1948 additions and 324 deletions

View File

@@ -4,6 +4,28 @@ Date: Tue, 28 Jan 2025 01:18:49 +0300
Subject: [PATCH] Multithreaded Tracker Subject: [PATCH] Multithreaded Tracker
diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..93272808d94e81d31af728ebe85df9a2bc7aedab 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<>();
+ // DivineMC start - Multithreaded Tracker
+ private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap<TrackedChunk> byChunk;
+ {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ byChunk = it.unimi.dsi.fastutil.longs.Long2ReferenceMaps.synchronize(new Long2ReferenceOpenHashMap<>());
+ } else {
+ byChunk = new Long2ReferenceOpenHashMap<>();
+ }
+ }
+ // DivineMC 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 diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
index bdc1200ef5317fdaf58973bf580b0a672aee800f..dc2b3ccf7810731c0e2c90e5a476c1c8203a1fb7 100644 index bdc1200ef5317fdaf58973bf580b0a672aee800f..dc2b3ccf7810731c0e2c90e5a476c1c8203a1fb7 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java --- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
@@ -21,17 +43,51 @@ index bdc1200ef5317fdaf58973bf580b0a672aee800f..dc2b3ccf7810731c0e2c90e5a476c1c8
private static final byte CHUNK_TICKET_STAGE_NONE = 0; private static final byte CHUNK_TICKET_STAGE_NONE = 0;
private static final byte CHUNK_TICKET_STAGE_LOADING = 1; 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..01ed1e3572e9c2ccfd19df117cda0d5cf65b9bcb 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; // DivineMC - Multithreaded Tracker
this.attributes
.add(
new ClientboundUpdateAttributesPacket.AttributeSnapshot(
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index edda52a8430386238be4963e8ea2406f0c2d4df3..687af4f52dc3ae5564079b6782b63a4277c43439 100644 index edda52a8430386238be4963e8ea2406f0c2d4df3..2c5104b9ae1d2ea902eeac5a1c9d49bc1af67c43 100644
--- a/net/minecraft/server/level/ChunkMap.java --- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java
@@ -1013,6 +1013,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -255,9 +255,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
- for (int i = 0, len = inRange.size(); i < len; i++) {
- ++(backingSet[i].mobCounts[index]);
+ // DivineMC start - Multithreaded Tracker
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ final ServerPlayer player = backingSet[i];
+ if (player == null) continue;
+ ++(player.mobCounts[index]);
+ }
+ } else {
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ ++(backingSet[i].mobCounts[index]);
+ }
}
+ // DivineMC end - Multithreaded Tracker
}
// Paper start - per player mob count backoff
@@ -1013,6 +1023,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end - optimise entity tracker // Paper end - optimise entity tracker
protected void tick() { protected void tick() {
+ // DivineMC start - Multithreaded tracker + // DivineMC start - Multithreaded tracker
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ final ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel level = this.level; + final ServerLevel level = this.level;
+ org.bxteam.divinemc.entity.tracking.MultithreadedTracker.tick(level); + org.bxteam.divinemc.entity.tracking.MultithreadedTracker.tick(level);
+ return; + return;
+ } + }
@@ -39,27 +95,110 @@ index edda52a8430386238be4963e8ea2406f0c2d4df3..687af4f52dc3ae5564079b6782b63a42
// Paper start - optimise entity tracker // Paper start - optimise entity tracker
if (true) { if (true) {
this.newTrackerTick(); this.newTrackerTick();
@@ -1135,7 +1142,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -1135,7 +1152,44 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final Entity entity; final Entity entity;
private final int range; private final int range;
SectionPos lastSectionPos; SectionPos lastSectionPos;
- public final Set<ServerPlayerConnection> seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl - public final Set<ServerPlayerConnection> seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl
+ // DivineMC start - Multithreaded tracker + // DivineMC start - Multithreaded tracker
+ public final Set<ServerPlayerConnection> seenBy = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled + public static final ServerPlayerConnection[] EMPTY_OBJECT_ARRAY = new ServerPlayerConnection[0];
+ ? com.google.common.collect.Sets.newConcurrentHashSet() + private final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ServerPlayerConnection> nonSyncSeenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>() {
+ : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl + @Override
+ public boolean add(ServerPlayerConnection serverPlayerConnection) {
+ seenByUpdated = true;
+ return super.add(serverPlayerConnection);
+ }
+
+ @Override
+ public boolean remove(Object k) {
+ seenByUpdated = true;
+ return super.remove(k);
+ }
+
+ @Override
+ public void clear() {
+ seenByUpdated = true;
+ super.clear();
+ }
+ };
+ public final Set<ServerPlayerConnection> seenBy = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(nonSyncSeenBy) : nonSyncSeenBy; // Paper - Perf: optimise map impl
+ private volatile boolean seenByUpdated = true;
+ private volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY;
+
+ public ServerPlayerConnection[] seenBy() {
+ if (!seenByUpdated) {
+ return seenByArray;
+ } else {
+ return seenBy.toArray(EMPTY_OBJECT_ARRAY);
+ }
+ }
+
+ public void seenByUpdated() {
+ this.seenByArray = this.seenBy.toArray(EMPTY_OBJECT_ARRAY);
+ seenByUpdated = false;
+ }
+ // DivineMC end - Multithreaded tracker + // DivineMC end - Multithreaded tracker
// Paper start - optimise entity tracker // Paper start - optimise entity tracker
private long lastChunkUpdate = -1L; private long lastChunkUpdate = -1L;
@@ -1162,21 +1173,55 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -1162,22 +1216,92 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lastTrackedChunk = chunk; this.lastTrackedChunk = chunk;
final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); final ServerPlayer[] playersRaw = players.getRawDataUnchecked();
+ final int playersLen = players.size(); // Ensure length won't change in the future tasks + final int playersLength = Math.min(playersRaw.length, players.size()); // DivineMC - Multithreaded tracker
- for (int i = 0, len = players.size(); i < len; ++i) {
+ for (int i = 0; i < playersLength; ++i) { // DivineMC - 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)) {
+ // DivineMC 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();
+ }
+ // DivineMC end - Multithreaded tracker
+ }
+ }
+ +
+ // DivineMC start - Multithreaded tracker + // DivineMC start - Multithreaded tracker
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled && org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedCompatModeEnabled) { + public final @Nullable Runnable tickCompact(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.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled || !org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedCompatModeEnabled) {
+ throw new IllegalStateException();
+ }
+ final boolean isServerPlayer = this.entity instanceof ServerPlayer; + final boolean isServerPlayer = this.entity instanceof ServerPlayer;
+ final boolean isRealPlayer = isServerPlayer && ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer) this.entity).moonrise$isRealPlayer(); + final boolean isRealPlayer = isServerPlayer && ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer) this.entity).moonrise$isRealPlayer();
+ Runnable updatePlayerTasks = () -> { + Runnable updatePlayerTasks = () -> {
@@ -67,20 +206,20 @@ index edda52a8430386238be4963e8ea2406f0c2d4df3..687af4f52dc3ae5564079b6782b63a42
+ final ServerPlayer player = playersRaw[i]; + final ServerPlayer player = playersRaw[i];
+ this.updatePlayer(player); + this.updatePlayer(player);
+ } + }
+
- for (int i = 0, len = players.size(); i < len; ++i) {
- final ServerPlayer player = playersRaw[i];
- this.updatePlayer(player);
- }
+ if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { + if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) {
+ // need to purge any players possible not in the chunk list + // need to purge any players possible not in the chunk list
+ for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { + boolean removed = false;
+ for (final ServerPlayerConnection conn : this.seenBy()) {
+ final ServerPlayer player = conn.getPlayer(); + final ServerPlayer player = conn.getPlayer();
+ if (!players.contains(player)) { + if (!players.contains(player)) {
+ this.removePlayer(player); + removed |= this.removePlayerMulti(player);
+ }
+ } + }
+ } + }
+ if (removed) {
+ this.seenByUpdated();
}
}
+ }; + };
+ +
+ // Only update asynchronously for real player, and sync update for fake players + // Only update asynchronously for real player, and sync update for fake players
@@ -88,60 +227,82 @@ index edda52a8430386238be4963e8ea2406f0c2d4df3..687af4f52dc3ae5564079b6782b63a42
+ // To prevent visible issue with player type NPCs + // To prevent visible issue with player type NPCs
+ // btw, still recommend to use packet based NPC plugins, like ZNPC Plus, Adyeshach, Fancy NPC, etc. + // btw, still recommend to use packet based NPC plugins, like ZNPC Plus, Adyeshach, Fancy NPC, etc.
+ if (isRealPlayer || !isServerPlayer) { + if (isRealPlayer || !isServerPlayer) {
+ org.bxteam.divinemc.entity.tracking.MultithreadedTracker.getTrackerExecutor().execute(updatePlayerTasks); + return updatePlayerTasks;
+ } else { + } else {
+ updatePlayerTasks.run(); + updatePlayerTasks.run();
+ } + return null;
+ } else {
+ for (int i = 0, len = players.size(); i < len; ++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
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
- final ServerPlayer player = conn.getPlayer();
- if (!players.contains(player)) {
- this.removePlayer(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)) {
+ final ServerPlayer player = conn.getPlayer();
+ if (!players.contains(player)) {
+ this.removePlayer(player);
+ }
}
} }
} }
+ // DivineMC end - Multithreaded tracker + // DivineMC end - Multithreaded tracker
@Override
public final void moonrise$removeNonTickThreadPlayers() {
@@ -1193,12 +1317,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()) { // DivineMC - Multithreaded tracker
ServerPlayer player = conn.getPlayer();
if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(player)) {
- this.removePlayer(player);
+ this.removePlayerMulti(player); // DivineMC - Multithreaded tracker
}
}
+ this.seenByUpdated(); // DivineMC - Multithreaded tracker
} }
@Override @Override
@@ -1238,7 +1283,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -1208,10 +1333,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()) { // DivineMC - Multithreaded tracker
ServerPlayer player = conn.getPlayer();
- this.removePlayer(player);
+ this.removePlayerMulti(player); // DivineMC - Multithreaded tracker
}
+ this.seenByUpdated(); // DivineMC - Multithreaded tracker
}
@Override
@@ -1238,7 +1364,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} }
public void broadcast(Packet<?> packet) { public void broadcast(Packet<?> packet) {
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) { - for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy.toArray(new ServerPlayerConnection[0])) { // DivineMC - Multithreaded tracker + for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // DivineMC - Multithreaded tracker
serverPlayerConnection.send(packet); serverPlayerConnection.send(packet);
} }
} }
@@ -1259,21 +1304,20 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -1259,21 +1385,32 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} }
public void broadcastRemoved() { public void broadcastRemoved() {
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) { - for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy.toArray(new ServerPlayerConnection[0])) { // DivineMC - Multithreaded tracker + for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // DivineMC - Multithreaded tracker
this.serverEntity.removePairing(serverPlayerConnection.getPlayer()); this.serverEntity.removePairing(serverPlayerConnection.getPlayer());
} }
} }
+ // DivineMC start - Multithreaded tracker
+ public boolean removePlayerMulti(ServerPlayer player) {
+ if (this.seenBy.remove(player.connection)) {
+ this.serverEntity.removePairing(player);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // DivineMC end - Multithreaded tracker
+
public void removePlayer(ServerPlayer player) { public void removePlayer(ServerPlayer player) {
- org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot - org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
if (this.seenBy.remove(player.connection)) { if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player); this.serverEntity.removePairing(player);
} }
+ this.seenByUpdated(); // DivineMC - Multithreaded tracker
} }
public void updatePlayer(ServerPlayer player) { public void updatePlayer(ServerPlayer player) {
@@ -151,6 +312,22 @@ index edda52a8430386238be4963e8ea2406f0c2d4df3..687af4f52dc3ae5564079b6782b63a42
// Paper start - remove allocation of Vec3D here // Paper start - remove allocation of Vec3D here
// Vec3 vec3 = player.position().subtract(this.entity.position()); // Vec3 vec3 = player.position().subtract(this.entity.position());
double vec3_dx = player.getX() - this.entity.getX(); double vec3_dx = player.getX() - this.entity.getX();
@@ -1301,6 +1438,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// CraftBukkit end
if (flag) {
if (this.seenBy.add(player.connection)) {
+ this.seenByUpdated(); // DivineMC - 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 +1447,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(); // DivineMC - Multithreaded tracker
this.serverEntity.removePairing(player);
}
}
diff --git a/net/minecraft/server/level/ServerBossEvent.java b/net/minecraft/server/level/ServerBossEvent.java diff --git a/net/minecraft/server/level/ServerBossEvent.java b/net/minecraft/server/level/ServerBossEvent.java
index f106373ef3ac4a8685c2939c9e8361688a285913..b844b6dd89bc53b74c0d1bdbf4657c115a892dc7 100644 index f106373ef3ac4a8685c2939c9e8361688a285913..b844b6dd89bc53b74c0d1bdbf4657c115a892dc7 100644
--- a/net/minecraft/server/level/ServerBossEvent.java --- a/net/minecraft/server/level/ServerBossEvent.java
@@ -169,39 +346,40 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..b844b6dd89bc53b74c0d1bdbf4657c11
public boolean visible = true; public boolean visible = true;
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index e96d4dee14c05f2fa329bfb1588ec795d4e3d730..fa0fd2a7f15a076ac981f85f4f249a15a4ab512e 100644 index e96d4dee14c05f2fa329bfb1588ec795d4e3d730..917029d96afb5843276f4fa4ee37292327aea626 100644
--- a/net/minecraft/server/level/ServerEntity.java --- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java
@@ -417,12 +417,13 @@ public class ServerEntity { @@ -134,7 +134,7 @@ public class ServerEntity {
if (this.entity instanceof LivingEntity) { MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames
Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level);
if (!attributesToSync.isEmpty()) { if (savedData != null) {
+ final Set<AttributeInstance> copy = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(attributesToSync); // DivineMC - Multithreaded tracker - for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers) { // Paper
// CraftBukkit start - Send scaled max health + for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers.toArray(ChunkMap.TrackedEntity.EMPTY_OBJECT_ARRAY)) { // Paper // DivineMC - Multithreaded tracker
if (this.entity instanceof ServerPlayer serverPlayer) { final ServerPlayer serverPlayer = connection.getPlayer(); // Paper
- serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); savedData.tickCarriedBy(serverPlayer, item);
+ serverPlayer.getBukkitEntity().injectScaledMaxHealth(copy, false); // DivineMC - Multithreaded tracker Packet<?> updatePacket = savedData.getUpdatePacket(mapId, serverPlayer);
} @@ -424,8 +424,6 @@ public class ServerEntity {
// CraftBukkit end // CraftBukkit end
- this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
+ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), copy)); // DivineMC - Multithreaded tracker }
-
- attributesToSync.clear();
}
} }
attributesToSync.clear();
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 9dcb9e5ecc31fcc3fc7547a47ec98d2689698769..dda62ab1d718d22e24ed5c88674d7a3d9dd4323e 100644 index 9dcb9e5ecc31fcc3fc7547a47ec98d2689698769..2560799fe6ec006916a2bc9915355a358ab6c8bb 100644
--- a/net/minecraft/server/level/ServerLevel.java --- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java
@@ -2504,7 +2504,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -2504,7 +2504,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@Override @Override
public LevelEntityGetter<Entity> getEntities() { public LevelEntityGetter<Entity> getEntities() {
- org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot - org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot
+ //org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // DivineMC - Multithreaded tracker
return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system
} }
@@ -2771,7 +2771,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -2771,7 +2770,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
} }
map.carriedByPlayers.remove(player); map.carriedByPlayers.remove(player);
@@ -211,41 +389,142 @@ index 9dcb9e5ecc31fcc3fc7547a47ec98d2689698769..dda62ab1d718d22e24ed5c88674d7a3d
} }
} }
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 5c8489b6cecf83c087d648ae2c10243ec0b4b614..e1e5e9d49439355d5f52a90e308cbad3ad74b41f 100644 index a8bca25578d6428565bff853332a1bd868e9f740..34571dd4683c6d32b909d36436db8f5a4ce2628f 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1917,7 +1917,7 @@ public class ServerGamePacketListenerImpl @@ -1917,7 +1917,6 @@ public class ServerGamePacketListenerImpl
} }
public void internalTeleport(PositionMoveRotation posMoveRotation, Set<Relative> relatives) { public void internalTeleport(PositionMoveRotation posMoveRotation, Set<Relative> relatives) {
- org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper - org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper
+ //org.spigotmc.AsyncCatcher.catchOp("teleport"); // DivineMC - Multithreaded tracker
// Paper start - Prevent teleporting dead entities // Paper start - Prevent teleporting dead entities
if (this.player.isRemoved()) { if (this.player.isRemoved()) {
LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); 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 e3babfa292556c5f5a208536a3f869dc71b82498..b8bd792293cb5985db5e5dfa5644930971a34632 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1335,13 +1335,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
}
private void refreshDirtyAttributes() {
- Set<AttributeInstance> attributesToUpdate = this.getAttributes().getAttributesToUpdate();
+ // DivineMC 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();
+ // DivineMC 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..776445dd2dfee9d386ccb3ad17746d4c405a96b9 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;
+ // DivineMC start - Multithreaded Tracker
+ public final int uid;
+ private static final java.util.concurrent.atomic.AtomicInteger SIZE = new java.util.concurrent.atomic.AtomicInteger();
+ // DivineMC end - Multithreaded Tracker
protected Attribute(String descriptionId, double defaultValue) {
this.defaultValue = defaultValue;
this.descriptionId = descriptionId;
+ this.uid = SIZE.getAndAdd(1);
}
public double getDefaultValue() {
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
index 42ad600c6a5cb20e1d820f169f6a1a17ef3a5195..2ce6697a0bd2a094bbfdd0e2a9c7665e14837d40 100644 index 42ad600c6a5cb20e1d820f169f6a1a17ef3a5195..c93b2c684d773551b14cc2ce024923536780ee17 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
@@ -22,8 +22,11 @@ public class AttributeInstance { @@ -22,8 +22,24 @@ public class AttributeInstance {
private final Map<AttributeModifier.Operation, Map<ResourceLocation, AttributeModifier>> modifiersByOperation = Maps.newEnumMap( private final Map<AttributeModifier.Operation, Map<ResourceLocation, AttributeModifier>> modifiersByOperation = Maps.newEnumMap(
AttributeModifier.Operation.class AttributeModifier.Operation.class
); );
- private final Map<ResourceLocation, AttributeModifier> modifierById = new Object2ObjectArrayMap<>(); - private final Map<ResourceLocation, AttributeModifier> modifierById = new Object2ObjectArrayMap<>();
- private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>(); - private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>();
+ // DivineMC start - Multithreaded tracker + // DivineMC start - Multithreaded tracker
+ private final boolean multiThreadedTrackingEnabled = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled; + private final Map<ResourceLocation, AttributeModifier> modifierById;
+ private final Map<ResourceLocation, AttributeModifier> modifierById = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>(); + {
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>(); + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ modifierById = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
+ } else {
+ modifierById = new Object2ObjectArrayMap<>();
+ }
+ }
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers;
+ {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ permanentModifiers = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
+ } else {
+ permanentModifiers = new Object2ObjectArrayMap<>();
+ }
+ }
+ // DivineMC end - Multithreaded tracker + // DivineMC end - Multithreaded tracker
private double baseValue; private double baseValue;
private boolean dirty = true; private boolean dirty = true;
private double cachedValue; private double cachedValue;
@@ -52,7 +68,13 @@ public class AttributeInstance {
@VisibleForTesting
Map<ResourceLocation, AttributeModifier> getModifiers(AttributeModifier.Operation operation) {
- return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> new Object2ObjectOpenHashMap<>());
+ // DivineMC start - Multithreaded tracker
+ return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled)
+ return it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
+ else return new Object2ObjectArrayMap<>();
+ });
+ // DivineMC end - Multithreaded tracker
}
public Set<AttributeModifier> getModifiers() {
@@ -140,8 +162,12 @@ public class AttributeInstance {
public double getValue() {
if (this.dirty) {
- this.cachedValue = this.calculateValue();
+ // DivineMC start - Multithreaded tracker
+ double value = this.calculateValue();
+ this.cachedValue = value;
this.dirty = false;
+ return value;
+ // DivineMC end - Multithreaded tracker
}
return this.cachedValue;
@@ -184,7 +210,15 @@ public class AttributeInstance {
}
public AttributeInstance.Packed pack() {
- return new AttributeInstance.Packed(this.attribute, this.baseValue, List.copyOf(this.permanentModifiers.values()));
+ // DivineMC start - Multithreaded tracker
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ synchronized (this) {
+ return new AttributeInstance.Packed(this.attribute, this.baseValue, List.copyOf(this.permanentModifiers.values()));
+ }
+ } else {
+ return new AttributeInstance.Packed(this.attribute, this.baseValue, List.copyOf(this.permanentModifiers.values()));
+ }
+ // DivineMC end - Multithreaded tracker
}
public void apply(AttributeInstance.Packed instance) {
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index 7dd8c1c8e27410854ce1ee90defc607c2710b5a2..de647aa4eee2d9af2d2c5d1cde3a8a836d4f7e48 100644 index 7dd8c1c8e27410854ce1ee90defc607c2710b5a2..290a7fa565f695c7afe3cf0791f6cf1da6a39663 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java --- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -14,9 +14,12 @@ import net.minecraft.core.Holder; @@ -14,9 +14,11 @@ import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public class AttributeMap { public class AttributeMap {
@@ -253,14 +532,111 @@ index 7dd8c1c8e27410854ce1ee90defc607c2710b5a2..de647aa4eee2d9af2d2c5d1cde3a8a83
- private final Set<AttributeInstance> attributesToSync = new ObjectOpenHashSet<>(); - private final Set<AttributeInstance> attributesToSync = new ObjectOpenHashSet<>();
- private final Set<AttributeInstance> attributesToUpdate = new ObjectOpenHashSet<>(); - private final Set<AttributeInstance> attributesToUpdate = new ObjectOpenHashSet<>();
+ // DivineMC start - Multithreaded tracker + // DivineMC start - Multithreaded tracker
+ private final boolean multiThreadedTrackingEnabled = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled; + private final Map<Holder<Attribute>, AttributeInstance> attributes = new org.bxteam.divinemc.util.map.AttributeInstanceArrayMap();
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); + private final org.bxteam.divinemc.util.map.AttributeInstanceSet attributesToSync = new org.bxteam.divinemc.util.map.AttributeInstanceSet((org.bxteam.divinemc.util.map.AttributeInstanceArrayMap) attributes);
+ private final Set<AttributeInstance> attributesToSync = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); + private final org.bxteam.divinemc.util.map.AttributeInstanceSet attributesToUpdate = new org.bxteam.divinemc.util.map.AttributeInstanceSet((org.bxteam.divinemc.util.map.AttributeInstanceArrayMap) attributes);
+ private final Set<AttributeInstance> attributesToUpdate = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
+ // DivineMC end - Multithreaded tracker + // DivineMC end - Multithreaded tracker
private final AttributeSupplier supplier; private final AttributeSupplier supplier;
private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
@@ -30,28 +32,52 @@ public class AttributeMap {
this.supplier = defaultAttributes;
}
- private void onAttributeModified(AttributeInstance instance) {
+ private synchronized void onAttributeModified(AttributeInstance instance) { // DivineMC - Multithreaded Tracker
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;
+ // DivineMC start - Multithreaded Tracker
+ 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;
+ }
+ // DivineMC 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, holder -> this.supplier.createInstance(this::onAttributeModified, (Holder<Attribute>)holder));
+ // DivineMC 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;
+ // DivineMC 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..dbcff8bdd6911843bc42f64d5dcf1bb854128075 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.bxteam.divinemc.util.map.AttributeInstanceArrayMap(instances); // DivineMC - Multithreaded Tracker
}
public AttributeInstance getAttributeInstance(Holder<Attribute> attribute) {
diff --git a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
index 325ec57df2885f5e81b8a6b61e3a9fed9484b30f..1796f0a6f647c94b0943a6003a1307795294805e 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; // DivineMC - 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);
+ // DivineMC start - Multithreaded Tracker
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ this.lerpSteps = it.unimi.dsi.fastutil.objects.ObjectLists.synchronize(new it.unimi.dsi.fastutil.objects.ObjectArrayList<>());
+ } else {
+ this.lerpSteps = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ }
+ // DivineMC end - Multithreaded Tracker
}
@Override
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
index 7bbeed6c998c91e68376d3f17a510d68e3cd0b27..de7b3a8a7c841360310a88005da02a0733b46714 100644 index 7bbeed6c998c91e68376d3f17a510d68e3cd0b27..de7b3a8a7c841360310a88005da02a0733b46714 100644
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java --- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java

View File

@@ -569,39 +569,6 @@ index c8354d46ed909090f7c15f396863bf7d73afcefa..382ef2dad5e995bc01f6492218b8c8f7
} }
} }
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index e1f5a4814d051a43090bf6df2acbcd20fbbc1934..fd586acb15362a461c77256c6db9cc3a8002750d 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -715,7 +715,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) {
this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
} else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) {
- Entity nearestPlayer = this.level().findNearbyPlayer(this, -1.0, EntitySelector.PLAYER_AFFECTS_SPAWNING); // Paper - Affects Spawning API
+ Entity nearestPlayer = this.divinemc$findNearbyPlayer(this.level(), this, -1.0); // Paper - Affects Spawning API // DivineMC - faster player lookup
if (nearestPlayer != null) {
// Paper start - Configurable despawn distances
final io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DespawnRangePair despawnRangePair = this.level().paperConfig().entities.spawning.despawnRanges.get(this.getType().getCategory());
@@ -744,6 +744,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
}
}
+ // DivineMC start - faster player lookup
+ private Player divinemc$findNearbyPlayer(Level instance, Entity entity, double maxDistance) {
+ final Player closestPlayer = instance.getNearestPlayer(entity, this.getType().getCategory().getDespawnDistance());
+ if (closestPlayer != null) {
+ return closestPlayer;
+ } else {
+ final List<? extends Player> players = this.level().players();
+ if (players.isEmpty()) return null;
+ return players.get(0);
+ }
+ }
+ // DivineMC end - faster player lookup
+
@Override
protected final void serverAiStep() {
this.noActionTime++;
diff --git a/net/minecraft/world/level/GameRules.java b/net/minecraft/world/level/GameRules.java diff --git a/net/minecraft/world/level/GameRules.java b/net/minecraft/world/level/GameRules.java
index d510503a8ad272255aeba20a916642828023fd19..2d9bf302b779602d733187c6f86e52467f0dc540 100644 index d510503a8ad272255aeba20a916642828023fd19..2d9bf302b779602d733187c6f86e52467f0dc540 100644
--- a/net/minecraft/world/level/GameRules.java --- a/net/minecraft/world/level/GameRules.java

View File

@@ -5,22 +5,24 @@ Subject: [PATCH] Chunk System Optimizations
diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..78e3c49a233dc6bed558458d555fe740a8cc839e 100644 index 93272808d94e81d31af728ebe85df9a2bc7aedab..17c0f25206b12665518142f27c17d17f315aad5f 100644
--- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java --- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
+++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java +++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
@@ -59,12 +59,15 @@ public final class NearbyPlayers { @@ -59,7 +59,7 @@ public final class NearbyPlayers {
public static final int GENERAL_REALLY_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_REALLY_SMALL_VIEW_DISTANCE << 4); public static final int GENERAL_REALLY_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_REALLY_SMALL_VIEW_DISTANCE << 4);
private final ServerLevel world; private final ServerLevel world;
- private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>(); - private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
- private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>(); + private final it.unimi.dsi.fastutil.objects.Reference2ReferenceMap<ServerPlayer, TrackedPlayer[]> players = it.unimi.dsi.fastutil.objects.Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>()); // DivineMC - Chunk System optimization
// DivineMC start - Multithreaded Tracker
private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap<TrackedChunk> byChunk;
{
@@ -70,10 +70,10 @@ public final class NearbyPlayers {
}
}
// DivineMC end - Multithreaded Tracker
- private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES]; - private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES];
+ // DivineMC start - Chunk System optimization + private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap<ReferenceList<ServerPlayer>>[] directByChunk = new it.unimi.dsi.fastutil.longs.Long2ReferenceMap[TOTAL_MAP_TYPES]; // DivineMC - Chunk System optimization
+ private final Object callbackLock = new Object();
+ private final it.unimi.dsi.fastutil.objects.Reference2ReferenceMap<ServerPlayer, TrackedPlayer[]> players = it.unimi.dsi.fastutil.objects.Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>());
+ private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap<TrackedChunk> byChunk = it.unimi.dsi.fastutil.longs.Long2ReferenceMaps.synchronize(new Long2ReferenceOpenHashMap<>());
+ private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap<ReferenceList<ServerPlayer>>[] directByChunk = new it.unimi.dsi.fastutil.longs.Long2ReferenceMap[TOTAL_MAP_TYPES];
+ // DivineMC end - Chunk System optimization
{ {
for (int i = 0; i < this.directByChunk.length; ++i) { for (int i = 0; i < this.directByChunk.length; ++i) {
- this.directByChunk[i] = new Long2ReferenceOpenHashMap<>(); - this.directByChunk[i] = new Long2ReferenceOpenHashMap<>();
@@ -103,6 +105,43 @@ index b2bcfb3557a0326fd7ec1059f95d6da4568dfd80..6bb36686ae7ca9f4bf763baa89408614
} }
} }
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java
index 2d24d03bbdb5ee0d862cbfff2219f58afffafe12..950b284cb3b4488a794e6c6f936f55ea427ef7cc 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java
@@ -93,8 +93,14 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
if (entity == null) {
return null;
}
- final Visibility visibility = EntityLookup.getEntityStatus(entity);
- return visibility.isAccessible() ? entity : null;
+ // DivineMC start - Chunk System Optimizations
+ final FullChunkStatus entityStatus = ((ChunkSystemEntity) entity).moonrise$getChunkStatus();
+ return switch (entityStatus) {
+ case INACCESSIBLE -> null;
+ case FULL, BLOCK_TICKING, ENTITY_TICKING -> entity;
+ case null -> null;
+ };
+ // DivineMC end - Chunk System Optimizations
}
@Override
@@ -398,7 +404,14 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
return Visibility.TICKING;
}
final FullChunkStatus entityStatus = ((ChunkSystemEntity)entity).moonrise$getChunkStatus();
- return Visibility.fromFullChunkStatus(entityStatus == null ? FullChunkStatus.INACCESSIBLE : entityStatus);
+ // DivineMC start - Chunk System Optimizations
+ return switch (entityStatus) {
+ case INACCESSIBLE -> Visibility.HIDDEN;
+ case FULL, BLOCK_TICKING -> Visibility.TRACKED;
+ case ENTITY_TICKING -> Visibility.TICKING;
+ case null -> Visibility.HIDDEN;
+ };
+ // DivineMC end - Chunk System Optimizations
}
protected boolean addEntity(final Entity entity, final boolean fromDisk, final boolean event) {
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
index dc2b3ccf7810731c0e2c90e5a476c1c8203a1fb7..5cb896334f9916b030ee523119946d3b40585fc3 100644 index dc2b3ccf7810731c0e2c90e5a476c1c8203a1fb7..5cb896334f9916b030ee523119946d3b40585fc3 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java --- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
@@ -921,7 +960,7 @@ index df6fbb35e5023b42de0b97434712e04a6b3e66a3..8c6b853c77e5b3af90913e4a878f344b
commands.put(Set.of("fixlight"), new FixLightCommand()); // Paper - rewrite chunk system commands.put(Set.of("fixlight"), new FixLightCommand()); // Paper - rewrite chunk system
commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand()); // Paper - rewrite chunk system commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand()); // Paper - rewrite chunk system
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 687af4f52dc3ae5564079b6782b63a4277c43439..ab3b1c7442b9f3dd72a9ce8c70dc708d2371471a 100644 index 2c5104b9ae1d2ea902eeac5a1c9d49bc1af67c43..7ebaf03342e8802948b4f412c3f7120927f7b3b3 100644
--- a/net/minecraft/server/level/ChunkMap.java --- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java
@@ -132,8 +132,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -132,8 +132,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -935,20 +974,7 @@ index 687af4f52dc3ae5564079b6782b63a4277c43439..ab3b1c7442b9f3dd72a9ce8c70dc708d
// Paper - rewrite chunk system // Paper - rewrite chunk system
public int serverViewDistance; public int serverViewDistance;
public final WorldGenContext worldGenContext; // Paper - public public final WorldGenContext worldGenContext; // Paper - public
@@ -256,7 +256,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -283,7 +283,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
for (int i = 0, len = inRange.size(); i < len; i++) {
- ++(backingSet[i].mobCounts[index]);
+ // DivineMC start - Chunk System Optimizations
+ ServerPlayer player = backingSet[i];
+ if (player == null) continue;
+ ++(player.mobCounts[index]);
+ // DivineMC end - Chunk System Optimizations
}
}
@@ -273,7 +277,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} }
final ServerPlayer[] backingSet = inRange.getRawDataUnchecked(); final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
for (int i = 0, len = inRange.size(); i < len; i++) { for (int i = 0, len = inRange.size(); i < len; i++) {
@@ -961,7 +987,7 @@ index 687af4f52dc3ae5564079b6782b63a4277c43439..ab3b1c7442b9f3dd72a9ce8c70dc708d
} }
} }
// Paper end - per player mob count backoff // Paper end - per player mob count backoff
@@ -774,27 +782,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -784,27 +788,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return false; return false;
} }
@@ -990,7 +1016,7 @@ index 687af4f52dc3ae5564079b6782b63a4277c43439..ab3b1c7442b9f3dd72a9ce8c70dc708d
// Paper end - chunk tick iteration optimisation // Paper end - chunk tick iteration optimisation
} }
@@ -812,10 +800,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -822,10 +806,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final ServerPlayer[] raw = players.getRawDataUnchecked(); final ServerPlayer[] raw = players.getRawDataUnchecked();
final int len = players.size(); final int len = players.size();
@@ -1004,9 +1030,9 @@ index 687af4f52dc3ae5564079b6782b63a4277c43439..ab3b1c7442b9f3dd72a9ce8c70dc708d
if (ret == null) { if (ret == null) {
ret = new ArrayList<>(len - i); ret = new ArrayList<>(len - i);
ret.add(player); ret.add(player);
@@ -1208,6 +1196,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -1272,6 +1256,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} else { Runnable updatePlayerTasks = () -> {
for (int i = 0, len = players.size(); i < len; ++i) { for (int i = 0; i < playersLen; ++i) {
final ServerPlayer player = playersRaw[i]; final ServerPlayer player = playersRaw[i];
+ if (player == null) continue; // DivineMC - Chunk System Optimizations + if (player == null) continue; // DivineMC - Chunk System Optimizations
this.updatePlayer(player); this.updatePlayer(player);
@@ -1035,7 +1061,7 @@ index fd3d0f6cb53bc8b6186f0d86575f21007b2c20ed..7f3c41b59e288364d67534511fc038e6
} }
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 75c8ce32e68f92e20201e9c243f46f2be716eac8..2873642844c683ae4388ae27a045e01441d15426 100644 index 879d6eb8e72b63bc95d8028cbc2f6e93e516ab1d..6de832e7aec630914e70fb0f11223907ab28298c 100644
--- a/net/minecraft/server/level/ServerChunkCache.java --- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -444,8 +444,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -444,8 +444,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -1049,7 +1075,7 @@ index 75c8ce32e68f92e20201e9c243f46f2be716eac8..2873642844c683ae4388ae27a045e014
} }
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index d0d97e063acf8f11c32adf1fd2ec6bca59913c33..14c8df2f3b9c133b38e871e81f7ee2333322437c 100644 index 740f6324eeb4021bc45d27d6145ff71282c761c2..0dbcf9460b27959f99f7a95824b711ee6a4e4c4d 100644
--- a/net/minecraft/server/level/ServerLevel.java --- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java
@@ -179,6 +179,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -179,6 +179,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1156,7 +1182,7 @@ index d0d97e063acf8f11c32adf1fd2ec6bca59913c33..14c8df2f3b9c133b38e871e81f7ee233
} }
// Paper end - optimise random ticking // Paper end - optimise random ticking
@@ -2559,16 +2561,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -2558,16 +2560,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
public boolean isPositionTickingWithEntitiesLoaded(long chunkPos) { public boolean isPositionTickingWithEntitiesLoaded(long chunkPos) {
// Paper start - rewrite chunk system // Paper start - rewrite chunk system

View File

@@ -5,10 +5,10 @@ Subject: [PATCH] Block Log4Shell exploit
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 672995a6fb8c0f9e9155aa6f48edb1a52fd5cade..2858bd6c456e95adf80bb251044659e9e5c21700 100644 index 8c94a5e7257c3728c6b23b9f943de999cf673a67..7e4a166169b1ef7dd2efedba918bc988c0dc82fd 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2516,6 +2516,7 @@ public class ServerGamePacketListenerImpl @@ -2515,6 +2515,7 @@ public class ServerGamePacketListenerImpl
} }
private void tryHandleChat(String message, Runnable handler, boolean sync) { // CraftBukkit private void tryHandleChat(String message, Runnable handler, boolean sync) { // CraftBukkit
@@ -16,7 +16,7 @@ index 672995a6fb8c0f9e9155aa6f48edb1a52fd5cade..2858bd6c456e95adf80bb251044659e9
if (isChatMessageIllegal(message)) { if (isChatMessageIllegal(message)) {
this.disconnectAsync(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - add proper async disconnect this.disconnectAsync(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - add proper async disconnect
} else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales } else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales
@@ -2548,6 +2549,15 @@ public class ServerGamePacketListenerImpl @@ -2547,6 +2548,15 @@ public class ServerGamePacketListenerImpl
} }
} }

View File

@@ -5,10 +5,10 @@ Subject: [PATCH] Optimize canSee checks
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index ab3b1c7442b9f3dd72a9ce8c70dc708d2371471a..1a9b7de55555484196de695f61982c940448b262 100644 index 7ebaf03342e8802948b4f412c3f7120927f7b3b3..c47b38a9ef01c9f9d58d64b2662d1b0e51353c0d 100644
--- a/net/minecraft/server/level/ChunkMap.java --- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java
@@ -1328,7 +1328,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -1417,7 +1417,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z); flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
// Paper end - Configurable entity tracking range by Y // Paper end - Configurable entity tracking range by Y
// CraftBukkit start - respect vanish API // CraftBukkit start - respect vanish API

View File

@@ -5,7 +5,7 @@ Subject: [PATCH] Option to disable disconnect.spam
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 121daa3509879dfec7cce629e1d5260fcbccb087..e4c7307d7efbcad86cd58a06bb53540faed4b574 100644 index 4656b502b0d1025564147a917e99a58a5c31b007..f010c166a9744e1428f02aaf1a2218501435841e 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -839,7 +839,7 @@ public class ServerGamePacketListenerImpl @@ -839,7 +839,7 @@ public class ServerGamePacketListenerImpl
@@ -34,7 +34,7 @@ index 121daa3509879dfec7cce629e1d5260fcbccb087..e4c7307d7efbcad86cd58a06bb53540f
&& parseResults.getExceptions().values().stream().anyMatch(e -> e instanceof io.papermc.paper.brigadier.TagParseCommandSyntaxException)) { && parseResults.getExceptions().values().stream().anyMatch(e -> e instanceof io.papermc.paper.brigadier.TagParseCommandSyntaxException)) {
this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM);
return; return;
@@ -2643,6 +2644,7 @@ public class ServerGamePacketListenerImpl @@ -2642,6 +2643,7 @@ public class ServerGamePacketListenerImpl
// this.chatSpamThrottler.increment(); // this.chatSpamThrottler.increment();
if (!this.chatSpamThrottler.isIncrementAndUnderThreshold() if (!this.chatSpamThrottler.isIncrementAndUnderThreshold()
// CraftBukkit end // CraftBukkit end
@@ -42,7 +42,7 @@ index 121daa3509879dfec7cce629e1d5260fcbccb087..e4c7307d7efbcad86cd58a06bb53540f
&& !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.getPlayerList().isOp(this.player.getGameProfile())
&& !this.server.isSingleplayerOwner(this.player.getGameProfile())) { && !this.server.isSingleplayerOwner(this.player.getGameProfile())) {
this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause & add proper async disconnect this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause & add proper async disconnect
@@ -3400,7 +3402,7 @@ public class ServerGamePacketListenerImpl @@ -3399,7 +3401,7 @@ public class ServerGamePacketListenerImpl
public void handlePlaceRecipe(ServerboundPlaceRecipePacket packet) { public void handlePlaceRecipe(ServerboundPlaceRecipePacket packet) {
// Paper start - auto recipe limit // Paper start - auto recipe limit
if (!org.bukkit.Bukkit.isPrimaryThread()) { if (!org.bukkit.Bukkit.isPrimaryThread()) {

View File

@@ -31,7 +31,7 @@ index ca21597263cb430e2a5ae07e8cecfb0d53a270d2..226088405c019922085285ba5d04d7c1
} }
} }
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 05b8834b5566ac12655b563a1a58f123275fdedb..e05a0eed63098c535360b66276f154e4dc119d6a 100644 index b4876d35ee6a4e0dab144b471520234157d7173b..00d9e6421185b1ba4f0fdff347a53cae5b830a0a 100644
--- a/net/minecraft/server/level/ServerLevel.java --- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java
@@ -804,6 +804,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -804,6 +804,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -43,7 +43,7 @@ index 05b8834b5566ac12655b563a1a58f123275fdedb..e05a0eed63098c535360b66276f154e4
if (!tickRateManager.isEntityFrozen(entity)) { if (!tickRateManager.isEntityFrozen(entity)) {
entity.checkDespawn(); entity.checkDespawn();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 55e82d490ed3489ff671ae8a49745571c4bfb993..e96eeae793d127b76a2fe6853cba49ce192b91f9 100644 index 9971c034391c13237dc403cdc9f8806b3bfc69f4..1e201ff091bdf33c96e2cfa333030ae105ab7bc9 100644
--- a/net/minecraft/world/entity/Entity.java --- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java
@@ -365,6 +365,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @@ -365,6 +365,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -56,7 +56,7 @@ index 55e82d490ed3489ff671ae8a49745571c4bfb993..e96eeae793d127b76a2fe6853cba49ce
// Paper start - EAR 2 // Paper start - EAR 2
public final boolean defaultActivationState; public final boolean defaultActivationState;
diff --git a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java diff --git a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java
index c960166814cdb3043a38de40c59cb067ae936ba3..79d93bb4c7dd0f7c8c4cc8bafb893a0d93f70f2b 100644 index 9950fccc0a708e701b81fcabc9e8f370e6d3a19d..0159627e2c9a540d062073faf9018f5215e10866 100644
--- a/net/minecraft/world/entity/EntityType.java --- a/net/minecraft/world/entity/EntityType.java
+++ b/net/minecraft/world/entity/EntityType.java +++ b/net/minecraft/world/entity/EntityType.java
@@ -1085,6 +1085,7 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT @@ -1085,6 +1085,7 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
@@ -68,7 +68,7 @@ index c960166814cdb3043a38de40c59cb067ae936ba3..79d93bb4c7dd0f7c8c4cc8bafb893a0d
@Nullable @Nullable
private Component description; private Component description;
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index fd586acb15362a461c77256c6db9cc3a8002750d..7f1b36ca38a15aa987afb8069fd4e34bed4c569a 100644 index e1f5a4814d051a43090bf6df2acbcd20fbbc1934..c4292b95a0a90569aa8708cc3e54433d9757f1a5 100644
--- a/net/minecraft/world/entity/Mob.java --- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java
@@ -209,10 +209,10 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab @@ -209,10 +209,10 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
@@ -84,7 +84,7 @@ index fd586acb15362a461c77256c6db9cc3a8002750d..7f1b36ca38a15aa987afb8069fd4e34b
this.targetSelector.tick(); this.targetSelector.tick();
} }
} }
@@ -771,13 +771,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab @@ -758,13 +758,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
// Paper end - Allow nerfed mobs to jump and float // Paper end - Allow nerfed mobs to jump and float
this.sensing.tick(); this.sensing.tick();
int i = this.tickCount + this.getId(); int i = this.tickCount + this.getId();

View File

@@ -3,9 +3,10 @@ From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 29 Jan 2025 00:59:03 +0300 Date: Wed, 29 Jan 2025 00:59:03 +0300
Subject: [PATCH] Parallel world ticking Subject: [PATCH] Parallel world ticking
Original project: https://github.com/SparklyPower/SparklyPaper
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
index dce2b0ae83e70ccaf2ac97441f80b25876ee9058..d10c42cb10f419b59b0b9176eda97d54075d6041 100644 index dce2b0ae83e70ccaf2ac97441f80b25876ee9058..a25db7dd56e31f6a34e4b185b75c727e290e9af1 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
@@ -1155,7 +1155,7 @@ public final class ChunkHolderManager { @@ -1155,7 +1155,7 @@ public final class ChunkHolderManager {
@@ -13,7 +14,7 @@ index dce2b0ae83e70ccaf2ac97441f80b25876ee9058..d10c42cb10f419b59b0b9176eda97d54
return; return;
} }
- if (!TickThread.isTickThread()) { - if (!TickThread.isTickThread()) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && !TickThread.isTickThreadFor(world)) { // DivineMC - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && !TickThread.isTickThreadFor(world) || !TickThread.isTickThread()) { // DivineMC - Parallel world ticking
// These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update // These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update
// which will invoke processTicketUpdates // which will invoke processTicketUpdates
this.getData().offThreadPendingFullLoadUpdate.addAll(changedFullStatus); // DivineMC - Chunk System optimization this.getData().offThreadPendingFullLoadUpdate.addAll(changedFullStatus); // DivineMC - Chunk System optimization
@@ -41,6 +42,28 @@ index dce2b0ae83e70ccaf2ac97441f80b25876ee9058..d10c42cb10f419b59b0b9176eda97d54
if (!PlatformHooks.get().allowAsyncTicketUpdates() && isTickThread) { if (!PlatformHooks.get().allowAsyncTicketUpdates() && isTickThread) {
TickThread.ensureTickThread("Cannot asynchronously process ticket updates"); TickThread.ensureTickThread("Cannot asynchronously process ticket updates");
diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java
index ff747a1ecdf3c888bca0d69de4f85dcd810b6139..544c05c94b535174d97675ea3c21706dfe305438 100644
--- a/io/papermc/paper/redstone/RedstoneWireTurbo.java
+++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java
@@ -829,14 +829,9 @@ public final class RedstoneWireTurbo {
j = getMaxCurrentStrength(upd, j);
int l = 0;
- wire.shouldSignal = false;
- // Unfortunately, World.isBlockIndirectlyGettingPowered is complicated,
- // and I'm not ready to try to replicate even more functionality from
- // elsewhere in Minecraft into this accelerator. So sadly, we must
- // suffer the performance hit of this very expensive call. If there
- // is consistency to what this call returns, we may be able to cache it.
- final int k = worldIn.getBestNeighborSignal(upd.self);
- wire.shouldSignal = true;
+ // DivineMC start - Parallel world ticking
+ final int k = wire.getBlockSignal(worldIn, upd.self);
+ // DivineMC end - Parallel world ticking
// The variable 'k' holds the maximum redstone power value of any adjacent blocks.
// If 'k' has the highest level of all neighbors, then the power level of this
diff --git a/net/minecraft/core/dispenser/DispenseItemBehavior.java b/net/minecraft/core/dispenser/DispenseItemBehavior.java diff --git a/net/minecraft/core/dispenser/DispenseItemBehavior.java b/net/minecraft/core/dispenser/DispenseItemBehavior.java
index ac27ff24f018d8798921c5152e679ceed1e88d8d..ec7d1353b19e55b00c558df8981323efb9b88bdf 100644 index ac27ff24f018d8798921c5152e679ceed1e88d8d..ec7d1353b19e55b00c558df8981323efb9b88bdf 100644
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java --- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -209,6 +232,106 @@ index c067f46935753794b49f29358262273fcd15d707..cbeb56539f00a3139f7c19d29cce92fa
this.levels = Collections.unmodifiableMap(newLevels); this.levels = Collections.unmodifiableMap(newLevels);
} }
// CraftBukkit end // CraftBukkit end
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
index 10e5469df1800bcdfb3f8cb4045ee25a4bafc58c..8efed0ffdc906b6c1ba054831e481f53c11f102d 100644
--- a/net/minecraft/server/PlayerAdvancements.java
+++ b/net/minecraft/server/PlayerAdvancements.java
@@ -54,6 +54,7 @@ public class PlayerAdvancements {
private final Map<AdvancementHolder, AdvancementProgress> progress = new LinkedHashMap<>();
private final Set<AdvancementHolder> visible = new HashSet<>();
private final Set<AdvancementHolder> progressChanged = new HashSet<>();
+ private final Set<AdvancementHolder> progressChangedConcurrent = java.util.concurrent.ConcurrentHashMap.newKeySet(); // DivineMC - Parallel world ticking
private final Set<AdvancementNode> rootsToUpdate = new HashSet<>();
private ServerPlayer player;
@Nullable
@@ -88,6 +89,7 @@ public class PlayerAdvancements {
this.visible.clear();
this.rootsToUpdate.clear();
this.progressChanged.clear();
+ this.progressChangedConcurrent.clear(); // DivineMC - Parallel world ticking
this.isFirstPacket = true;
this.lastSelectedTab = null;
this.tree = manager.tree();
@@ -178,10 +180,12 @@ public class PlayerAdvancements {
return false;
}
// Paper end - Add PlayerAdvancementCriterionGrantEvent
- this.unregisterListeners(advancement);
- this.progressChanged.add(advancement);
- flag = true;
- if (!isDone && orStartProgress.isDone()) {
+ // DivineMC start - Parallel world ticking
+ this.unregisterListeners(advancement); // Must unregister criteria listeners
+ (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking ? this.progressChangedConcurrent : this.progressChanged).add(advancement);
+ flag = true; // Mark progress changed
+ if (!isDone && orStartProgress.isDone()) { // If the advancement was just completed
+ // DivineMC end - Parallel world ticking
// Paper start - Add Adventure message to PlayerAdvancementDoneEvent
final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> {
return java.util.Optional.ofNullable(
@@ -215,8 +219,10 @@ public class PlayerAdvancements {
AdvancementProgress orStartProgress = this.getOrStartProgress(advancement);
boolean isDone = orStartProgress.isDone();
if (orStartProgress.revokeProgress(criterionKey)) {
+ // DivineMC start - Parallel world ticking
this.registerListeners(advancement);
- this.progressChanged.add(advancement);
+ (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking ? this.progressChangedConcurrent : this.progressChanged).add(advancement);
+ // DivineMC end - Parallel world ticking
flag = true;
}
@@ -266,7 +272,11 @@ public class PlayerAdvancements {
}
public void flushDirty(ServerPlayer player, boolean showAdvancements) {
- if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !this.progressChanged.isEmpty()) {
+ // DivineMC start - Parallel world ticking
+ final boolean useConcurrent = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking;
+ final Set<AdvancementHolder> relevantProgressSet = useConcurrent ? this.progressChangedConcurrent : this.progressChanged;
+ if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !relevantProgressSet.isEmpty()) {
+ // DivineMC end - Parallel world ticking
Map<ResourceLocation, AdvancementProgress> map = new HashMap<>();
Set<AdvancementHolder> set = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically.
Set<ResourceLocation> set1 = new HashSet<>();
@@ -277,13 +287,23 @@ public class PlayerAdvancements {
this.rootsToUpdate.clear();
- for (AdvancementHolder advancementHolder : this.progressChanged) {
- if (this.visible.contains(advancementHolder)) {
- map.put(advancementHolder.id(), this.progress.get(advancementHolder));
+ // DivineMC start - Parallel world ticking
+ if (!relevantProgressSet.isEmpty()) {
+ Set<AdvancementHolder> toProcess = useConcurrent ? new HashSet<>(relevantProgressSet) : relevantProgressSet;
+
+ for (AdvancementHolder advancementHolder : toProcess) {
+ if (this.visible.contains(advancementHolder)) {
+ map.put(advancementHolder.id(), this.progress.get(advancementHolder));
+ }
+ }
+
+ if (useConcurrent) {
+ this.progressChangedConcurrent.removeAll(toProcess);
+ } else {
+ this.progressChanged.clear();
}
}
-
- this.progressChanged.clear();
+ // DivineMC end - Parallel world ticking
if (!map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) {
player.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, set, set1, map, showAdvancements));
}
@@ -328,7 +348,7 @@ public class PlayerAdvancements {
if (this.visible.add(advancementHolder)) {
advancementOutput.add(advancementHolder);
if (this.progress.containsKey(advancementHolder)) {
- this.progressChanged.add(advancementHolder);
+ (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking ? this.progressChangedConcurrent : this.progressChanged).add(advancementHolder); // DivineMC - Parallel world ticking
}
}
} else if (this.visible.remove(advancementHolder)) {
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 978934f81ba023d7565d2e66c51f6ca249510702..13d85eb366a070bfd6723088412f51af07892362 100644 index 978934f81ba023d7565d2e66c51f6ca249510702..13d85eb366a070bfd6723088412f51af07892362 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java --- a/net/minecraft/server/dedicated/DedicatedServer.java
@@ -227,8 +350,20 @@ index 978934f81ba023d7565d2e66c51f6ca249510702..13d85eb366a070bfd6723088412f51af
this.setPvpAllowed(properties.pvp); this.setPvpAllowed(properties.pvp);
this.setFlightAllowed(properties.allowFlight); this.setFlightAllowed(properties.allowFlight);
this.setMotd(properties.motd); this.setMotd(properties.motd);
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 7205fc8d3b17863c262d4c4c3cb956c852468c6f..3cf54630d36f821a232fa03f9094c4c1f70902a1 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -175,7 +175,6 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
// call mid-tick tasks for chunk system
if ((i & 7) == 0) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks();
continue;
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index e05a0eed63098c535360b66276f154e4dc119d6a..0102d87411dcf0bba7f6873dc54385c957a7e2d7 100644 index 00d9e6421185b1ba4f0fdff347a53cae5b830a0a..c3ed824b2c31902b50de74df37d9d8fd504454fc 100644
--- a/net/minecraft/server/level/ServerLevel.java --- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java
@@ -181,7 +181,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -181,7 +181,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -248,6 +383,15 @@ index e05a0eed63098c535360b66276f154e4dc119d6a..0102d87411dcf0bba7f6873dc54385c9
// CraftBukkit start // CraftBukkit start
public final LevelStorageSource.LevelStorageAccess levelStorageAccess; public final LevelStorageSource.LevelStorageAccess levelStorageAccess;
@@ -681,7 +682,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.sleepStatus = new SleepStatus();
this.gameEventDispatcher = new GameEventDispatcher(this);
this.randomSequences = Objects.requireNonNullElseGet(randomSequences, () -> this.getDataStorage().computeIfAbsent(RandomSequences.TYPE));
- this.waypointManager = new ServerWaypointManager();
+ this.waypointManager = new ServerWaypointManager(this); // DivineMC - Parallel world ticking
// Paper start - rewrite chunk system
this.moonrise$setEntityLookup(new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup((ServerLevel)(Object)this, ((ServerLevel)(Object)this).new EntityCallbacks()));
this.chunkTaskScheduler = new ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler((ServerLevel)(Object)this);
@@ -698,6 +699,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -698,6 +699,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.chunkDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.ChunkDataController((ServerLevel)(Object)this, this.chunkTaskScheduler); this.chunkDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.ChunkDataController((ServerLevel)(Object)this, this.chunkTaskScheduler);
// Paper end - rewrite chunk system // Paper end - rewrite chunk system
@@ -316,7 +460,7 @@ index e05a0eed63098c535360b66276f154e4dc119d6a..0102d87411dcf0bba7f6873dc54385c9
// Paper start - extra debug info // Paper start - extra debug info
if (entity.valid) { if (entity.valid) {
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index c7d081aff58170251d075a58d5e1345fc1bda9f8..7f5ec82ad273226b34d0212e17dfd1fe905d68a0 100644 index 95aabca0b1f2f3762df52e9a09afe19ed1939209..87fe8f80e76a63f7ab4f55668524f97be01e6929 100644
--- a/net/minecraft/server/level/ServerPlayer.java --- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java
@@ -433,6 +433,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -433,6 +433,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -403,6 +547,91 @@ index 015e0d1400f1dfbe086c817f121d01f0726e82f2..199e0abdd3a00786a259e28db9d826df
// CraftBukkit end // CraftBukkit end
serverPlayer.connection = player.connection; serverPlayer.connection = player.connection;
serverPlayer.restoreFrom(player, keepInventory); serverPlayer.restoreFrom(player, keepInventory);
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
index f9e7532f86122a379692561a639a209a126e8bba..839f6b7696ef85314da185bedba7cfc5870c314a 100644
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
@@ -19,9 +19,17 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
private final Set<WaypointTransmitter> waypoints = new HashSet<>();
private final Set<ServerPlayer> players = new HashSet<>();
private final Table<ServerPlayer, WaypointTransmitter, WaypointTransmitter.Connection> connections = HashBasedTable.create();
+ // DivineMC start - Parallel world ticking
+ private final net.minecraft.server.level.ServerLevel level;
+
+ public ServerWaypointManager(net.minecraft.server.level.ServerLevel level) {
+ this.level = level;
+ }
+ // DivineMC end - Parallel world ticking
@Override
public void trackWaypoint(WaypointTransmitter waypoint) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot track waypoints off-main"); // DivineMC - Parallel world ticking
this.waypoints.add(waypoint);
for (ServerPlayer serverPlayer : this.players) {
@@ -31,6 +39,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
@Override
public void updateWaypoint(WaypointTransmitter waypoint) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot update waypoints off-main"); // DivineMC - Parallel world ticking
if (this.waypoints.contains(waypoint)) {
Map<ServerPlayer, WaypointTransmitter.Connection> map = Tables.transpose(this.connections).row(waypoint);
SetView<ServerPlayer> set = Sets.difference(this.players, map.keySet());
@@ -47,12 +56,14 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
@Override
public void untrackWaypoint(WaypointTransmitter waypoint) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot untrack waypoints off-main"); // DivineMC - Parallel world ticking
this.connections.column(waypoint).forEach((serverPlayer, connection) -> connection.disconnect());
Tables.transpose(this.connections).row(waypoint).clear();
this.waypoints.remove(waypoint);
}
public void addPlayer(ServerPlayer player) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot add player to waypoints off-main"); // DivineMC - Parallel world ticking
this.players.add(player);
for (WaypointTransmitter waypointTransmitter : this.waypoints) {
@@ -65,6 +76,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
}
public void updatePlayer(ServerPlayer player) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot update player for waypoints off-main"); // DivineMC - Parallel world ticking
Map<WaypointTransmitter, WaypointTransmitter.Connection> map = this.connections.row(player);
SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, map.keySet());
@@ -78,6 +90,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
}
public void removePlayer(ServerPlayer player) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot remove player from waypoints off-main"); // DivineMC - Parallel world ticking
this.connections.row(player).values().removeIf(connection -> {
connection.disconnect();
return true;
@@ -87,6 +100,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
}
public void breakAllConnections() {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot break all waypoint connections off-main"); // DivineMC - Parallel world ticking
this.connections.values().forEach(WaypointTransmitter.Connection::disconnect);
this.connections.clear();
}
@@ -106,6 +120,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
}
private void createConnection(ServerPlayer player, WaypointTransmitter waypoint) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot create waypoint connections off-main"); // DivineMC - Parallel world ticking
if (player != waypoint) {
if (isLocatorBarEnabledFor(player)) {
waypoint.makeWaypointConnectionWith(player).ifPresentOrElse(connection -> {
@@ -122,6 +137,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
}
private void updateConnection(ServerPlayer player, WaypointTransmitter waypoint, WaypointTransmitter.Connection connection) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot update waypoint connection off-main"); // DivineMC - Parallel world ticking
if (player != waypoint) {
if (isLocatorBarEnabledFor(player)) {
if (!connection.isBroken()) {
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 1e201ff091bdf33c96e2cfa333030ae105ab7bc9..5a58fb937419369d887c4fc35d9383d1b5e35a99 100644 index 1e201ff091bdf33c96e2cfa333030ae105ab7bc9..5a58fb937419369d887c4fc35d9383d1b5e35a99 100644
--- a/net/minecraft/world/entity/Entity.java --- a/net/minecraft/world/entity/Entity.java
@@ -517,37 +746,48 @@ index 91f6d43b3785ddad7db8eb529ba3293c45f3588d..fc3ab0881bf9b275beb9b32ca5a7475d
+ // DivineMC end - Parallel world ticking + // DivineMC end - Parallel world ticking
} }
diff --git a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java diff --git a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java
index 3614551856c594f3c0cfee984fcf03fad672b007..ad52152d090c32b81044b8fd9496eae0b4e755b0 100644 index 3614551856c594f3c0cfee984fcf03fad672b007..d972bcdba9c26cb66fedae58ca9658bb465e3af2 100644
--- a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java --- a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java
+++ b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java +++ b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java
@@ -46,12 +46,22 @@ public class GoToPotentialJobSite extends Behavior<Villager> { @@ -44,15 +44,31 @@ public class GoToPotentialJobSite extends Behavior<Villager> {
Optional<GlobalPos> memory = entity.getBrain().getMemory(MemoryModuleType.POTENTIAL_JOB_SITE);
memory.ifPresent(globalPos -> {
BlockPos blockPos = globalPos.pos(); BlockPos blockPos = globalPos.pos();
ServerLevel level1 = level.getServer().getLevel(globalPos.dimension()); - ServerLevel level1 = level.getServer().getLevel(globalPos.dimension());
if (level1 != null) { - if (level1 != null) {
- PoiManager poiManager = level1.getPoiManager(); - PoiManager poiManager = level1.getPoiManager();
- if (poiManager.exists(blockPos, holder -> true)) { - if (poiManager.exists(blockPos, holder -> true)) {
- poiManager.release(blockPos); - poiManager.release(blockPos);
- } - }
+ // DivineMC start - Parallel world ticking + // DivineMC start - Parallel world ticking
+ Runnable releasePoiTask = () -> { + ServerLevel entityLevel = level;
+ PoiManager poiManager = level1.getPoiManager(); + ServerLevel poiLevel = entityLevel.getServer().getLevel(globalPos.dimension());
+
+ if (poiLevel != null) {
+ Runnable poiOperationsTask = () -> {
+ PoiManager poiManager = poiLevel.getPoiManager();
+ if (poiManager.exists(blockPos, holder -> true)) { + if (poiManager.exists(blockPos, holder -> true)) {
+ poiManager.release(blockPos); + poiManager.release(blockPos);
+ } + }
+
+ DebugPackets.sendPoiTicketCountPacket(level, blockPos);
+ }; + };
- DebugPackets.sendPoiTicketCountPacket(level, blockPos); - DebugPackets.sendPoiTicketCountPacket(level, blockPos);
+ Runnable debugPacketTask = () -> {
+ DebugPackets.sendPoiTicketCountPacket(entityLevel, blockPos);
+ };
+
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) {
+ level.moonrise$getChunkTaskScheduler().scheduleChunkTask(0, 0, releasePoiTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); + poiLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(blockPos.getX() >> 4, blockPos.getZ() >> 4, poiOperationsTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING);
+ entityLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(entity.chunkPosition().x, entity.chunkPosition().z, debugPacketTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING);
+ } else { + } else {
+ releasePoiTask.run(); + poiOperationsTask.run();
+ debugPacketTask.run();
+ } + }
+ // DivineMC end - Parallel world ticking
} }
+ // DivineMC end - Parallel world ticking
}); });
entity.getBrain().eraseMemory(MemoryModuleType.POTENTIAL_JOB_SITE); entity.getBrain().eraseMemory(MemoryModuleType.POTENTIAL_JOB_SITE);
}
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
index 3e23bc54e0315b6324843851e072005ca4da3f8e..50130de436bb12334f5b7f63bf6bf30578b569b2 100644 index 3e23bc54e0315b6324843851e072005ca4da3f8e..50130de436bb12334f5b7f63bf6bf30578b569b2 100644
--- a/net/minecraft/world/entity/npc/Villager.java --- a/net/minecraft/world/entity/npc/Villager.java
@@ -826,10 +1066,18 @@ index d306f5f524dc64618df94c9783c2168dc561a5e3..6a0c4dc2ff5e3d82e811db63dc9da7b9
return true; return true;
} else { } else {
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..9585d3164eaa7522be950fe10d8487dcc88e0f0b 100644 index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..48c5c13f993f5c30912833a99c82071942c83401 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java --- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -283,7 +283,13 @@ public class RedStoneWireBlock extends Block { @@ -66,6 +66,7 @@ public class RedStoneWireBlock extends Block {
private final BlockState crossState;
private final RedstoneWireEvaluator evaluator = new DefaultRedstoneWireEvaluator(this);
public boolean shouldSignal = true;
+ private final ThreadLocal<Boolean> shouldSignalTL = ThreadLocal.withInitial(() -> true); // DivineMC - Parallel world ticking
@Override
public MapCodec<RedStoneWireBlock> codec() {
@@ -283,7 +284,13 @@ public class RedStoneWireBlock extends Block {
if (orientation != null) { if (orientation != null) {
source = pos.relative(orientation.getFront().getOpposite()); source = pos.relative(orientation.getFront().getOpposite());
} }
@@ -844,7 +1092,7 @@ index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..9585d3164eaa7522be950fe10d8487dc
return; return;
} }
updatePowerStrength(worldIn, pos, state, orientation, blockAdded); updatePowerStrength(worldIn, pos, state, orientation, blockAdded);
@@ -311,7 +317,13 @@ public class RedStoneWireBlock extends Block { @@ -311,7 +318,13 @@ public class RedStoneWireBlock extends Block {
// [Space Walker] suppress shape updates and emit those manually to // [Space Walker] suppress shape updates and emit those manually to
// bypass the new neighbor update stack. // bypass the new neighbor update stack.
if (level.setBlock(pos, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS)) { if (level.setBlock(pos, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS)) {
@@ -859,6 +1107,71 @@ index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..9585d3164eaa7522be950fe10d8487dc
} }
} }
} }
@@ -328,10 +341,19 @@ public class RedStoneWireBlock extends Block {
}
public int getBlockSignal(Level level, BlockPos pos) {
- this.shouldSignal = false;
- int bestNeighborSignal = level.getBestNeighborSignal(pos);
- this.shouldSignal = true;
- return bestNeighborSignal;
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) {
+ this.shouldSignalTL.set(false);
+ int bestNeighborSignal = level.getBestNeighborSignal(pos);
+ this.shouldSignalTL.set(true);
+ return bestNeighborSignal;
+ } else {
+ this.shouldSignal = false;
+ int bestNeighborSignal = level.getBestNeighborSignal(pos);
+ this.shouldSignal = true;
+ return bestNeighborSignal;
+ }
+ // DivineMC end - Parallel world ticking
}
private void checkCornerChangeAt(Level level, BlockPos pos) {
@@ -422,12 +444,23 @@ public class RedStoneWireBlock extends Block {
@Override
protected int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) {
- return !this.shouldSignal ? 0 : blockState.getSignal(blockAccess, pos, side);
+ // DivineMC start - Parallel world ticking
+ boolean signal = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking ? this.shouldSignalTL.get() : this.shouldSignal;
+ return !signal ? 0 : blockState.getSignal(blockAccess, pos, side);
+ // DivineMC end - Parallel world ticking
}
@Override
protected int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) {
- if (this.shouldSignal && side != Direction.DOWN) {
+ // DivineMC start - Parallel world ticking
+ boolean signal;
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) {
+ signal = this.shouldSignalTL.get();
+ } else {
+ signal = this.shouldSignal;
+ }
+ // DivineMC end - Parallel world ticking
+ if (signal && side != Direction.DOWN) { // DivineMC - Parallel world ticking
int powerValue = blockState.getValue(POWER);
if (powerValue == 0) {
return 0;
@@ -459,7 +492,13 @@ public class RedStoneWireBlock extends Block {
@Override
protected boolean isSignalSource(BlockState state) {
- return this.shouldSignal;
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) {
+ return this.shouldSignalTL.get();
+ } else {
+ return this.shouldSignal;
+ }
+ // DivineMC end - Parallel world ticking
}
public static int getColorForPower(int power) {
diff --git a/net/minecraft/world/level/block/SaplingBlock.java b/net/minecraft/world/level/block/SaplingBlock.java diff --git a/net/minecraft/world/level/block/SaplingBlock.java b/net/minecraft/world/level/block/SaplingBlock.java
index a22cb810622e0ae97bc2a0d6390d026d9482b783..5856178e41523700ca7ed9a46c1c802c33903b53 100644 index a22cb810622e0ae97bc2a0d6390d026d9482b783..5856178e41523700ca7ed9a46c1c802c33903b53 100644
--- a/net/minecraft/world/level/block/SaplingBlock.java --- a/net/minecraft/world/level/block/SaplingBlock.java
@@ -1042,17 +1355,22 @@ index 010c7204bfa772ff9a2187a5db76d7c65ea34f66..6de61b3e75edee87562e3aab01c7020c
LevelChunkSection section = this.getSection(this.getSectionIndex(y)); LevelChunkSection section = this.getSection(this.getSectionIndex(y));
boolean hasOnlyAir = section.hasOnlyAir(); boolean hasOnlyAir = section.hasOnlyAir();
diff --git a/net/minecraft/world/level/entity/EntityTickList.java b/net/minecraft/world/level/entity/EntityTickList.java diff --git a/net/minecraft/world/level/entity/EntityTickList.java b/net/minecraft/world/level/entity/EntityTickList.java
index 9e75320e51886e0f93c23683d8614128f44a613e..df8ede5c6d1f1af275f813d4b939136134214dff 100644 index 9e75320e51886e0f93c23683d8614128f44a613e..d8b3e0901922016a22ca2801c2c4e0afd28e1c0a 100644
--- a/net/minecraft/world/level/entity/EntityTickList.java --- a/net/minecraft/world/level/entity/EntityTickList.java
+++ b/net/minecraft/world/level/entity/EntityTickList.java +++ b/net/minecraft/world/level/entity/EntityTickList.java
@@ -10,17 +10,26 @@ import net.minecraft.world.entity.Entity; @@ -10,17 +10,31 @@ import net.minecraft.world.entity.Entity;
public class EntityTickList { public class EntityTickList {
public final java.util.concurrent.ConcurrentLinkedQueue<Entity> entities = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Paper - rewrite chunk system // DivineMC - Async mob spawning public final java.util.concurrent.ConcurrentLinkedQueue<Entity> entities = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Paper - rewrite chunk system // DivineMC - Async mob spawning
+ // DivineMC start - Parallel world ticking + // DivineMC start - Parallel world ticking
+ @Nullable
+ private final net.minecraft.server.level.ServerLevel serverLevel; + private final net.minecraft.server.level.ServerLevel serverLevel;
+ +
+ public EntityTickList(net.minecraft.server.level.ServerLevel serverLevel) { + public EntityTickList() {
+ this(null);
+ }
+
+ public EntityTickList(@Nullable net.minecraft.server.level.ServerLevel serverLevel) {
+ this.serverLevel = serverLevel; + this.serverLevel = serverLevel;
+ } + }
+ // DivineMC end - Parallel world ticking + // DivineMC end - Parallel world ticking
@@ -1072,7 +1390,7 @@ index 9e75320e51886e0f93c23683d8614128f44a613e..df8ede5c6d1f1af275f813d4b9391361
this.ensureActiveIsNotIterated(); this.ensureActiveIsNotIterated();
this.entities.remove(entity); // Paper - rewrite chunk system this.entities.remove(entity); // Paper - rewrite chunk system
} }
@@ -30,6 +39,7 @@ public class EntityTickList { @@ -30,6 +44,7 @@ public class EntityTickList {
} }
public void forEach(Consumer<Entity> entity) { public void forEach(Consumer<Entity> entity) {
@@ -1080,3 +1398,23 @@ index 9e75320e51886e0f93c23683d8614128f44a613e..df8ede5c6d1f1af275f813d4b9391361
// Paper start - rewrite chunk system // Paper start - rewrite chunk system
// To ensure nothing weird happens with dimension travelling, do not iterate over new entries... // 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) // (by dfl iterator() is configured to not iterate over new entries)
diff --git a/net/minecraft/world/level/saveddata/maps/MapIndex.java b/net/minecraft/world/level/saveddata/maps/MapIndex.java
index 06025d79cc2297119b22224d777aca79f9d3d9c1..53e6a478eafec512be42422898e7fb334015946b 100644
--- a/net/minecraft/world/level/saveddata/maps/MapIndex.java
+++ b/net/minecraft/world/level/saveddata/maps/MapIndex.java
@@ -23,8 +23,12 @@ public class MapIndex extends SavedData {
}
public MapId getNextMapId() {
- MapId mapId = new MapId(++this.lastMapId);
- this.setDirty();
- return mapId;
+ // DivineMC start - Parallel world ticking
+ synchronized (TYPE) {
+ MapId mapId = new MapId(++this.lastMapId);
+ this.setDirty();
+ return mapId;
+ }
+ // DivineMC end - Parallel world ticking
}
}

View File

@@ -21,7 +21,7 @@ index 343ec870d6ea3e2792f369c4867a3afb4bcfa385..db909744c922f2e5b486a8f15dac79d6
if (var2 instanceof ClosedChannelException) { if (var2 instanceof ClosedChannelException) {
LOGGER.info("Connection closed during protocol change"); LOGGER.info("Connection closed during protocol change");
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 7205fc8d3b17863c262d4c4c3cb956c852468c6f..9f6f8aace06702152117efc8252aefb7197b73b9 100644 index 3cf54630d36f821a232fa03f9094c4c1f70902a1..5f62eee8a60d7fcadb1f9efb7473bc5c20c47263 100644
--- a/net/minecraft/server/level/ServerChunkCache.java --- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -57,6 +57,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -57,6 +57,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -45,7 +45,7 @@ index 7205fc8d3b17863c262d4c4c3cb956c852468c6f..9f6f8aace06702152117efc8252aefb7
@Nullable @Nullable
@VisibleForDebug @VisibleForDebug
private NaturalSpawner.SpawnState lastSpawnState; private NaturalSpawner.SpawnState lastSpawnState;
@@ -153,32 +156,241 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -153,31 +156,240 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
return load ? this.syncLoad(chunkX, chunkZ, toStatus) : null; return load ? this.syncLoad(chunkX, chunkZ, toStatus) : null;
} }
// Paper end - rewrite chunk system // Paper end - rewrite chunk system
@@ -268,11 +268,7 @@ index 7205fc8d3b17863c262d4c4c3cb956c852468c6f..9f6f8aace06702152117efc8252aefb7
+ latch.await(); + latch.await();
+ return true; + return true;
+ } + }
+
- // call mid-tick tasks for chunk system
- if ((i & 7) == 0) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks();
- continue;
+ @Override + @Override
+ public boolean isReleasable() { + public boolean isReleasable() {
+ return latch.getCount() == 0; + return latch.getCount() == 0;
@@ -285,10 +281,12 @@ index 7205fc8d3b17863c262d4c4c3cb956c852468c6f..9f6f8aace06702152117efc8252aefb7
+ java.util.Objects.checkFromToIndex(0, size, raw.length); + java.util.Objects.checkFromToIndex(0, size, raw.length);
+ for (int i = 0; i < size; ++i) { + for (int i = 0; i < size; ++i) {
+ world.tickChunk(raw[i], randomTickSpeed); + world.tickChunk(raw[i], randomTickSpeed);
+
- // call mid-tick tasks for chunk system
- if ((i & 7) == 0) {
- continue;
+ // call mid-tick tasks for chunk system + // call mid-tick tasks for chunk system
+ if ((i & 7) == 0) { + if ((i & 7) == 0) {
+ ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer) this.level.getServer()).moonrise$executeMidTickTasks();
+ continue; + continue;
+ } + }
} }
@@ -297,7 +295,7 @@ index 7205fc8d3b17863c262d4c4c3cb956c852468c6f..9f6f8aace06702152117efc8252aefb7
} }
// Paper end - chunk tick iteration optimisations // Paper end - chunk tick iteration optimisations
@@ -502,14 +714,21 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -501,14 +713,21 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
long gameTime = this.level.getGameTime(); long gameTime = this.level.getGameTime();
long l = gameTime - this.lastInhabitedUpdate; long l = gameTime - this.lastInhabitedUpdate;
this.lastInhabitedUpdate = gameTime; this.lastInhabitedUpdate = gameTime;
@@ -323,7 +321,7 @@ index 7205fc8d3b17863c262d4c4c3cb956c852468c6f..9f6f8aace06702152117efc8252aefb7
// DivineMC start - Async mob spawning // DivineMC start - Async mob spawning
if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableAsyncSpawning) { if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableAsyncSpawning) {
for (ServerPlayer player : this.level.players) { for (ServerPlayer player : this.level.players) {
@@ -538,14 +757,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -537,14 +756,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
} }
private void broadcastChangedChunks() { private void broadcastChangedChunks() {
@@ -348,7 +346,7 @@ index 7205fc8d3b17863c262d4c4c3cb956c852468c6f..9f6f8aace06702152117efc8252aefb7
} }
private void tickChunks(long timeInhabited) { private void tickChunks(long timeInhabited) {
@@ -595,25 +818,28 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -594,25 +817,28 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
filteredSpawningCategories = List.of(); filteredSpawningCategories = List.of();
} }
@@ -394,7 +392,7 @@ index 7205fc8d3b17863c262d4c4c3cb956c852468c6f..9f6f8aace06702152117efc8252aefb7
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
} }
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 690b6d9f89f70afd8f37d907863b87dd1f4591af..7ae5ba48632528dfe5bbdedf252b5995f5452f1c 100644 index 6edf0d8a4b8b82f85eac4d89cafd24e6730a61c6..83b46b31d1c0b565deeefe3dafa3b0287f14fb00 100644
--- a/net/minecraft/server/level/ServerLevel.java --- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java
@@ -192,7 +192,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -192,7 +192,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe

View File

@@ -1,26 +0,0 @@
--- a/net/minecraft/server/PlayerAdvancements.java
+++ b/net/minecraft/server/PlayerAdvancements.java
@@ -51,16 +_,18 @@
private final PlayerList playerList;
private final Path playerSavePath;
private AdvancementTree tree;
- private final Map<AdvancementHolder, AdvancementProgress> progress = new LinkedHashMap<>();
- private final Set<AdvancementHolder> visible = new HashSet<>();
- private final Set<AdvancementHolder> progressChanged = new HashSet<>();
- private final Set<AdvancementNode> rootsToUpdate = new HashSet<>();
+ // DivineMC start - Use synchronized map
+ private final Map<AdvancementHolder, AdvancementProgress> progress = java.util.Collections.synchronizedMap(new LinkedHashMap<>());
+ private final Set<AdvancementHolder> visible = com.google.common.collect.Sets.newConcurrentHashSet();
+ private final Set<AdvancementHolder> progressChanged = com.google.common.collect.Sets.newConcurrentHashSet();
+ private final Set<AdvancementNode> rootsToUpdate = com.google.common.collect.Sets.newConcurrentHashSet();
+ // DivineMC end - Use synchronized map
private ServerPlayer player;
@Nullable
private AdvancementHolder lastSelectedTab;
private boolean isFirstPacket = true;
private final Codec<PlayerAdvancements.Data> codec;
- public final Map<net.minecraft.advancements.critereon.SimpleCriterionTrigger<?>, Set<CriterionTrigger.Listener<?>>> criterionData = new java.util.IdentityHashMap<>(); // Paper - fix advancement data player leakage
+ public final Map<net.minecraft.advancements.critereon.SimpleCriterionTrigger<?>, Set<CriterionTrigger.Listener<?>>> criterionData = java.util.Collections.synchronizedMap(new java.util.IdentityHashMap<>()); // Paper - fix advancement data player leakage // DivineMC - Use synchronized map
public PlayerAdvancements(DataFixer dataFixer, PlayerList playerList, ServerAdvancementManager manager, Path playerSavePath, ServerPlayer player) {
this.playerList = playerList;

View File

@@ -21,6 +21,19 @@ index d7398b1ecf2660c29fb7d106b48fe02d3736603e..ab499a7eaccdc1578ec64f90f54f79b0
throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
} }
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index f81a6a2a9e1adaa3418c301984afeb19441f2039..efa472345ec74e21ca1cf05645b3ab39eda3caa4 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -2875,7 +2875,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
Iterator<AttributeInstance> iterator = collection.iterator();
while (iterator.hasNext()) {
AttributeInstance genericInstance = iterator.next();
- if (genericInstance.getAttribute() == Attributes.MAX_HEALTH) {
+ if (genericInstance != null && genericInstance.getAttribute() == Attributes.MAX_HEALTH) {
iterator.remove();
break;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 98b766d115856dbf3ea11a983c1304591032f1b0..9b9b8b3439cfc0c36009fdb9e5d46b64c3aca393 100644 index 98b766d115856dbf3ea11a983c1304591032f1b0..9b9b8b3439cfc0c36009fdb9e5d46b64c3aca393 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java

View File

@@ -3,7 +3,6 @@ package org.bxteam.divinemc.entity.tracking;
import ca.spottedleaf.moonrise.common.list.ReferenceList; import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers; import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.common.util.TickThread; import ca.spottedleaf.moonrise.common.util.TickThread;
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.chunk_system.level.entity.server.ServerEntityLookup;
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity; import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
@@ -19,7 +18,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
@@ -31,7 +29,7 @@ public class MultithreadedTracker {
private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX); private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX);
private static long lastWarnMillis = System.currentTimeMillis(); private static long lastWarnMillis = System.currentTimeMillis();
private static final ThreadPoolExecutor trackerExecutor = new ThreadPoolExecutor( private static final ThreadPoolExecutor TRACKER_EXECUTOR = new ThreadPoolExecutor(
getCorePoolSize(), getCorePoolSize(),
getMaxPoolSize(), getMaxPoolSize(),
getKeepAliveTime(), TimeUnit.SECONDS, getKeepAliveTime(), TimeUnit.SECONDS,
@@ -40,11 +38,7 @@ public class MultithreadedTracker {
getRejectedPolicy() getRejectedPolicy()
); );
public static Executor getTrackerExecutor() { public static void tick(ServerLevel level) {
return trackerExecutor;
}
public static void tick(ChunkSystemServerLevel level) {
try { try {
if (!DivineConfig.AsyncCategory.multithreadedCompatModeEnabled) { if (!DivineConfig.AsyncCategory.multithreadedCompatModeEnabled) {
tickAsync(level); tickAsync(level);
@@ -56,15 +50,14 @@ public class MultithreadedTracker {
} }
} }
private static void tickAsync(ChunkSystemServerLevel level) { private static void tickAsync(ServerLevel level) {
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers(); final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup(); final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities; final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
// Move tracking to off-main TRACKER_EXECUTOR.execute(() -> {
trackerExecutor.execute(() -> {
for (final Entity entity : trackerEntitiesRaw) { for (final Entity entity : trackerEntitiesRaw) {
if (entity == null) continue; if (entity == null) continue;
@@ -72,19 +65,23 @@ public class MultithreadedTracker {
if (tracker == null) continue; if (tracker == null) continue;
tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); synchronized (tracker) {
var trackedChunk = nearbyPlayers.getChunk(entity.chunkPosition());
tracker.moonrise$tick(trackedChunk);
tracker.serverEntity.sendChanges(); tracker.serverEntity.sendChanges();
} }
}
}); });
} }
private static void tickAsyncWithCompatMode(ChunkSystemServerLevel level) { private static void tickAsyncWithCompatMode(ServerLevel level) {
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers(); final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup(); final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities; final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
final Runnable[] sendChangesTasks = new Runnable[trackerEntitiesRaw.length]; final Runnable[] sendChangesTasks = new Runnable[trackerEntitiesRaw.length];
final Runnable[] tickTask = new Runnable[trackerEntitiesRaw.length];
int index = 0; int index = 0;
for (final Entity entity : trackerEntitiesRaw) { for (final Entity entity : trackerEntitiesRaw) {
@@ -94,12 +91,19 @@ public class MultithreadedTracker {
if (tracker == null) continue; if (tracker == null) continue;
tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); synchronized (tracker) {
sendChangesTasks[index++] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array tickTask[index] = tracker.tickCompact(nearbyPlayers.getChunk(entity.chunkPosition()));
sendChangesTasks[index] = () -> tracker.serverEntity.sendChanges();
}
index++;
} }
// batch submit tasks TRACKER_EXECUTOR.execute(() -> {
trackerExecutor.execute(() -> { for (final Runnable tick : tickTask) {
if (tick == null) continue;
tick.run();
}
for (final Runnable sendChanges : sendChangesTasks) { for (final Runnable sendChanges : sendChangesTasks) {
if (sendChanges == null) continue; if (sendChanges == null) continue;

View File

@@ -0,0 +1,326 @@
package org.bxteam.divinemc.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;
/**
* @author hayanesuru
*/
public final class AttributeInstanceArrayMap implements Map<Holder<Attribute>, AttributeInstance>, Cloneable {
private static final int VANILLA_ATTRIBUTE_SIZE = 35; // 1.21.6
private int size = 0;
private transient AttributeInstance[] a = new AttributeInstance[VANILLA_ATTRIBUTE_SIZE];
private transient KeySet keys;
private transient Values values;
private transient EntrySet entries;
public AttributeInstanceArrayMap() {
if (BuiltInRegistries.ATTRIBUTE.size() != VANILLA_ATTRIBUTE_SIZE) {
throw new IllegalStateException("Unexpected registry minecraft:attribute size");
}
}
public AttributeInstanceArrayMap(final @NotNull Map<Holder<Attribute>, AttributeInstance> m) {
this();
putAll(m);
}
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 int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public 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 boolean containsValue(Object value) {
return value instanceof AttributeInstance val && Objects.equals(getInstance(val.getAttribute().value().uid), val);
}
@Override
public AttributeInstance get(Object key) {
return key instanceof Holder<?> holder && holder.value() instanceof Attribute attribute ? a[attribute.uid] : null;
}
@Nullable
public AttributeInstance getInstance(int key) {
return a[key];
}
@Override
public AttributeInstance put(@NotNull Holder<Attribute> key, AttributeInstance value) {
int uid = key.value().uid;
AttributeInstance prev = a[uid];
setByIndex(uid, value);
return prev;
}
@Override
public 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 void putAll(@NotNull Map<? extends Holder<Attribute>, ? extends AttributeInstance> m) {
for (AttributeInstance e : m.values()) {
if (e != null) {
setByIndex(e.getAttribute().value().uid, e);
}
}
}
@Override
public void clear() {
Arrays.fill(a, null);
size = 0;
}
@Override
public @NotNull Set<Holder<Attribute>> keySet() {
if (keys == null) {
keys = new KeySet();
}
return keys;
}
@Override
public @NotNull Collection<AttributeInstance> values() {
if (values == null) {
values = new Values();
}
return values;
}
@Override
public @NotNull Set<Entry<Holder<Attribute>, AttributeInstance>> entrySet() {
if (entries == null) {
entries = new EntrySet();
}
return entries;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Map<?, ?> s)) return false;
if (s.size() != size()) return false;
if (o instanceof AttributeInstanceArrayMap that) {
return Arrays.equals(a, that.a);
}
for (Entry<?, ?> e : s.entrySet()) {
if (!Objects.equals(get(e.getKey()), e.getValue())) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return Arrays.hashCode(a);
}
@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.get(currentIndex).orElseThrow();
}
@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.get(nextIndex).orElseThrow();
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;
}
}

View File

@@ -0,0 +1,116 @@
package org.bxteam.divinemc.util.map;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Array;
import java.util.*;
/**
* @author hayanesuru
*/
public final class AttributeInstanceSet extends AbstractCollection<AttributeInstance> implements Set<AttributeInstance> {
public final IntSet inner;
public final AttributeInstanceArrayMap map;
public AttributeInstanceSet(AttributeInstanceArrayMap map) {
this.map = map;
inner = new IntArraySet();
}
@Override
public boolean add(AttributeInstance instance) {
return inner.add(instance.getAttribute().value().uid);
}
@Override
public boolean remove(Object o) {
return o instanceof AttributeInstance instance && inner.remove(instance.getAttribute().value().uid);
}
@Override
public @NotNull Iterator<AttributeInstance> iterator() {
return new CloneIterator(inner.toIntArray(), map);
}
@Override
public int size() {
return inner.size();
}
@Override
public boolean isEmpty() {
return inner.isEmpty();
}
@Override
public void clear() {
inner.clear();
}
@Override
public boolean contains(Object o) {
if (o instanceof AttributeInstance instance) {
return inner.contains(instance.getAttribute().value().uid);
}
return false;
}
@Override
public AttributeInstance @NotNull [] toArray() {
int[] innerClone = inner.toIntArray();
AttributeInstance[] arr = new AttributeInstance[innerClone.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = map.getInstance(innerClone[i]);
}
return arr;
}
@SuppressWarnings({"unchecked"})
@Override
public <T> T @NotNull [] toArray(T[] a) {
if (a == null || (a.getClass() == AttributeInstance[].class && a.length == 0)) {
return (T[]) toArray();
}
if (a.length < size()) {
a = (T[]) Array.newInstance(a.getClass().getComponentType(), size());
}
System.arraycopy((T[]) toArray(), 0, a, 0, size());
if (a.length > size()) {
a[size()] = null;
}
return a;
}
static class CloneIterator implements Iterator<AttributeInstance> {
private final int[] array;
private int index = 0;
private final AttributeInstanceArrayMap map;
CloneIterator(int[] array, AttributeInstanceArrayMap map) {
this.array = array;
this.map = map;
}
@Override
public boolean hasNext() {
return index < array.length;
}
@Override
public AttributeInstance next() {
if (!hasNext()) throw new NoSuchElementException();
return map.getInstance(array[index++]);
}
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Set<?> s)) return false;
if (s.size() != size()) return false;
return containsAll(s);
}
}

View File

@@ -6,102 +6,136 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/** /**
* A thread-safe implementation of {@link LongOpenHashSet} using ConcurrentHashMap.KeySetView as backing storage. * Optimized thread-safe implementation of {@link LongSet} that uses striped locking
* This implementation provides concurrent access and high performance for concurrent operations. * and primitive long arrays to minimize boxing/unboxing overhead.
* *
* @author HaHaWTH at Leaf * @author HaHaWTH at Leaf
*/ */
@SuppressWarnings({"unused", "deprecation"}) @SuppressWarnings({"unused", "deprecation"})
public final class ConcurrentLongHashSet extends LongOpenHashSet implements LongSet { public final class ConcurrentLongHashSet extends LongOpenHashSet implements LongSet {
private final ConcurrentHashMap.KeySetView<Long, Boolean> backing; // Number of lock stripes - higher number means more concurrency but more memory
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
// Load factor - when to resize the hash table
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
// Initial capacity per stripe
private static final int DEFAULT_INITIAL_CAPACITY = 16;
// Array of segments (stripes)
private final Segment[] segments;
// Total size, cached for faster size() operation
private final AtomicInteger size;
/** /**
* Creates a new empty concurrent long set. * Creates a new empty concurrent long set with default parameters.
*/ */
public ConcurrentLongHashSet() { public ConcurrentLongHashSet() {
this.backing = ConcurrentHashMap.newKeySet(); this(DEFAULT_CONCURRENCY_LEVEL * DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
/**
* Creates a new concurrent long set with the specified parameters.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @param concurrencyLevel the concurrency level
*/
public ConcurrentLongHashSet(int initialCapacity, float loadFactor, int concurrencyLevel) {
// Need to call super() even though we don't use its state
super();
// Validate parameters
if (initialCapacity < 0) {
throw new IllegalArgumentException("Initial capacity must be positive");
}
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new IllegalArgumentException("Load factor must be positive");
}
if (concurrencyLevel <= 0) {
throw new IllegalArgumentException("Concurrency level must be positive");
}
// Calculate segment count (power of 2)
int segmentCount = 1;
while (segmentCount < concurrencyLevel) {
segmentCount <<= 1;
}
// Calculate capacity per segment
int segmentCapacity = Math.max(initialCapacity / segmentCount, DEFAULT_INITIAL_CAPACITY);
// Create segments
this.segments = new Segment[segmentCount];
for (int i = 0; i < segmentCount; i++) {
this.segments[i] = new Segment(segmentCapacity, loadFactor);
}
this.size = new AtomicInteger(0);
} }
@Override @Override
public int size() { public int size() {
return backing.size(); return size.get();
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return backing.isEmpty(); return size.get() == 0;
}
@Override
public @NotNull LongIterator iterator() {
return new WrappingLongIterator(backing.iterator());
}
@NotNull
@Override
public Object @NotNull [] toArray() {
return backing.toArray();
}
@NotNull
@Override
public <T> T @NotNull [] toArray(@NotNull T @NotNull [] array) {
Objects.requireNonNull(array, "Array cannot be null");
return backing.toArray(array);
}
@Override
public boolean containsAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
return backing.containsAll(collection);
}
@Override
public boolean addAll(@NotNull Collection<? extends Long> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
return backing.addAll(collection);
}
@Override
public boolean removeAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
return backing.removeAll(collection);
}
@Override
public boolean retainAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
return backing.retainAll(collection);
}
@Override
public void clear() {
backing.clear();
} }
@Override @Override
public boolean add(long key) { public boolean add(long key) {
return backing.add(key); Segment segment = segmentFor(key);
int delta = segment.add(key) ? 1 : 0;
if (delta > 0) {
size.addAndGet(delta);
}
return delta > 0;
} }
@Override @Override
public boolean contains(long key) { public boolean contains(long key) {
return backing.contains(key); return segmentFor(key).contains(key);
}
@Override
public boolean remove(long key) {
Segment segment = segmentFor(key);
int delta = segment.remove(key) ? -1 : 0;
if (delta < 0) {
size.addAndGet(delta);
}
return delta < 0;
}
@Override
public void clear() {
for (Segment segment : segments) {
segment.clear();
}
size.set(0);
}
@Override
public @NotNull LongIterator iterator() {
return new ConcurrentLongIterator();
} }
@Override @Override
public long[] toLongArray() { public long[] toLongArray() {
int size = backing.size(); long[] result = new long[size()];
long[] result = new long[size]; int index = 0;
int i = 0; for (Segment segment : segments) {
for (Long value : backing) { index = segment.toLongArray(result, index);
result[i++] = value;
} }
return result; return result;
} }
@@ -120,6 +154,104 @@ public final class ConcurrentLongHashSet extends LongOpenHashSet implements Long
return array; return array;
} }
@NotNull
@Override
public Object @NotNull [] toArray() {
Long[] result = new Long[size()];
int index = 0;
for (Segment segment : segments) {
index = segment.toObjectArray(result, index);
}
return result;
}
@NotNull
@Override
public <T> T @NotNull [] toArray(@NotNull T @NotNull [] array) {
Objects.requireNonNull(array, "Array cannot be null");
Long[] result = new Long[size()];
int index = 0;
for (Segment segment : segments) {
index = segment.toObjectArray(result, index);
}
if (array.length < result.length) {
return (T[]) result;
}
System.arraycopy(result, 0, array, 0, result.length);
if (array.length > result.length) {
array[result.length] = null;
}
return array;
}
@Override
public boolean containsAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
for (Object o : collection) {
if (o instanceof Long) {
if (!contains(((Long) o).longValue())) {
return false;
}
} else {
return false;
}
}
return true;
}
@Override
public boolean addAll(@NotNull Collection<? extends Long> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
boolean modified = false;
for (Long value : collection) {
modified |= add(value);
}
return modified;
}
@Override
public boolean removeAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
boolean modified = false;
for (Object o : collection) {
if (o instanceof Long) {
modified |= remove(((Long) o).longValue());
}
}
return modified;
}
@Override
public boolean retainAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
// Convert collection to a set of longs for faster lookups
LongOpenHashSet toRetain = new LongOpenHashSet();
for (Object o : collection) {
if (o instanceof Long) {
toRetain.add(((Long) o).longValue());
}
}
boolean modified = false;
for (Segment segment : segments) {
modified |= segment.retainAll(toRetain);
}
if (modified) {
// Recalculate size
int newSize = 0;
for (Segment segment : segments) {
newSize += segment.size();
}
size.set(newSize);
}
return modified;
}
@Override @Override
public boolean addAll(LongCollection c) { public boolean addAll(LongCollection c) {
Objects.requireNonNull(c, "Collection cannot be null"); Objects.requireNonNull(c, "Collection cannot be null");
@@ -157,12 +289,23 @@ public final class ConcurrentLongHashSet extends LongOpenHashSet implements Long
@Override @Override
public boolean retainAll(LongCollection c) { public boolean retainAll(LongCollection c) {
Objects.requireNonNull(c, "Collection cannot be null"); Objects.requireNonNull(c, "Collection cannot be null");
return backing.retainAll(c);
// For LongCollection we can directly use it
boolean modified = false;
for (Segment segment : segments) {
modified |= segment.retainAll(c);
} }
@Override if (modified) {
public boolean remove(long k) { // Recalculate size
return backing.remove(k); int newSize = 0;
for (Segment segment : segments) {
newSize += segment.size();
}
size.set(newSize);
}
return modified;
} }
@Override @Override
@@ -175,39 +318,382 @@ public final class ConcurrentLongHashSet extends LongOpenHashSet implements Long
@Override @Override
public int hashCode() { public int hashCode() {
return backing.hashCode(); int hash = 0;
for (Segment segment : segments) {
hash += segment.hashCode();
}
return hash;
} }
@Override @Override
public String toString() { public String toString() {
return backing.toString(); StringBuilder sb = new StringBuilder();
sb.append('[');
LongIterator it = iterator();
boolean hasNext = it.hasNext();
while (hasNext) {
sb.append(it.nextLong());
hasNext = it.hasNext();
if (hasNext) {
sb.append(", ");
}
} }
static class WrappingLongIterator implements LongIterator { sb.append(']');
private final Iterator<Long> backing; return sb.toString();
}
WrappingLongIterator(Iterator<Long> backing) { /**
this.backing = Objects.requireNonNull(backing); * Find the segment for a given key.
*/
private Segment segmentFor(long key) {
// Use high bits of hash to determine segment
// This helps spread keys more evenly across segments
return segments[(int) ((spread(key) >>> segmentShift()) & segmentMask())];
}
/**
* Spread bits to reduce clustering for keys with similar hash codes.
*/
private static long spread(long key) {
long h = key;
h ^= h >>> 32;
h ^= h >>> 16;
h ^= h >>> 8;
return h;
}
private int segmentShift() {
return Integer.numberOfLeadingZeros(segments.length);
}
private int segmentMask() {
return segments.length - 1;
}
/**
* A segment is a striped portion of the hash set with its own lock.
*/
private static class Segment {
private final ReentrantLock lock = new ReentrantLock();
private long[] keys;
private boolean[] used;
private int size;
private int threshold;
private final float loadFactor;
Segment(int initialCapacity, float loadFactor) {
int capacity = MathUtil.nextPowerOfTwo(initialCapacity);
this.keys = new long[capacity];
this.used = new boolean[capacity];
this.size = 0;
this.loadFactor = loadFactor;
this.threshold = (int) (capacity * loadFactor);
}
int size() {
lock.lock();
try {
return size;
} finally {
lock.unlock();
}
}
boolean contains(long key) {
lock.lock();
try {
int index = indexOf(key);
return used[index] && keys[index] == key;
} finally {
lock.unlock();
}
}
boolean add(long key) {
lock.lock();
try {
int index = indexOf(key);
// Key already exists
if (used[index] && keys[index] == key) {
return false;
}
// Insert key
keys[index] = key;
if (!used[index]) {
used[index] = true;
size++;
// Check if rehash is needed
if (size > threshold) {
rehash();
}
}
return true;
} finally {
lock.unlock();
}
}
boolean remove(long key) {
lock.lock();
try {
int index = indexOf(key);
// Key not found
if (!used[index] || keys[index] != key) {
return false;
}
// Mark slot as unused
used[index] = false;
size--;
// If the next slot is also used, we need to handle the removal properly
// to maintain the open addressing property
// This rehashing serves as a "cleanup" after removal
if (size > 0) {
rehashFromIndex(index);
}
return true;
} finally {
lock.unlock();
}
}
void clear() {
lock.lock();
try {
Arrays.fill(used, false);
size = 0;
} finally {
lock.unlock();
}
}
int toLongArray(long[] array, int offset) {
lock.lock();
try {
for (int i = 0; i < keys.length; i++) {
if (used[i]) {
array[offset++] = keys[i];
}
}
return offset;
} finally {
lock.unlock();
}
}
int toObjectArray(Long[] array, int offset) {
lock.lock();
try {
for (int i = 0; i < keys.length; i++) {
if (used[i]) {
array[offset++] = keys[i];
}
}
return offset;
} finally {
lock.unlock();
}
}
boolean retainAll(LongCollection toRetain) {
lock.lock();
try {
boolean modified = false;
for (int i = 0; i < keys.length; i++) {
if (used[i] && !toRetain.contains(keys[i])) {
used[i] = false;
size--;
modified = true;
}
}
// Rehash to clean up if needed
if (modified && size > 0) {
rehash();
}
return modified;
} finally {
lock.unlock();
}
}
/**
* Find the index where a key should be stored.
* Uses linear probing for collision resolution.
*/
private int indexOf(long key) {
int mask = keys.length - 1;
int index = (int) (spread(key) & mask);
while (used[index] && keys[index] != key) {
index = (index + 1) & mask;
}
return index;
}
/**
* Rehash the segment with a larger capacity.
*/
private void rehash() {
int oldCapacity = keys.length;
int newCapacity = oldCapacity * 2;
long[] oldKeys = keys;
boolean[] oldUsed = used;
keys = new long[newCapacity];
used = new boolean[newCapacity];
size = 0;
threshold = (int) (newCapacity * loadFactor);
// Re-add all keys
for (int i = 0; i < oldCapacity; i++) {
if (oldUsed[i]) {
add(oldKeys[i]);
}
}
}
/**
* Rehash from a specific index after removal to maintain proper open addressing.
*/
private void rehashFromIndex(int startIndex) {
int mask = keys.length - 1;
int currentIndex = startIndex;
int nextIndex = (currentIndex + 1) & mask;
// For each cluster of used slots following the removal point
while (used[nextIndex]) {
long key = keys[nextIndex];
int targetIndex = (int) (spread(key) & mask);
// If the key's ideal position is between the removal point and the current position,
// move it to the removal point
if ((targetIndex <= currentIndex && currentIndex < nextIndex) ||
(nextIndex < targetIndex && targetIndex <= currentIndex) ||
(currentIndex < nextIndex && nextIndex < targetIndex)) {
keys[currentIndex] = keys[nextIndex];
used[currentIndex] = true;
used[nextIndex] = false;
currentIndex = nextIndex;
}
nextIndex = (nextIndex + 1) & mask;
}
}
@Override
public int hashCode() {
lock.lock();
try {
int hash = 0;
for (int i = 0; i < keys.length; i++) {
if (used[i]) {
hash += Long.hashCode(keys[i]);
}
}
return hash;
} finally {
lock.unlock();
}
}
}
/**
* Concurrent iterator for the set.
*/
private class ConcurrentLongIterator implements LongIterator {
private int segmentIndex;
private int keyIndex;
private long lastReturned;
private boolean lastReturnedValid;
ConcurrentLongIterator() {
segmentIndex = 0;
keyIndex = 0;
lastReturnedValid = false;
advance();
} }
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return backing.hasNext(); return segmentIndex < segments.length;
} }
@Override @Override
public long nextLong() { public long nextLong() {
return backing.next(); if (!hasNext()) {
throw new java.util.NoSuchElementException();
}
lastReturned = segments[segmentIndex].keys[keyIndex];
lastReturnedValid = true;
advance();
return lastReturned;
} }
@Override @Override
public Long next() { public Long next() {
return backing.next(); return nextLong();
} }
@Override @Override
public void remove() { public void remove() {
backing.remove(); if (!lastReturnedValid) {
throw new IllegalStateException();
}
ConcurrentLongHashSet.this.remove(lastReturned);
lastReturnedValid = false;
}
private void advance() {
while (segmentIndex < segments.length) {
Segment segment = segments[segmentIndex];
// Lock the segment to get a consistent view
segment.lock.lock();
try {
while (keyIndex < segment.keys.length) {
if (segment.used[keyIndex]) {
// Found next element
return;
}
keyIndex++;
}
} finally {
segment.lock.unlock();
}
// Move to next segment
segmentIndex++;
keyIndex = 0;
}
}
}
/**
* Utility class for math operations.
*/
private static class MathUtil {
/**
* Returns the next power of two greater than or equal to the given value.
*/
static int nextPowerOfTwo(int value) {
int highestBit = Integer.highestOneBit(value);
return value > highestBit ? highestBit << 1 : value;
} }
} }
} }