From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: wangxyper Date: Wed, 18 Jan 2023 14:39:00 +0800 Subject: [PATCH] Hearse: Multithreaded tracker Original license: MIT Original project: https://github.com/Era4FunMC/Hearse diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java index dfb747eba6bf7088af0ff400da169de00a076365..0f18f7ce182e35ee6f3f385553e1bdb5ee4ad587 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -1,5 +1,7 @@ package net.minecraft.server.level; +import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor; +import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory; import com.google.common.collect.*; import com.google.common.collect.ImmutableList.Builder; import com.google.gson.JsonElement; @@ -13,10 +15,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.*; -import it.unimi.dsi.fastutil.objects.ObjectIterator; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; -import it.unimi.dsi.fastutil.objects.ReferenceSet; -import it.unimi.dsi.fastutil.objects.ReferenceSets; +import it.unimi.dsi.fastutil.objects.*; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; @@ -61,10 +60,7 @@ import java.io.IOException; import java.io.Writer; import java.nio.file.Path; import java.util.*; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.Executor; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.*; @@ -1194,78 +1190,64 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider entity.tracker = null; // Paper - We're no longer tracked } + private final Executor asyncTrackWorker = new WorkerThreadPoolExecutor( + 0, + 2, + 5L, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + new DefaultWorkerFactory("tracker-async")); + private final Executor concurrentTrackWorker = new WorkerThreadPoolExecutor( + Runtime.getRuntime().availableProcessors(), + Runtime.getRuntime().availableProcessors(), + 5L, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + new DefaultWorkerFactory("tracker-concurrent")); + + private final AtomicInteger totalRunning = new AtomicInteger(); + // Paper start - optimised tracker private final void processTrackQueue() { - //this.level.timings.tracker1.startTiming(); // Purpur - try { - for (TrackedEntity tracker : this.entityMap.values()) { - // update tracker entry - tracker.updatePlayers(tracker.entity.getPlayersInTrackRange()); - } - } finally { - //this.level.timings.tracker1.stopTiming(); // Purpur + if(this.totalRunning.get() > 0){ + return; } + this.totalRunning.set(2); + + this.asyncTrackWorker.execute(()->{ + //this.level.timings.tracker1.startTiming(); // Purpur + try { + CompletableFuture.allOf(this.entityMap.values() + .stream() + .map(tracker -> CompletableFuture.runAsync(()->{ + tracker.updatePlayers(tracker.entity.getPlayersInTrackRange()); + },this.concurrentTrackWorker)) + .toArray(CompletableFuture[]::new)).join(); + } finally { + //this.level.timings.tracker1.stopTiming(); // Purpur + this.totalRunning.getAndDecrement(); + } + }); - //this.level.timings.tracker2.startTiming(); // Purpur - try { - for (TrackedEntity tracker : this.entityMap.values()) { - tracker.serverEntity.sendChanges(); + this.asyncTrackWorker.execute(()->{ + //this.level.timings.tracker2.startTiming(); // Purpur + try { + for (TrackedEntity tracker : this.entityMap.values()) { + tracker.serverEntity.sendChanges(); + } + } finally { + //this.level.timings.tracker2.stopTiming(); // Purpur + this.totalRunning.getAndDecrement(); } - } finally { - //this.level.timings.tracker2.stopTiming(); // Purpur - } + }); } // Paper end - optimised tracker protected void tick() { // Paper start - optimized tracker - if (true) { - this.processTrackQueue(); - return; - } + this.processTrackQueue(); // Paper end - optimized tracker - List list = Lists.newArrayList(); - List list1 = this.level.players(); - Iterator objectiterator = this.entityMap.values().iterator(); - //level.timings.tracker1.startTiming(); // Paper // Purpur - - ChunkMap.TrackedEntity playerchunkmap_entitytracker; - - while (objectiterator.hasNext()) { - playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next(); - SectionPos sectionposition = playerchunkmap_entitytracker.lastSectionPos; - SectionPos sectionposition1 = SectionPos.of((EntityAccess) playerchunkmap_entitytracker.entity); - boolean flag = !Objects.equals(sectionposition, sectionposition1); - - if (flag) { - playerchunkmap_entitytracker.updatePlayers(list1); - Entity entity = playerchunkmap_entitytracker.entity; - - if (entity instanceof ServerPlayer) { - list.add((ServerPlayer) entity); - } - - playerchunkmap_entitytracker.lastSectionPos = sectionposition1; - } - - if (flag || this.distanceManager.inEntityTickingRange(sectionposition1.chunk().toLong())) { - playerchunkmap_entitytracker.serverEntity.sendChanges(); - } - } - //level.timings.tracker1.stopTiming(); // Paper // Purpur - - if (!list.isEmpty()) { - objectiterator = this.entityMap.values().iterator(); - - //level.timings.tracker2.startTiming(); // Paper // Purpur - while (objectiterator.hasNext()) { - playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next(); - playerchunkmap_entitytracker.updatePlayers(list); - } - //level.timings.tracker2.stopTiming(); // Paper // Purpur - } - } public void broadcast(Entity entity, Packet packet) { @@ -1446,7 +1428,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final Entity entity; private final int range; SectionPos lastSectionPos; - public final Set seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl + public final Set seenBy = ReferenceSets.synchronize(new ReferenceOpenHashSet<>()); // Paper - optimise map impl //Hearse - multithread tracker public TrackedEntity(Entity entity, int i, int j, boolean flag) { this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit @@ -1464,12 +1446,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (newTrackerCandidates != null) { Object[] rawData = newTrackerCandidates.getBackingSet(); - for (int i = 0, len = rawData.length; i < len; ++i) { - Object raw = rawData[i]; + for (Object raw : rawData) { if (!(raw instanceof ServerPlayer)) { continue; } - ServerPlayer player = (ServerPlayer)raw; + ServerPlayer player = (ServerPlayer) raw; this.updatePlayer(player); } } @@ -1500,14 +1481,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcast(Packet packet) { - Iterator iterator = this.seenBy.iterator(); - - while (iterator.hasNext()) { - ServerPlayerConnection serverplayerconnection = (ServerPlayerConnection) iterator.next(); - + for (ServerPlayerConnection serverplayerconnection : this.seenBy) { serverplayerconnection.send(packet); } - } public void broadcastAndSend(Packet packet) { @@ -1519,14 +1495,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcastRemoved() { - Iterator iterator = this.seenBy.iterator(); - - while (iterator.hasNext()) { - ServerPlayerConnection serverplayerconnection = (ServerPlayerConnection) iterator.next(); - + for (ServerPlayerConnection serverplayerconnection : this.seenBy) { this.serverEntity.removePairing(serverplayerconnection.getPlayer()); } - } public void removePlayer(ServerPlayer player) { @@ -1534,7 +1505,6 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (this.seenBy.remove(player.connection)) { this.serverEntity.removePairing(player); } - } public void updatePlayer(ServerPlayer player) { diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java index 50cf4d200bc2892f2140c9929193b4b20ad2bd17..afccc8e6d6a6f8d324a58570c7c2245544516e7b 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java @@ -3,14 +3,12 @@ package net.minecraft.server.level; import com.google.common.collect.Lists; import com.mojang.datafixers.util.Pair; import com.mojang.logging.LogUtils; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.Set; + +import java.util.*; import java.util.function.Consumer; import javax.annotation.Nullable; + +import it.unimi.dsi.fastutil.objects.ObjectArraySet; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket; @@ -373,7 +371,7 @@ public class ServerEntity { } if (this.entity instanceof LivingEntity) { - Set set = ((LivingEntity) this.entity).getAttributes().getDirtyAttributes(); + List set = new ArrayList<>(((LivingEntity) this.entity).getAttributes().getDirtyAttributes()); if (!set.isEmpty()) { // CraftBukkit start - Send scaled max health @@ -394,6 +392,5 @@ public class ServerEntity { if (this.entity instanceof ServerPlayer) { ((ServerPlayer) this.entity).connection.send(packet); } - } }