mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-26 02:19:19 +00:00
715 lines
40 KiB
Diff
715 lines
40 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: wangxyper <wangxyper@163.com>
|
|
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/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index e9a114a4431cedaafef4b427a8baf5030ab60751..c45e8de3a2d46e06f2a5eaa85c789420c49a260b 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -1,7 +1,6 @@
|
|
package net.minecraft.server;
|
|
|
|
import co.earthme.hearse.Hearse;
|
|
-import co.earthme.hearse.HearseConfig;
|
|
import co.earthme.hearse.server.ServerEntityTickHook;
|
|
import co.earthme.hearse.server.ServerLevelTickHook;
|
|
import com.google.common.base.Splitter;
|
|
@@ -1614,6 +1613,8 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop
|
|
this.tickStep_recalculateRegions(worldserver);
|
|
this.tickStep_clearExplosionDensityCache(worldserver);
|
|
}
|
|
+ ServerEntityTickHook.awaitEntityTasks();
|
|
+ ServerLevelTickHook.awaitWorldTicKTasks();
|
|
this.tickStep_stopIteratingOverLevels();
|
|
this.tickStep_tickConnection();
|
|
this.tickStep_tickPlayerList();
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index 1ff00b202b759095661617242749091b532a6711..04e869e31fce1e0f0ad0690894bcdc6597e00b85 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -2,6 +2,10 @@ package net.minecraft.server.level;
|
|
|
|
import co.aikar.timings.Timing; // Paper
|
|
import com.destroystokyo.paper.util.misc.PooledLinkedHashSets;
|
|
+import co.earthme.hearse.Hearse;
|
|
+import co.earthme.hearse.HearseConfig;
|
|
+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;
|
|
@@ -15,10 +19,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;
|
|
@@ -72,10 +73,8 @@ 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.AtomicBoolean;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.function.*;
|
|
|
|
@@ -111,7 +110,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
public final StructureTemplateManager structureTemplateManager; // Paper - rewrite chunk system
|
|
private final String storageName;
|
|
private final PlayerMap playerMap;
|
|
- public final Map<Integer,ChunkMap.TrackedEntity> entityMap;
|
|
+ public final Map<Integer, ChunkMap.TrackedEntity> entityMap;
|
|
private final Long2ByteMap chunkTypeCache;
|
|
private final Long2LongMap chunkSaveCooldowns;
|
|
private final Queue<Runnable> unloadQueue;
|
|
@@ -140,6 +139,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// these maps are named after spigot's uses
|
|
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap; // this map is absent from updateMaps since it's controlled at the start of the chunkproviderserver tick
|
|
public final com.destroystokyo.paper.util.misc.PlayerDistanceTrackingAreaMap playerChunkTickRangeMap; // Gale - base thread pool - chunk-sorted cache tasks
|
|
+
|
|
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
// Paper start - use distance map to optimise tracker
|
|
public static boolean isLegacyTrackingEntity(Entity entity) {
|
|
@@ -150,6 +150,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
static final org.spigotmc.TrackingRange.TrackingRangeType[] TRACKING_RANGE_TYPES = org.spigotmc.TrackingRange.TrackingRangeType.values();
|
|
public final com.destroystokyo.paper.util.misc.PlayerAreaMap[] playerEntityTrackerTrackMaps;
|
|
final int[] entityTrackerTrackRanges;
|
|
+
|
|
public final int getEntityTrackerRange(final int ordinal) {
|
|
return this.entityTrackerTrackRanges[ordinal];
|
|
}
|
|
@@ -222,6 +223,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// Paper end - use distance map to optimise entity tracker
|
|
this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn
|
|
}
|
|
+
|
|
// Paper end
|
|
// Paper start
|
|
public final List<io.papermc.paper.chunk.SingleThreadChunkRegionManager> regionManagers = new java.util.ArrayList<>();
|
|
@@ -235,17 +237,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
@Override
|
|
public void removeFromRegion(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section,
|
|
final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region from) {
|
|
- final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData;
|
|
- final DataRegionData fromData = (DataRegionData)from.regionData;
|
|
+ final DataRegionSectionData sectionData = (DataRegionSectionData) section.sectionData;
|
|
+ final DataRegionData fromData = (DataRegionData) from.regionData;
|
|
}
|
|
|
|
@Override
|
|
public void addToRegion(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section,
|
|
final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region oldRegion,
|
|
final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region newRegion) {
|
|
- final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData;
|
|
- final DataRegionData oldRegionData = oldRegion == null ? null : (DataRegionData)oldRegion.regionData;
|
|
- final DataRegionData newRegionData = (DataRegionData)newRegion.regionData;
|
|
+ final DataRegionSectionData sectionData = (DataRegionSectionData) section.sectionData;
|
|
+ final DataRegionData oldRegionData = oldRegion == null ? null : (DataRegionData) oldRegion.regionData;
|
|
+ final DataRegionData newRegionData = (DataRegionData) newRegion.regionData;
|
|
}
|
|
}
|
|
|
|
@@ -348,57 +350,57 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// Paper end - use distance map to optimise entity tracker
|
|
// Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
this.playerChunkTickRangeMap = new com.destroystokyo.paper.util.misc.PlayerDistanceTrackingAreaMap(this.pooledLinkedPlayerHashSets, // Gale - base thread pool - chunk-sorted cache tasks
|
|
- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
- ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
- if (playerChunk != null) {
|
|
- playerChunk.playersInChunkTickRange = newState;
|
|
- }
|
|
- },
|
|
- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
- ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
- if (playerChunk != null) {
|
|
- playerChunk.playersInChunkTickRange = newState;
|
|
- }
|
|
- // Gale start - base thread pool - chunk-sorted cache tasks
|
|
- },
|
|
- (int posX, int posZ, int oldNearestDistance, int newNearestDistance, PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> state) -> {
|
|
- this.level.chunkSource.mainThreadProcessor.onChunkDistanceChange(posX, posZ, newNearestDistance);
|
|
- });
|
|
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
+ if (playerChunk != null) {
|
|
+ playerChunk.playersInChunkTickRange = newState;
|
|
+ }
|
|
+ },
|
|
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
+ if (playerChunk != null) {
|
|
+ playerChunk.playersInChunkTickRange = newState;
|
|
+ }
|
|
+ // Gale start - base thread pool - chunk-sorted cache tasks
|
|
+ },
|
|
+ (int posX, int posZ, int oldNearestDistance, int newNearestDistance, PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> state) -> {
|
|
+ this.level.chunkSource.mainThreadProcessor.onChunkDistanceChange(posX, posZ, newNearestDistance);
|
|
+ });
|
|
// Gale end - base thread pool - chunk-sorted cache tasks
|
|
this.playerMobSpawnMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
|
- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
- ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
- if (playerChunk != null) {
|
|
- playerChunk.playersInMobSpawnRange = newState;
|
|
- }
|
|
- },
|
|
- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
- ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
- if (playerChunk != null) {
|
|
- playerChunk.playersInMobSpawnRange = newState;
|
|
- }
|
|
- });
|
|
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
+ if (playerChunk != null) {
|
|
+ playerChunk.playersInMobSpawnRange = newState;
|
|
+ }
|
|
+ },
|
|
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
+ if (playerChunk != null) {
|
|
+ playerChunk.playersInMobSpawnRange = newState;
|
|
+ }
|
|
+ });
|
|
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
// Paper start - optimise checkDespawn
|
|
this.playerGeneralAreaMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
|
- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
- LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
|
- if (chunk != null) {
|
|
- chunk.updateGeneralAreaCache(newState);
|
|
- }
|
|
- },
|
|
- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
- LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
|
- if (chunk != null) {
|
|
- chunk.updateGeneralAreaCache(newState);
|
|
- }
|
|
- });
|
|
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
+ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
|
+ if (chunk != null) {
|
|
+ chunk.updateGeneralAreaCache(newState);
|
|
+ }
|
|
+ },
|
|
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
+ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
|
+ if (chunk != null) {
|
|
+ chunk.updateGeneralAreaCache(newState);
|
|
+ }
|
|
+ });
|
|
// Paper end - optimise checkDespawn
|
|
}
|
|
|
|
@@ -588,11 +590,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
protected void tick(BooleanSupplier shouldKeepTicking) {
|
|
try (Timing ignored = this.level.timings.poiUnload.startTiming()) { // Paper
|
|
- this.poiManager.tick(shouldKeepTicking);
|
|
+ this.poiManager.tick(shouldKeepTicking);
|
|
} // Paper
|
|
if (!this.level.noSave()) {
|
|
try (Timing ignored = this.level.timings.chunkUnload.startTiming()) { // Paper
|
|
- this.processUnloads(shouldKeepTicking);
|
|
+ this.processUnloads(shouldKeepTicking);
|
|
} // Paper
|
|
}
|
|
}
|
|
@@ -712,8 +714,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
public static boolean checkDupeUUID(ServerLevel level, Entity entity) {
|
|
io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode mode = level.paperConfig().entities.spawning.duplicateUuid.mode;
|
|
if (mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.WARN
|
|
- && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.DELETE
|
|
- && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.SAFE_REGEN) {
|
|
+ && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.DELETE
|
|
+ && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.SAFE_REGEN) {
|
|
return false;
|
|
}
|
|
Entity other = level.getEntity(entity.getUUID());
|
|
@@ -723,10 +725,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
|
|
if (mode == io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.SAFE_REGEN && other != null && !other.isRemoved()
|
|
- && Objects.equals(other.getEncodeId(), entity.getEncodeId())
|
|
- && entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < level.paperConfig().entities.spawning.duplicateUuid.safeRegenDeleteRange
|
|
+ && Objects.equals(other.getEncodeId(), entity.getEncodeId())
|
|
+ && entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < level.paperConfig().entities.spawning.duplicateUuid.safeRegenDeleteRange
|
|
) {
|
|
- if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
|
+ if (ServerLevel.DEBUG_ENTITIES)
|
|
+ LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
|
entity.discard();
|
|
return true;
|
|
}
|
|
@@ -734,21 +737,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
switch (mode) {
|
|
case SAFE_REGEN: {
|
|
entity.setUUID(java.util.UUID.randomUUID());
|
|
- if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
|
+ if (ServerLevel.DEBUG_ENTITIES)
|
|
+ LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
|
break;
|
|
}
|
|
case DELETE: {
|
|
- if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
|
+ if (ServerLevel.DEBUG_ENTITIES)
|
|
+ LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
|
entity.discard();
|
|
return true;
|
|
}
|
|
default:
|
|
- if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
|
+ if (ServerLevel.DEBUG_ENTITIES)
|
|
+ LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
+
|
|
// Paper end
|
|
public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareTickingChunk(ChunkHolder holder) {
|
|
throw new UnsupportedOperationException(); // Paper - rewrite chunk system
|
|
@@ -778,6 +785,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
public void setTickViewDistance(int distance) {
|
|
this.playerChunkManager.setTickDistance(distance);
|
|
}
|
|
+
|
|
// Paper end - replace player loader system
|
|
public void setViewDistance(int watchDistance) {
|
|
int j = Mth.clamp(watchDistance + 1, (int) 3, (int) 33);
|
|
@@ -853,8 +861,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// Paper start - rewrite chunk system
|
|
if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
|
|
return io.papermc.paper.chunk.system.io.RegionFileIOThread.loadData(
|
|
- this.level, chunkcoordintpair.x, chunkcoordintpair.z, io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA,
|
|
- io.papermc.paper.chunk.system.io.RegionFileIOThread.getIOBlockingPriorityForCurrentThread()
|
|
+ this.level, chunkcoordintpair.x, chunkcoordintpair.z, io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA,
|
|
+ io.papermc.paper.chunk.system.io.RegionFileIOThread.getIOBlockingPriorityForCurrentThread()
|
|
);
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
@@ -866,8 +874,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// Paper start - rewrite chunk system
|
|
if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
|
|
io.papermc.paper.chunk.system.io.RegionFileIOThread.scheduleSave(
|
|
- this.level, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound,
|
|
- io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA);
|
|
+ this.level, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound,
|
|
+ io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA);
|
|
return;
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
@@ -1146,10 +1154,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
|
|
// Paper start - ignore and warn about illegal addEntity calls instead of crashing server
|
|
if (!entity.valid || entity.level != this.level || this.entityMap.containsKey(entity.getId())) {
|
|
- LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName() + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""));
|
|
+ LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName() + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""));
|
|
return;
|
|
}
|
|
- if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Delay adding to tracker until after list packets
|
|
+ if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin)
|
|
+ return; // Delay adding to tracker until after list packets
|
|
// Paper end
|
|
if (!(entity instanceof EnderDragonPart)) {
|
|
EntityType<?> entitytypes = entity.getType();
|
|
@@ -1204,77 +1213,92 @@ 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 static WorkerThreadPoolExecutor concurrentTrackWorker = null;
|
|
+ private final AtomicInteger totalRunning = new AtomicInteger(0);
|
|
+ private static final AtomicBoolean isInited = new AtomicBoolean(false);
|
|
+ private static final AtomicBoolean enabled = new AtomicBoolean();
|
|
+
|
|
+ public static void tryInitIfNotInited() {
|
|
+ if (!isInited.get()) {
|
|
+ enabled.set(HearseConfig.getBoolean("optimizations.enable-multithreaded-tracker", true));
|
|
+ if (enabled.get()) {
|
|
+ final int threadCount = HearseConfig.getInt("workers.multithreaded-tracker-thread-count", Runtime.getRuntime().availableProcessors());
|
|
+ concurrentTrackWorker = new WorkerThreadPoolExecutor(
|
|
+ threadCount,
|
|
+ threadCount,
|
|
+ 5L,
|
|
+ TimeUnit.SECONDS,
|
|
+ new LinkedBlockingQueue<>(),
|
|
+ new DefaultWorkerFactory("tracker-concurrent"));
|
|
+ Hearse.getWorkerManager().addWorker("tracker", concurrentTrackWorker);
|
|
+ }
|
|
+ isInited.set(true);
|
|
+ }
|
|
+ }
|
|
+
|
|
// Paper start - optimised tracker
|
|
private final void processTrackQueue() {
|
|
this.level.timings.tracker1.startTiming();
|
|
- try {
|
|
+ tryInitIfNotInited();
|
|
+
|
|
+ if (!enabled.get()) {
|
|
for (TrackedEntity tracker : this.entityMap.values()) {
|
|
- // update tracker entry
|
|
tracker.updatePlayers(tracker.entity.getPlayersInTrackRange());
|
|
}
|
|
- } finally {
|
|
- this.level.timings.tracker1.stopTiming();
|
|
- }
|
|
|
|
|
|
- this.level.timings.tracker2.startTiming();
|
|
- try {
|
|
+ this.level.timings.tracker2.startTiming();
|
|
for (TrackedEntity tracker : this.entityMap.values()) {
|
|
tracker.serverEntity.sendChanges();
|
|
+ return;
|
|
+ }
|
|
+ if (this.totalRunning.get() > 0) {
|
|
+ return;
|
|
}
|
|
- } finally {
|
|
- this.level.timings.tracker2.stopTiming();
|
|
+
|
|
+ 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());
|
|
+ }, concurrentTrackWorker))
|
|
+ .toArray(CompletableFuture[]::new)).join();
|
|
+ } finally {
|
|
+ //this.level.timings.tracker1.stopTiming(); // Purpur
|
|
+ this.totalRunning.getAndDecrement();
|
|
+ }
|
|
+ });
|
|
+
|
|
+ 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();
|
|
+ }
|
|
+ });
|
|
}
|
|
}
|
|
- // Paper end - optimised tracker
|
|
|
|
+ // Paper end - optimised tracker
|
|
protected void tick() {
|
|
// Paper start - optimized tracker
|
|
- if (true) {
|
|
- this.processTrackQueue();
|
|
- return;
|
|
- }
|
|
+ this.processTrackQueue();
|
|
// Paper end - optimized tracker
|
|
- List<ServerPlayer> list = Lists.newArrayList();
|
|
- List<ServerPlayer> list1 = this.level.players();
|
|
- Iterator objectiterator = this.entityMap.values().iterator();
|
|
- level.timings.tracker1.startTiming(); // Paper
|
|
-
|
|
- 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
|
|
-
|
|
- if (!list.isEmpty()) {
|
|
- objectiterator = this.entityMap.values().iterator();
|
|
-
|
|
- level.timings.tracker2.startTiming(); // Paper
|
|
- while (objectiterator.hasNext()) {
|
|
- playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
|
|
- playerchunkmap_entitytracker.updatePlayers(list);
|
|
- }
|
|
- level.timings.tracker2.stopTiming(); // Paper
|
|
- }
|
|
|
|
}
|
|
|
|
@@ -1456,7 +1480,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
final Entity entity;
|
|
private final int range;
|
|
SectionPos lastSectionPos;
|
|
- public final Set<ServerPlayerConnection> seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl
|
|
+ public final Set<ServerPlayerConnection> seenBy = Sets.newConcurrentHashSet(); // 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
|
|
@@ -1474,12 +1498,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);
|
|
}
|
|
}
|
|
@@ -1510,14 +1533,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) {
|
|
@@ -1529,14 +1547,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) {
|
|
@@ -1544,7 +1557,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/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index c77f87cfc8043b2a1cf7514a7dc1f0f2041f0e8c..a4e371743abda5503b305decf7c682928e732cfa 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -1,5 +1,6 @@
|
|
package net.minecraft.server.level;
|
|
|
|
+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
import co.earthme.hearse.concurrent.thread.Worker;
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
import com.google.common.collect.Lists;
|
|
@@ -393,7 +394,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
}
|
|
// Paper end - async chunk io
|
|
|
|
- private final Object workerGetChunkLock = new Object();
|
|
+ private final Object schedulingMutex = new Object();
|
|
|
|
@Nullable
|
|
@Override
|
|
@@ -403,55 +404,44 @@ public class ServerChunkCache extends ChunkSource {
|
|
return this.getChunk(x, z, leastStatus, create);
|
|
}, this.mainThreadProcessor.createExecutorForChunk(x, z)).join(); // Gale - base thread pool - chunk-sorted cache tasks
|
|
} else {
|
|
- if (Thread.currentThread() instanceof Worker) {
|
|
- synchronized (this.workerGetChunkLock) {
|
|
- return this.getChunkUnsafe(x, z, leastStatus, create);
|
|
- }
|
|
+ final int x1 = x;
|
|
+ final int z1 = z; // Paper - conflict on variable change
|
|
+ LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z);
|
|
+ if (ifLoaded != null) {
|
|
+ return ifLoaded;
|
|
}
|
|
- return this.getChunkUnsafe(x, z, leastStatus, create);
|
|
- }
|
|
- }
|
|
-
|
|
- private ChunkAccess getChunkUnsafe(int x, int z, ChunkStatus leastStatus, boolean create) {
|
|
- final int x1 = x;
|
|
- final int z1 = z; // Paper - conflict on variable change
|
|
- // Paper start - optimise for loaded chunks
|
|
- LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z);
|
|
- if (ifLoaded != null) {
|
|
- return ifLoaded;
|
|
- }
|
|
- // Paper end
|
|
- long k = ChunkPos.asLong(x, z);
|
|
+ long k = ChunkPos.asLong(x, z);
|
|
|
|
- ChunkAccess ichunkaccess;
|
|
+ ChunkAccess ichunkaccess;
|
|
|
|
- // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system
|
|
+ // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system
|
|
|
|
- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper
|
|
- ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor;
|
|
+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper
|
|
+ ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor;
|
|
|
|
- Objects.requireNonNull(completablefuture);
|
|
- if (!completablefuture.isDone()) { // Paper
|
|
- // Paper start - async chunk io/loading
|
|
- io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
|
|
- // Paper end
|
|
- com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info
|
|
- this.level.timings.syncChunkLoad.startTiming(); // Paper
|
|
- chunkproviderserver_b.managedYield(completablefuture); // Gale - base thread pool
|
|
- io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
|
|
- this.level.timings.syncChunkLoad.stopTiming(); // Paper
|
|
- } // Paper
|
|
- ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
|
|
- return ichunkaccess1;
|
|
- }, (playerchunk_failure) -> {
|
|
- if (create) {
|
|
- throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + playerchunk_failure));
|
|
- } else {
|
|
- return null;
|
|
- }
|
|
- });
|
|
- this.storeInCache(k, ichunkaccess, leastStatus);
|
|
- return ichunkaccess;
|
|
+ Objects.requireNonNull(completablefuture);
|
|
+ if (!completablefuture.isDone()) { // Paper
|
|
+ // Paper start - async chunk io/loading
|
|
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
|
|
+ // Paper end
|
|
+ com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info
|
|
+ this.level.timings.syncChunkLoad.startTiming(); // Paper
|
|
+ chunkproviderserver_b.managedYield(completablefuture); // Gale - base thread pool
|
|
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
|
|
+ this.level.timings.syncChunkLoad.stopTiming(); // Paper
|
|
+ } // Paper
|
|
+ ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
|
|
+ return ichunkaccess1;
|
|
+ }, (playerchunk_failure) -> {
|
|
+ if (create) {
|
|
+ throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + playerchunk_failure));
|
|
+ } else {
|
|
+ return null;
|
|
+ }
|
|
+ });
|
|
+ this.storeInCache(k, ichunkaccess, leastStatus);
|
|
+ return ichunkaccess;
|
|
+ }
|
|
}
|
|
|
|
@Nullable
|
|
@@ -522,9 +512,9 @@ public class ServerChunkCache extends ChunkSource {
|
|
};
|
|
|
|
this.level.chunkTaskScheduler.scheduleChunkLoad(
|
|
- chunkX, chunkZ, leastStatus, true,
|
|
- isUrgent ? ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.BLOCKING : ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL,
|
|
- complete
|
|
+ chunkX, chunkZ, leastStatus, true,
|
|
+ isUrgent ? PrioritisedExecutor.Priority.BLOCKING : PrioritisedExecutor.Priority.NORMAL,
|
|
+ complete
|
|
);
|
|
|
|
return ret;
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
index 46dc2deb8eb066e51a03a010a7bd920486914bb2..570ed9837ee3fe075bb0de9510d5111f0158ac16 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;
|
|
@@ -375,7 +373,7 @@ public class ServerEntity {
|
|
}
|
|
|
|
if (this.entity instanceof LivingEntity) {
|
|
- Set<AttributeInstance> set = ((LivingEntity) this.entity).getAttributes().getDirtyAttributes();
|
|
+ List<AttributeInstance> set = new ArrayList<>(((LivingEntity) this.entity).getAttributes().getDirtyAttributes());
|
|
|
|
if (!set.isEmpty()) {
|
|
// CraftBukkit start - Send scaled max health
|
|
@@ -396,6 +394,5 @@ public class ServerEntity {
|
|
if (this.entity instanceof ServerPlayer) {
|
|
((ServerPlayer) this.entity).connection.send(packet);
|
|
}
|
|
-
|
|
}
|
|
}
|