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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user