From 289775716360dd982fb5433691953018379b77bd Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 3 Jun 2025 15:16:15 +0900 Subject: [PATCH 01/57] optimise getNearestPlayer --- .../0123-Slightly-optimise-getNearestPlayer.patch | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0123-Slightly-optimise-getNearestPlayer.patch b/leaf-server/minecraft-patches/features/0123-Slightly-optimise-getNearestPlayer.patch index ac9e99a1..4733c36c 100644 --- a/leaf-server/minecraft-patches/features/0123-Slightly-optimise-getNearestPlayer.patch +++ b/leaf-server/minecraft-patches/features/0123-Slightly-optimise-getNearestPlayer.patch @@ -20,10 +20,10 @@ One-time operation: Convert distance to squared distance Total operations: ~3 × n (in the no-predicate case) or ~4 × n (with predicate) diff --git a/net/minecraft/world/level/EntityGetter.java b/net/minecraft/world/level/EntityGetter.java -index 670860df81a3abfc1b8b53be505fce0ee32ee2c4..083a2b5da246113913bcd5d0b2b9be42cf0554d9 100644 +index 670860df81a3abfc1b8b53be505fce0ee32ee2c4..308e00947da3fe4888fb1b1b9fa11ac5da46663b 100644 --- a/net/minecraft/world/level/EntityGetter.java +++ b/net/minecraft/world/level/EntityGetter.java -@@ -201,23 +201,42 @@ public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_syst +@@ -201,23 +201,43 @@ public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_syst } // Paper end - Affects Spawning API @@ -41,9 +41,10 @@ index 670860df81a3abfc1b8b53be505fce0ee32ee2c4..083a2b5da246113913bcd5d0b2b9be42 - for (Player player1 : this.players()) { - if (predicate == null || predicate.test(player1)) { ++ List players = this.players(); + if (predicate == null) { -+ for (int i = 0; i < this.players().size(); i++) { -+ Player player1 = this.players().get(i); ++ for (int i = 0, playersSize = players.size(); i < playersSize; i++) { ++ Player player1 = players.get(i); double d1 = player1.distanceToSqr(x, y, z); - if ((distance < 0.0 || d1 < distance * distance) && (d == -1.0 || d1 < d)) { - d = d1; @@ -53,8 +54,8 @@ index 670860df81a3abfc1b8b53be505fce0ee32ee2c4..083a2b5da246113913bcd5d0b2b9be42 } } + } else { -+ for (int i = 0; i < this.players().size(); i++) { -+ Player player1 = this.players().get(i); ++ for (int i = 0, playersSize = players.size(); i < playersSize; i++) { ++ Player player1 = players.get(i); + if (predicate.test(player1)) { + double d1 = player1.distanceToSqr(x, y, z); + if (d1 < distance) { From 347ef03d5fe6fed728eb9c4e431aba4a62babe51 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 3 Jun 2025 15:29:00 +0900 Subject: [PATCH 02/57] remove unused KineticDamage config --- .../dreeam/leaf/config/modules/network/ProtocolSupport.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/ProtocolSupport.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/ProtocolSupport.java index 1aea4693..1e40980c 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/ProtocolSupport.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/ProtocolSupport.java @@ -29,7 +29,6 @@ public class ProtocolSupport extends ConfigModules { public static boolean doABarrelRollForceEnabled = false; public static boolean doABarrelRollForceInstalled = false; public static int doABarrelRollInstalledTimeout = 40; - public static DoABarrelRollPackets.KineticDamage doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.VANILLA; @Override public void onLoaded() { @@ -53,7 +52,6 @@ public class ProtocolSupport extends ConfigModules { doABarrelRollForceEnabled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-enabled", doABarrelRollForceEnabled); doABarrelRollForceInstalled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-installed", doABarrelRollForceInstalled); doABarrelRollInstalledTimeout = config.getInt(getBasePath() + ".do-a-barrel-roll-installed-timeout", 0); - doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.valueOf(config.getString(getBasePath() + ".do-a-barrel-roll-kinetic-damage", doABarrelRollKineticDamage.name())); if (doABarrelRollInstalledTimeout <= 0) { doABarrelRollInstalledTimeout = 40; } @@ -63,7 +61,7 @@ public class ProtocolSupport extends ConfigModules { doABarrelRollForceEnabled, doABarrelRollForceInstalled, doABarrelRollInstalledTimeout, - doABarrelRollKineticDamage + DoABarrelRollPackets.KineticDamage.VANILLA ); } else { DoABarrelRollProtocol.deinit(); From 4249e11cc365297c39a2faedbd1eb7a664017bb4 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 3 Jun 2025 16:17:08 +0900 Subject: [PATCH 03/57] optimize natural spawner --- ...83-optimise-ChunkGenerator-getMobsAt.patch | 56 ++++++++ .../features/0184-optimise-getBiome.patch | 102 ++++++++++++++ ...ptimise-NaturalSpawner-spawnForChunk.patch | 130 ++++++++++++++++++ .../0186-optimize-structure-map.patch | 46 +++++++ 4 files changed, 334 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch create mode 100644 leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch create mode 100644 leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch create mode 100644 leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch diff --git a/leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch b/leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch new file mode 100644 index 00000000..7c35a7f0 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 15:16:32 +0900 +Subject: [PATCH] optimise ChunkGenerator#getMobsAt + +inline fillStartsForStructure + +diff --git a/net/minecraft/world/level/StructureManager.java b/net/minecraft/world/level/StructureManager.java +index 8bc6a6c86cd8db53feefba7508b6031ba67e242e..0785bb41560aae7edd4a727fe2403a064c9b5d9f 100644 +--- a/net/minecraft/world/level/StructureManager.java ++++ b/net/minecraft/world/level/StructureManager.java +@@ -78,7 +78,7 @@ public class StructureManager { + + public void fillStartsForStructure(Structure structure, LongSet structureRefs, Consumer startConsumer) { + for (long l : structureRefs) { +- SectionPos sectionPos = SectionPos.of(new ChunkPos(l), this.level.getMinSectionY()); ++ SectionPos sectionPos = SectionPos.of(ChunkPos.getX(l), this.level.getMinSectionY(), ChunkPos.getZ(l)); // Leaf + StructureStart startForStructure = this.getStartForStructure( + sectionPos, structure, this.level.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_STARTS) + ); +diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java +index 176adfcaa0fc458043d4bc05ead1861864b63606..9cc9ba1f008635ab713a4547ca3cdbfafcee9ffc 100644 +--- a/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -501,18 +501,21 @@ public abstract class ChunkGenerator { + Structure structure = entry.getKey(); + StructureSpawnOverride structureSpawnOverride = structure.spawnOverrides().get(category); + if (structureSpawnOverride != null) { +- MutableBoolean mutableBoolean = new MutableBoolean(false); +- Predicate predicate = structureSpawnOverride.boundingBox() == StructureSpawnOverride.BoundingBoxType.PIECE +- ? structureStart -> structureManager.structureHasPieceAt(pos, structureStart) +- : structureStart -> structureStart.getBoundingBox().isInside(pos); +- structureManager.fillStartsForStructure(structure, entry.getValue(), structureStart -> { +- if (mutableBoolean.isFalse() && predicate.test(structureStart)) { +- mutableBoolean.setTrue(); ++ // Leaf start ++ for (long l : entry.getValue()) { ++ SectionPos sectionPos = SectionPos.of(ChunkPos.getX(l), structureManager.level.getMinSectionY(), ChunkPos.getZ(l)); // Leaf ++ StructureStart startForStructure = structureManager.getStartForStructure( ++ sectionPos, structure, structureManager.level.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_STARTS) ++ ); ++ if (startForStructure != null && startForStructure.isValid()) { ++ if (structureSpawnOverride.boundingBox() == StructureSpawnOverride.BoundingBoxType.PIECE ++ ? structureManager.structureHasPieceAt(pos, startForStructure) ++ : startForStructure.getBoundingBox().isInside(pos)) { ++ return structureSpawnOverride.spawns(); ++ } + } +- }); +- if (mutableBoolean.isTrue()) { +- return structureSpawnOverride.spawns(); + } ++ // Leaf end + } + } + diff --git a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch new file mode 100644 index 00000000..571b78a0 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 15:20:40 +0900 +Subject: [PATCH] optimise getBiome + + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index eb849c57992658005e0f514c6f7923f8ca43bebf..c706f0dacd9c322d9b09d6ee073872c4229818b0 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -899,6 +899,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + this.advanceWeatherCycle(); + } + ++ if (runsNormally && (getGameTime() & 7L) == 7L) this.getBiomeManager().recomputeCache(); // Leaf - cache getBiome + // Leaf start - SparklyPaper - parallel world ticking + if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { + this.moonrise$midTickTasks(); +diff --git a/net/minecraft/world/level/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java +index a48175a7ebb1788ace46395621ed78d910178a53..2ce1754e0cf854468791688752a7d16c76917d3b 100644 +--- a/net/minecraft/world/level/biome/BiomeManager.java ++++ b/net/minecraft/world/level/biome/BiomeManager.java +@@ -15,6 +15,10 @@ public class BiomeManager { + private final BiomeManager.NoiseBiomeSource noiseBiomeSource; + private final long biomeZoomSeed; + private static final double maxOffset = 0.4500000001D; // Leaf - Carpet-Fixes - Optimized getBiome method ++ // Leaf start - cache getBiome ++ private final Holder[] biomeCache = new Holder[65536]; ++ private final long[] biomeCachePos = new long[65536]; ++ // Leaf end - cache getBiome + + public BiomeManager(BiomeManager.NoiseBiomeSource noiseBiomeSource, long biomeZoomSeed) { + this.noiseBiomeSource = noiseBiomeSource; +@@ -29,7 +33,28 @@ public class BiomeManager { + return new BiomeManager(newSource, this.biomeZoomSeed); + } + ++ // Leaf start - cache getBiome ++ public synchronized void recomputeCache() { ++ java.util.Arrays.fill(this.biomeCache, null); ++ } ++ // Leaf end - cache getBiome ++ + public Holder getBiome(BlockPos pos) { ++ // Leaf start - cache getBiome ++ long packedPos = pos.asLong(); ++ long hash = packedPos; ++ hash = (hash ^ (hash >>> 32)) * 0xff51afd7ed558ccdL; ++ hash = (hash ^ (hash >>> 32)) * 0xc4ceb9fe1a85ec53L; ++ hash = (hash ^ (hash >>> 32)) & 65535L; ++ synchronized (this) { ++ if (biomeCachePos[(int) hash] == packedPos) { ++ Holder biome = biomeCache[(int) hash]; ++ if (biome != null) { ++ return biome; ++ } ++ } ++ } ++ // Leaf end - cache getBiome + // Leaf start - Carpet-Fixes - Optimized getBiome method + int xMinus2 = pos.getX() - 2; + int yMinus2 = pos.getY() - 2; +@@ -85,11 +110,18 @@ public class BiomeManager { + smallestDist = biomeDist; + } + } +- return this.noiseBiomeSource.getNoiseBiome( ++ // Leaf start - cache getBiome ++ Holder biome = this.noiseBiomeSource.getNoiseBiome( + (smallestX & 4) == 0 ? x : x + 1, + (smallestX & 2) == 0 ? y : y + 1, + (smallestX & 1) == 0 ? z : z + 1 + ); ++ synchronized (this) { ++ biomeCache[(int) hash] = biome; ++ biomeCachePos[(int) hash] = packedPos; ++ } ++ return biome; ++ // Leaf end - cache getBiome + // Leaf end - Carpet-Fixes - Optimized getBiome method + } + +@@ -126,9 +158,18 @@ public class BiomeManager { + return Mth.square(zNoise + fiddle2) + Mth.square(yNoise + fiddle1) + Mth.square(xNoise + fiddle); + } + ++ // Leaf start ++ private static final double[] FIDDLE_TABLE = new double[1024]; ++ static { ++ for (int i = 0; i < 1024; i++) { ++ FIDDLE_TABLE[i] = (i - 512) * (0.9 / 1024.0); ++ } ++ } + private static double getFiddle(long seed) { +- return (double)(((seed >> 24) & (1024 - 1)) - (1024/2)) * (0.9 / 1024.0); // Paper - avoid floorMod, fp division, and fp subtraction ++ return FIDDLE_TABLE[(int)(seed >>> 24) & 1023]; ++ // return (double)(((seed >> 24) & (1024 - 1)) - (1024/2)) * (0.9 / 1024.0); // Paper - avoid floorMod, fp division, and fp subtraction + } ++ // Leaf end + + public interface NoiseBiomeSource { + Holder getNoiseBiome(int x, int y, int z); diff --git a/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch b/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch new file mode 100644 index 00000000..c9119e11 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch @@ -0,0 +1,130 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 15:20:59 +0900 +Subject: [PATCH] optimise NaturalSpawner#spawnForChunk + + +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..a6831e701a0ffbc91ea947c09c39ac7d919d1870 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -156,8 +156,15 @@ public final class NaturalSpawner { + } + + public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories) { ++ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); // Leaf + for (MobCategory mobCategory : categories) { + // Paper start - Optional per player mob spawns ++ // Leaf start - reduce 3/4 while failed 16 canSpawn attempts ++ if (chunk.failedSpawnAttempts[mobCategory.ordinal()] >= 16 && (level.random.nextInt(4)) != 0) { ++ continue; ++ } ++ // Leaf end - reduce 3/4 while failed 16 canSpawn attempts ++ // Paper start - Optional per player mob attempts + final boolean canSpawn; + int maxSpawns = Integer.MAX_VALUE; + if (level.paperConfig().entities.spawning.perPlayerMobSpawns) { +@@ -197,9 +204,16 @@ public final class NaturalSpawner { + canSpawn = spawnState.canSpawnForCategoryLocal(mobCategory, chunk.getPos()); + } + if (canSpawn) { ++ // Leaf start + // Paper start - throttle failed spawn attempts +- int spawnCount = spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn, +- maxSpawns, level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null, false); ++ int spawnCount = 0; ++ final Consumer trackEntity = level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null; ++ // Paper end - Optional per player mob spawns ++ mutableRandomPosWithin(pos, level, chunk); ++ if (pos.getY() >= level.getMinY() + 1) { ++ spawnCount = spawnCategoryForPosition(mobCategory, level, chunk, pos, spawnState::canSpawn, spawnState::afterSpawn, maxSpawns, trackEntity, false);// Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts ++ } // Paper - throttle failed spawn attempts ++ // Leaf end + if (spawnCount == 0) { + chunk.failedSpawnAttempts[mobCategory.ordinal()]++; + } else { +@@ -275,24 +289,48 @@ public final class NaturalSpawner { + StructureManager structureManager = level.structureManager(); + ChunkGenerator generator = level.getChunkSource().getGenerator(); + int y = pos.getY(); ++ int posX = pos.getX(); // Leaf ++ int posZ = pos.getZ(); // Leaf + int i = 0; // Paper - throttle failed spawn attempts + BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn + if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn +- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); ++ BlockPos.MutableBlockPos mutableBlockPos = pos instanceof BlockPos.MutableBlockPos pos2 ? pos2 : new BlockPos.MutableBlockPos(); // Leaf + //int i = 0; // Paper - throttle failed spawn attempts - move up + ++ // Leaf start ++ long rand = level.random.nextLong(); ++ int bits = 0; + for (int i1 = 0; i1 < 3; i1++) { +- int x = pos.getX(); +- int z = pos.getZ(); +- int i2 = 6; ++ int x = posX; ++ int z = posZ; + MobSpawnSettings.SpawnerData spawnerData = null; + SpawnGroupData spawnGroupData = null; +- int ceil = Mth.ceil(level.random.nextFloat() * 4.0F); ++ int ceil = (int) ((rand & 0x3L) + 1L); ++ bits += 2; + int i3 = 0; + + for (int i4 = 0; i4 < ceil; i4++) { +- x += level.random.nextInt(6) - level.random.nextInt(6); +- z += level.random.nextInt(6) - level.random.nextInt(6); ++ int rand1=0,rand2=0,rand3=0,rand4=0,valuesNeeded=4; ++ while (valuesNeeded > 0) { ++ if (bits > 61) { ++ rand = level.random.nextLong(); ++ bits = 0; ++ } ++ int threeBits = (int) ((rand >>> bits) & 0x7L); ++ bits += 3; ++ if (threeBits != 7 && threeBits != 6) { ++ switch (valuesNeeded) { ++ case 1 -> rand4 = threeBits; ++ case 2 -> rand3 = threeBits; ++ case 3 -> rand2 = threeBits; ++ case 4 -> rand1 = threeBits; ++ } ++ valuesNeeded--; ++ } ++ } ++ x += rand1 - rand2; ++ z += rand3 - rand4; ++ // Leaf end + mutableBlockPos.set(x, y, z); + double d = x + 0.5; + double d1 = z + 0.5; +@@ -368,8 +406,8 @@ public final class NaturalSpawner { + + private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) { + return !(distance <= 576.0) +- && !level.getSharedSpawnPos().closerToCenterThan(new Vec3(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5), 24.0) +- && (Objects.equals(new ChunkPos(pos), chunk.getPos()) || level.isNaturalSpawningAllowed(pos)); ++ && !(level.getSharedSpawnPos().distToCenterSqr(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5) < 576.0) // Leaf ++ && (ChunkPos.asLong(pos) == chunk.getPos().longKey || level.isNaturalSpawningAllowed(pos)); // Leaf + } + + // Paper start - PreCreatureSpawnEvent +@@ -474,6 +512,17 @@ public final class NaturalSpawner { + } + } + ++ // Leaf start ++ private static void mutableRandomPosWithin(BlockPos.MutableBlockPos pos1, Level level, LevelChunk chunk) { ++ ChunkPos pos = chunk.getPos(); ++ int i = pos.getMinBlockX() + level.random.nextInt(16); ++ int i1 = pos.getMinBlockZ() + level.random.nextInt(16); ++ int i2 = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, i, i1) + 1; ++ int i3 = Mth.randomBetweenInclusive(level.random, level.getMinY(), i2); ++ pos1.set(i, i3, i1); ++ } ++ // Leaf end ++ + private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) { + ChunkPos pos = chunk.getPos(); + int i = pos.getMinBlockX() + level.random.nextInt(16); diff --git a/leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch b/leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch new file mode 100644 index 00000000..19712045 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 15:41:12 +0900 +Subject: [PATCH] optimize structure map + + +diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java +index 50a9903367f49ece2a267d10944b1515c7b93859..db6828ec94682c16b5b7ec6fc6262df256781f01 100644 +--- a/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -76,8 +76,8 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + protected BlendingData blendingData; + public final Map heightmaps = Maps.newEnumMap(Heightmap.Types.class); + // Paper - rewrite chunk system +- private final Map structureStarts = Maps.newHashMap(); +- private final Map structuresRefences = Maps.newHashMap(); ++ private final Map structureStarts = new it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap<>(); // Leaf ++ private final Map structuresRefences = new it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap<>(); // Leaf + protected final Map pendingBlockEntities = Maps.newHashMap(); + public final Map blockEntities = new Object2ObjectOpenHashMap<>(); + protected final LevelHeightAccessor levelHeightAccessor; +@@ -297,7 +297,7 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + } + + public Map getAllStarts() { +- return Collections.unmodifiableMap(this.structureStarts); ++ return it.unimi.dsi.fastutil.objects.Object2ObjectMaps.unmodifiable((it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap) this.structureStarts); // Leaf + } + + public void setAllStarts(Map structureStarts) { +@@ -313,13 +313,13 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + + @Override + public void addReferenceForStructure(Structure structure, long reference) { +- this.structuresRefences.computeIfAbsent(structure, key -> new LongOpenHashSet()).add(reference); ++ this.structuresRefences.computeIfAbsent(structure, key -> new it.unimi.dsi.fastutil.longs.LongArraySet()).add(reference); // Leaf + this.markUnsaved(); + } + + @Override + public Map getAllReferences() { +- return Collections.unmodifiableMap(this.structuresRefences); ++ return it.unimi.dsi.fastutil.objects.Object2ObjectMaps.unmodifiable((it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap) this.structuresRefences); // Leaf + } + + @Override From 73cefcf57ab76fb781c667923442b3e379001e5e Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 3 Jun 2025 18:54:47 +0900 Subject: [PATCH 04/57] optimize get biome --- .../features/0184-optimise-getBiome.patch | 122 ++++++++++++------ ...ptimise-NaturalSpawner-spawnForChunk.patch | 16 ++- .../features/0039-cache-getBiome.patch | 24 ++++ 3 files changed, 118 insertions(+), 44 deletions(-) create mode 100644 leaf-server/paper-patches/features/0039-cache-getBiome.patch diff --git a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch index 571b78a0..31b88d56 100644 --- a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch +++ b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch @@ -4,20 +4,72 @@ Date: Tue, 3 Jun 2025 15:20:40 +0900 Subject: [PATCH] optimise getBiome +diff --git a/net/minecraft/advancements/critereon/LocationPredicate.java b/net/minecraft/advancements/critereon/LocationPredicate.java +index a26a5311f87873e0d4d26fda9cb8956a32ee81e8..9405f1f211db3fa2d313429866de454ede64d95b 100644 +--- a/net/minecraft/advancements/critereon/LocationPredicate.java ++++ b/net/minecraft/advancements/critereon/LocationPredicate.java +@@ -49,7 +49,7 @@ public record LocationPredicate( + } else { + BlockPos blockPos = BlockPos.containing(x, y, z); + boolean isLoaded = level.isLoaded(blockPos); +- return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(level.getBiome(blockPos))) ++ return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(level.getBiomeCached(blockPos))) // Leaf - cache getBiome + && (!this.structures.isPresent() || isLoaded && level.structureManager().getStructureWithPieceAt(blockPos, this.structures.get()).isValid()) + && (!this.smokey.isPresent() || isLoaded && this.smokey.get() == CampfireBlock.isSmokeyPos(level, blockPos)) + && (!this.light.isPresent() || this.light.get().matches(level, blockPos)) diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index eb849c57992658005e0f514c6f7923f8ca43bebf..c706f0dacd9c322d9b09d6ee073872c4229818b0 100644 +index eb849c57992658005e0f514c6f7923f8ca43bebf..29dc898b2586868da961616524eaad824f0a24b3 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -899,6 +899,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.advanceWeatherCycle(); } -+ if (runsNormally && (getGameTime() & 7L) == 7L) this.getBiomeManager().recomputeCache(); // Leaf - cache getBiome ++ if (runsNormally && (random.nextInt(32) == 0)) this.getBiomeManager().clearCache(); // Leaf - cache getBiome // Leaf start - SparklyPaper - parallel world ticking if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { this.moonrise$midTickTasks(); +diff --git a/net/minecraft/world/level/LevelReader.java b/net/minecraft/world/level/LevelReader.java +index 26c8c1e5598daf3550aef05b12218c47bda6618b..91b2fc5b2da6566b05fef1111665b895cadad343 100644 +--- a/net/minecraft/world/level/LevelReader.java ++++ b/net/minecraft/world/level/LevelReader.java +@@ -53,6 +53,12 @@ public interface LevelReader extends ca.spottedleaf.moonrise.patches.chunk_syste + return this.getBiomeManager().getBiome(pos); + } + ++ // Leaf start - cache getBiome ++ default Holder getBiomeCached(BlockPos pos) { ++ return this.getBiomeManager().getBiomeCached(pos); ++ } ++ // Leaf end - cache getBiome ++ + default Stream getBlockStatesIfLoaded(AABB aabb) { + int floor = Mth.floor(aabb.minX); + int floor1 = Mth.floor(aabb.maxX); +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..c0d941af10ffe8c158dab9db40c7c5767b6cfd6e 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -445,7 +445,7 @@ public final class NaturalSpawner { + private static Optional getRandomSpawnMobAt( + ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, RandomSource random, BlockPos pos + ) { +- Holder biome = level.getBiome(pos); ++ Holder biome = level.getBiomeCached(pos); // Leaf - cache getBiome + return category == MobCategory.WATER_AMBIENT && biome.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F + ? Optional.empty() + : mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random); +@@ -462,7 +462,7 @@ public final class NaturalSpawner { + ) { + return isInNetherFortressBounds(pos, level, category, structureManager) + ? NetherFortressStructure.FORTRESS_ENEMIES +- : generator.getMobsAt(biome != null ? biome : level.getBiome(pos), structureManager, category, pos); ++ : generator.getMobsAt(biome != null ? biome : level.getBiomeCached(pos), structureManager, category, pos); // Leaf - cache getBiome + } + + public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) { diff --git a/net/minecraft/world/level/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java -index a48175a7ebb1788ace46395621ed78d910178a53..2ce1754e0cf854468791688752a7d16c76917d3b 100644 +index a48175a7ebb1788ace46395621ed78d910178a53..c9e3011ff873ace4d25727a5d82e249b8f028de0 100644 --- a/net/minecraft/world/level/biome/BiomeManager.java +++ b/net/minecraft/world/level/biome/BiomeManager.java @@ -15,6 +15,10 @@ public class BiomeManager { @@ -31,56 +83,48 @@ index a48175a7ebb1788ace46395621ed78d910178a53..2ce1754e0cf854468791688752a7d16c public BiomeManager(BiomeManager.NoiseBiomeSource noiseBiomeSource, long biomeZoomSeed) { this.noiseBiomeSource = noiseBiomeSource; -@@ -29,7 +33,28 @@ public class BiomeManager { +@@ -29,6 +33,40 @@ public class BiomeManager { return new BiomeManager(newSource, this.biomeZoomSeed); } + // Leaf start - cache getBiome -+ public synchronized void recomputeCache() { ++ public void clearCache() { + java.util.Arrays.fill(this.biomeCache, null); + } -+ // Leaf end - cache getBiome -+ - public Holder getBiome(BlockPos pos) { -+ // Leaf start - cache getBiome -+ long packedPos = pos.asLong(); ++ public Holder getBiomeCached(BlockPos pos) { ++ int xMinus2 = pos.getX() - 2; ++ int yMinus2 = pos.getY() - 2; ++ int zMinus2 = pos.getZ() - 2; ++ int x = xMinus2 >> 2; ++ int y = yMinus2 >> 2; ++ int z = zMinus2 >> 2; ++ long packedPos = BlockPos.asLong(x, y, z); + long hash = packedPos; + hash = (hash ^ (hash >>> 32)) * 0xff51afd7ed558ccdL; + hash = (hash ^ (hash >>> 32)) * 0xc4ceb9fe1a85ec53L; + hash = (hash ^ (hash >>> 32)) & 65535L; -+ synchronized (this) { -+ if (biomeCachePos[(int) hash] == packedPos) { -+ Holder biome = biomeCache[(int) hash]; -+ if (biome != null) { -+ return biome; -+ } ++ ++ long pos1 = biomeCachePos[(int) hash]; ++ if (pos1 == packedPos) { ++ Holder biome = biomeCache[(int) hash]; ++ if (biome != null) { ++ return biome; + } + } -+ // Leaf end - cache getBiome ++ ++ Holder biome = getBiome(pos); ++ ++ biomeCache[(int) hash] = biome; ++ biomeCachePos[(int) hash] = packedPos; ++ ++ return biome; ++ } ++ // Leaf end - cache getBiome ++ + public Holder getBiome(BlockPos pos) { // Leaf start - Carpet-Fixes - Optimized getBiome method int xMinus2 = pos.getX() - 2; - int yMinus2 = pos.getY() - 2; -@@ -85,11 +110,18 @@ public class BiomeManager { - smallestDist = biomeDist; - } - } -- return this.noiseBiomeSource.getNoiseBiome( -+ // Leaf start - cache getBiome -+ Holder biome = this.noiseBiomeSource.getNoiseBiome( - (smallestX & 4) == 0 ? x : x + 1, - (smallestX & 2) == 0 ? y : y + 1, - (smallestX & 1) == 0 ? z : z + 1 - ); -+ synchronized (this) { -+ biomeCache[(int) hash] = biome; -+ biomeCachePos[(int) hash] = packedPos; -+ } -+ return biome; -+ // Leaf end - cache getBiome - // Leaf end - Carpet-Fixes - Optimized getBiome method - } - -@@ -126,9 +158,18 @@ public class BiomeManager { +@@ -126,9 +164,18 @@ public class BiomeManager { return Mth.square(zNoise + fiddle2) + Mth.square(yNoise + fiddle1) + Mth.square(xNoise + fiddle); } diff --git a/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch b/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch index c9119e11..e2bab927 100644 --- a/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch +++ b/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch @@ -5,7 +5,7 @@ Subject: [PATCH] optimise NaturalSpawner#spawnForChunk diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..a6831e701a0ffbc91ea947c09c39ac7d919d1870 100644 +index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..bbbb1b1f37406d86d15dca107e8b4f3e614a280d 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java @@ -156,8 +156,15 @@ public final class NaturalSpawner { @@ -43,14 +43,20 @@ index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..a6831e701a0ffbc91ea947c09c39ac7d if (spawnCount == 0) { chunk.failedSpawnAttempts[mobCategory.ordinal()]++; } else { -@@ -275,24 +289,48 @@ public final class NaturalSpawner { +@@ -275,24 +289,53 @@ public final class NaturalSpawner { StructureManager structureManager = level.structureManager(); ChunkGenerator generator = level.getChunkSource().getGenerator(); int y = pos.getY(); + int posX = pos.getX(); // Leaf + int posZ = pos.getZ(); // Leaf int i = 0; // Paper - throttle failed spawn attempts - BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn +- BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn ++ // Leaf start ++ if (!level.getWorldBorder().isWithinBounds(pos) || level.isOutsideBuildHeight(pos)) { ++ return i; ++ } ++ BlockState blockState = chunk.getPos().longKey == ChunkPos.asLong(pos) ? chunk.getBlockState(posX, y, posZ) : level.getBlockStateIfLoaded(pos); ++ // Leaf end if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn - BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + BlockPos.MutableBlockPos mutableBlockPos = pos instanceof BlockPos.MutableBlockPos pos2 ? pos2 : new BlockPos.MutableBlockPos(); // Leaf @@ -99,7 +105,7 @@ index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..a6831e701a0ffbc91ea947c09c39ac7d mutableBlockPos.set(x, y, z); double d = x + 0.5; double d1 = z + 0.5; -@@ -368,8 +406,8 @@ public final class NaturalSpawner { +@@ -368,8 +411,8 @@ public final class NaturalSpawner { private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) { return !(distance <= 576.0) @@ -110,7 +116,7 @@ index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..a6831e701a0ffbc91ea947c09c39ac7d } // Paper start - PreCreatureSpawnEvent -@@ -474,6 +512,17 @@ public final class NaturalSpawner { +@@ -474,6 +517,17 @@ public final class NaturalSpawner { } } diff --git a/leaf-server/paper-patches/features/0039-cache-getBiome.patch b/leaf-server/paper-patches/features/0039-cache-getBiome.patch new file mode 100644 index 00000000..21295438 --- /dev/null +++ b/leaf-server/paper-patches/features/0039-cache-getBiome.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 18:51:44 +0900 +Subject: [PATCH] cache getBiome + + +diff --git a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java +index 09e87552159e24603aa9a4f658ab4449d7eaeb0a..cfe6d74fa06e19c33e6342c76ca99e473840a65b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java +@@ -302,6 +302,13 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel { + return this.handle.getBiome(pos); + } + ++ // Leaf start - cache getBiome ++ @Override ++ public Holder getBiomeCached(BlockPos pos) { ++ return this.handle.getBiomeCached(pos); ++ } ++ // Leaf end - cache getBiome ++ + @Override + public Stream getBlockStatesIfLoaded(AABB box) { + return this.handle.getBlockStatesIfLoaded(box); From 196822ba6254c4c97c951d35b17815773d03c2be Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 3 Jun 2025 19:31:05 +0900 Subject: [PATCH 05/57] reduce chunk loaded check --- .../0185-optimise-NaturalSpawner-spawnForChunk.patch | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch b/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch index e2bab927..a97d2de1 100644 --- a/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch +++ b/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch @@ -5,7 +5,7 @@ Subject: [PATCH] optimise NaturalSpawner#spawnForChunk diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..bbbb1b1f37406d86d15dca107e8b4f3e614a280d 100644 +index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..878eb750f2d2bddae81d9fbfb772d13077b37d51 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java @@ -156,8 +156,15 @@ public final class NaturalSpawner { @@ -43,7 +43,7 @@ index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..bbbb1b1f37406d86d15dca107e8b4f3e if (spawnCount == 0) { chunk.failedSpawnAttempts[mobCategory.ordinal()]++; } else { -@@ -275,24 +289,53 @@ public final class NaturalSpawner { +@@ -275,31 +289,60 @@ public final class NaturalSpawner { StructureManager structureManager = level.structureManager(); ChunkGenerator generator = level.getChunkSource().getGenerator(); int y = pos.getY(); @@ -105,6 +105,14 @@ index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..bbbb1b1f37406d86d15dca107e8b4f3e mutableBlockPos.set(x, y, z); double d = x + 0.5; double d1 = z + 0.5; + Player nearestPlayer = level.getNearestPlayer(d, y, d1, -1.0, level.purpurConfig.mobSpawningIgnoreCreativePlayers); // Purpur - mob spawning option to ignore creative players + if (nearestPlayer != null) { + double d2 = nearestPlayer.distanceToSqr(d, y, d1); +- if (level.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn ++ if (level.getWorldBorder().isWithinBounds(mutableBlockPos) && (chunk.getPos().longKey == ChunkPos.asLong(mutableBlockPos) || level.getChunkIfLoadedImmediately(mutableBlockPos.getX() >> 4, mutableBlockPos.getZ() >> 4) != null) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn // Leaf + if (spawnerData == null) { + Optional randomSpawnMobAt = getRandomSpawnMobAt( + level, structureManager, generator, category, level.random, mutableBlockPos @@ -368,8 +411,8 @@ public final class NaturalSpawner { private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) { From 27fb282b43aa30d6c95a5080f52bbd87adde768c Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 3 Jun 2025 20:09:10 +0900 Subject: [PATCH 06/57] remove clear biome cache --- ...83-optimise-ChunkGenerator-getMobsAt.patch | 20 +++++++++++++----- .../features/0184-optimise-getBiome.patch | 21 +++---------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch b/leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch index 7c35a7f0..a7facb01 100644 --- a/leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch +++ b/leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch @@ -6,7 +6,7 @@ Subject: [PATCH] optimise ChunkGenerator#getMobsAt inline fillStartsForStructure diff --git a/net/minecraft/world/level/StructureManager.java b/net/minecraft/world/level/StructureManager.java -index 8bc6a6c86cd8db53feefba7508b6031ba67e242e..0785bb41560aae7edd4a727fe2403a064c9b5d9f 100644 +index 8bc6a6c86cd8db53feefba7508b6031ba67e242e..20a2bc31a8082afd4f758bd7e91691bbc58ba16e 100644 --- a/net/minecraft/world/level/StructureManager.java +++ b/net/minecraft/world/level/StructureManager.java @@ -78,7 +78,7 @@ public class StructureManager { @@ -18,11 +18,22 @@ index 8bc6a6c86cd8db53feefba7508b6031ba67e242e..0785bb41560aae7edd4a727fe2403a06 StructureStart startForStructure = this.getStartForStructure( sectionPos, structure, this.level.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_STARTS) ); +@@ -173,8 +173,8 @@ public class StructureManager { + } + + public Map getAllStructuresAt(BlockPos pos) { +- SectionPos sectionPos = SectionPos.of(pos); +- return this.level.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); ++ // SectionPos sectionPos = SectionPos.of(pos); // Leaf ++ return this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); // Leaf + } + + public StructureCheckResult checkStructurePresence(ChunkPos chunkPos, Structure structure, StructurePlacement placement, boolean skipKnownStructures) { diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java -index 176adfcaa0fc458043d4bc05ead1861864b63606..9cc9ba1f008635ab713a4547ca3cdbfafcee9ffc 100644 +index 176adfcaa0fc458043d4bc05ead1861864b63606..87df3cd0981e94c28b816eb231942499c266a8da 100644 --- a/net/minecraft/world/level/chunk/ChunkGenerator.java +++ b/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -501,18 +501,21 @@ public abstract class ChunkGenerator { +@@ -501,18 +501,20 @@ public abstract class ChunkGenerator { Structure structure = entry.getKey(); StructureSpawnOverride structureSpawnOverride = structure.spawnOverrides().get(category); if (structureSpawnOverride != null) { @@ -35,9 +46,8 @@ index 176adfcaa0fc458043d4bc05ead1861864b63606..9cc9ba1f008635ab713a4547ca3cdbfa - mutableBoolean.setTrue(); + // Leaf start + for (long l : entry.getValue()) { -+ SectionPos sectionPos = SectionPos.of(ChunkPos.getX(l), structureManager.level.getMinSectionY(), ChunkPos.getZ(l)); // Leaf + StructureStart startForStructure = structureManager.getStartForStructure( -+ sectionPos, structure, structureManager.level.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_STARTS) ++ null, structure, structureManager.level.getChunk(ChunkPos.getX(l), ChunkPos.getZ(l), ChunkStatus.STRUCTURE_STARTS) + ); + if (startForStructure != null && startForStructure.isValid()) { + if (structureSpawnOverride.boundingBox() == StructureSpawnOverride.BoundingBoxType.PIECE diff --git a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch index 31b88d56..fca33074 100644 --- a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch +++ b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch @@ -17,18 +17,6 @@ index a26a5311f87873e0d4d26fda9cb8956a32ee81e8..9405f1f211db3fa2d313429866de454e && (!this.structures.isPresent() || isLoaded && level.structureManager().getStructureWithPieceAt(blockPos, this.structures.get()).isValid()) && (!this.smokey.isPresent() || isLoaded && this.smokey.get() == CampfireBlock.isSmokeyPos(level, blockPos)) && (!this.light.isPresent() || this.light.get().matches(level, blockPos)) -diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index eb849c57992658005e0f514c6f7923f8ca43bebf..29dc898b2586868da961616524eaad824f0a24b3 100644 ---- a/net/minecraft/server/level/ServerLevel.java -+++ b/net/minecraft/server/level/ServerLevel.java -@@ -899,6 +899,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - this.advanceWeatherCycle(); - } - -+ if (runsNormally && (random.nextInt(32) == 0)) this.getBiomeManager().clearCache(); // Leaf - cache getBiome - // Leaf start - SparklyPaper - parallel world ticking - if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { - this.moonrise$midTickTasks(); diff --git a/net/minecraft/world/level/LevelReader.java b/net/minecraft/world/level/LevelReader.java index 26c8c1e5598daf3550aef05b12218c47bda6618b..91b2fc5b2da6566b05fef1111665b895cadad343 100644 --- a/net/minecraft/world/level/LevelReader.java @@ -69,7 +57,7 @@ index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..c0d941af10ffe8c158dab9db40c7c576 public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) { diff --git a/net/minecraft/world/level/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java -index a48175a7ebb1788ace46395621ed78d910178a53..c9e3011ff873ace4d25727a5d82e249b8f028de0 100644 +index a48175a7ebb1788ace46395621ed78d910178a53..4c0c74b73651cdbc98f0c8f101832e4c23fd0f8d 100644 --- a/net/minecraft/world/level/biome/BiomeManager.java +++ b/net/minecraft/world/level/biome/BiomeManager.java @@ -15,6 +15,10 @@ public class BiomeManager { @@ -83,14 +71,11 @@ index a48175a7ebb1788ace46395621ed78d910178a53..c9e3011ff873ace4d25727a5d82e249b public BiomeManager(BiomeManager.NoiseBiomeSource noiseBiomeSource, long biomeZoomSeed) { this.noiseBiomeSource = noiseBiomeSource; -@@ -29,6 +33,40 @@ public class BiomeManager { +@@ -29,6 +33,37 @@ public class BiomeManager { return new BiomeManager(newSource, this.biomeZoomSeed); } + // Leaf start - cache getBiome -+ public void clearCache() { -+ java.util.Arrays.fill(this.biomeCache, null); -+ } + public Holder getBiomeCached(BlockPos pos) { + int xMinus2 = pos.getX() - 2; + int yMinus2 = pos.getY() - 2; @@ -124,7 +109,7 @@ index a48175a7ebb1788ace46395621ed78d910178a53..c9e3011ff873ace4d25727a5d82e249b public Holder getBiome(BlockPos pos) { // Leaf start - Carpet-Fixes - Optimized getBiome method int xMinus2 = pos.getX() - 2; -@@ -126,9 +164,18 @@ public class BiomeManager { +@@ -126,9 +161,18 @@ public class BiomeManager { return Mth.square(zNoise + fiddle2) + Mth.square(yNoise + fiddle1) + Mth.square(xNoise + fiddle); } From 8e48e51e7654dec9a840b85b1b988fcf9722bb55 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 3 Jun 2025 20:31:17 +0900 Subject: [PATCH 07/57] add config --- .../features/0184-optimise-getBiome.patch | 28 +++++++-- ...ptimise-NaturalSpawner-spawnForChunk.patch | 58 +++++++------------ .../0186-optimize-structure-map.patch | 6 +- .../features/0187-throttle-mob-spawning.patch | 29 ++++++++++ .../config/modules/opt/OptimizeBiome.java | 17 ++++++ .../modules/opt/ThrottleNaturalSpawnMob.java | 28 +++++++++ 6 files changed, 119 insertions(+), 47 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java diff --git a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch index fca33074..4eaf4697 100644 --- a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch +++ b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch @@ -57,26 +57,42 @@ index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..c0d941af10ffe8c158dab9db40c7c576 public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) { diff --git a/net/minecraft/world/level/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java -index a48175a7ebb1788ace46395621ed78d910178a53..4c0c74b73651cdbc98f0c8f101832e4c23fd0f8d 100644 +index a48175a7ebb1788ace46395621ed78d910178a53..cb6b99dd4504681bdf2f7dbd7e3d2218304f891a 100644 --- a/net/minecraft/world/level/biome/BiomeManager.java +++ b/net/minecraft/world/level/biome/BiomeManager.java -@@ -15,6 +15,10 @@ public class BiomeManager { +@@ -15,10 +15,23 @@ public class BiomeManager { private final BiomeManager.NoiseBiomeSource noiseBiomeSource; private final long biomeZoomSeed; private static final double maxOffset = 0.4500000001D; // Leaf - Carpet-Fixes - Optimized getBiome method + // Leaf start - cache getBiome -+ private final Holder[] biomeCache = new Holder[65536]; -+ private final long[] biomeCachePos = new long[65536]; ++ private final Holder[] biomeCache; ++ private final long[] biomeCachePos; + // Leaf end - cache getBiome public BiomeManager(BiomeManager.NoiseBiomeSource noiseBiomeSource, long biomeZoomSeed) { this.noiseBiomeSource = noiseBiomeSource; -@@ -29,6 +33,37 @@ public class BiomeManager { + this.biomeZoomSeed = biomeZoomSeed; ++ // Leaf start - cache getBiome ++ if (org.dreeam.leaf.config.modules.opt.OptimizeBiome.enabled) { ++ biomeCache = new Holder[65536]; ++ biomeCachePos = new long[65536]; ++ } else { ++ biomeCache = null; ++ biomeCachePos = null; ++ } ++ // Leaf end - cache getBiome + } + + public static long obfuscateSeed(long seed) { +@@ -29,6 +42,40 @@ public class BiomeManager { return new BiomeManager(newSource, this.biomeZoomSeed); } + // Leaf start - cache getBiome + public Holder getBiomeCached(BlockPos pos) { ++ if (biomeCache == null) { ++ return getBiome(pos); ++ } + int xMinus2 = pos.getX() - 2; + int yMinus2 = pos.getY() - 2; + int zMinus2 = pos.getZ() - 2; @@ -109,7 +125,7 @@ index a48175a7ebb1788ace46395621ed78d910178a53..4c0c74b73651cdbc98f0c8f101832e4c public Holder getBiome(BlockPos pos) { // Leaf start - Carpet-Fixes - Optimized getBiome method int xMinus2 = pos.getX() - 2; -@@ -126,9 +161,18 @@ public class BiomeManager { +@@ -126,9 +173,18 @@ public class BiomeManager { return Mth.square(zNoise + fiddle2) + Mth.square(yNoise + fiddle1) + Mth.square(xNoise + fiddle); } diff --git a/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch b/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch index a97d2de1..6601e40d 100644 --- a/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch +++ b/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch @@ -5,45 +5,27 @@ Subject: [PATCH] optimise NaturalSpawner#spawnForChunk diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..878eb750f2d2bddae81d9fbfb772d13077b37d51 100644 +index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..450f17badaa3f6c8f1cdb9e6dc76828b70afe6fc 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java -@@ -156,8 +156,15 @@ public final class NaturalSpawner { - } +@@ -238,10 +238,13 @@ public final class NaturalSpawner { + // Paper end - throttle failed spawn attempts + ) { + // Paper end - Optional per player mob spawns +- BlockPos randomPosWithin = getRandomPosWithin(level, chunk); +- if (randomPosWithin.getY() >= level.getMinY() + 1) { +- return spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts ++ // Leaf start ++ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); ++ mutableRandomPosWithin(pos, level, chunk); ++ if (pos.getY() >= level.getMinY() + 1) { ++ return spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts + } ++ // Leaf end - public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories) { -+ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); // Leaf - for (MobCategory mobCategory : categories) { - // Paper start - Optional per player mob spawns -+ // Leaf start - reduce 3/4 while failed 16 canSpawn attempts -+ if (chunk.failedSpawnAttempts[mobCategory.ordinal()] >= 16 && (level.random.nextInt(4)) != 0) { -+ continue; -+ } -+ // Leaf end - reduce 3/4 while failed 16 canSpawn attempts -+ // Paper start - Optional per player mob attempts - final boolean canSpawn; - int maxSpawns = Integer.MAX_VALUE; - if (level.paperConfig().entities.spawning.perPlayerMobSpawns) { -@@ -197,9 +204,16 @@ public final class NaturalSpawner { - canSpawn = spawnState.canSpawnForCategoryLocal(mobCategory, chunk.getPos()); - } - if (canSpawn) { -+ // Leaf start - // Paper start - throttle failed spawn attempts -- int spawnCount = spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn, -- maxSpawns, level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null, false); -+ int spawnCount = 0; -+ final Consumer trackEntity = level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null; -+ // Paper end - Optional per player mob spawns -+ mutableRandomPosWithin(pos, level, chunk); -+ if (pos.getY() >= level.getMinY() + 1) { -+ spawnCount = spawnCategoryForPosition(mobCategory, level, chunk, pos, spawnState::canSpawn, spawnState::afterSpawn, maxSpawns, trackEntity, false);// Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts -+ } // Paper - throttle failed spawn attempts -+ // Leaf end - if (spawnCount == 0) { - chunk.failedSpawnAttempts[mobCategory.ordinal()]++; - } else { -@@ -275,31 +289,60 @@ public final class NaturalSpawner { + return 0; // Paper - throttle failed spawn attempts + } +@@ -275,31 +278,60 @@ public final class NaturalSpawner { StructureManager structureManager = level.structureManager(); ChunkGenerator generator = level.getChunkSource().getGenerator(); int y = pos.getY(); @@ -113,7 +95,7 @@ index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..878eb750f2d2bddae81d9fbfb772d130 if (spawnerData == null) { Optional randomSpawnMobAt = getRandomSpawnMobAt( level, structureManager, generator, category, level.random, mutableBlockPos -@@ -368,8 +411,8 @@ public final class NaturalSpawner { +@@ -368,8 +400,8 @@ public final class NaturalSpawner { private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) { return !(distance <= 576.0) @@ -124,7 +106,7 @@ index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..878eb750f2d2bddae81d9fbfb772d130 } // Paper start - PreCreatureSpawnEvent -@@ -474,6 +517,17 @@ public final class NaturalSpawner { +@@ -474,6 +506,17 @@ public final class NaturalSpawner { } } diff --git a/leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch b/leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch index 19712045..34592250 100644 --- a/leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch +++ b/leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch @@ -5,7 +5,7 @@ Subject: [PATCH] optimize structure map diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java -index 50a9903367f49ece2a267d10944b1515c7b93859..db6828ec94682c16b5b7ec6fc6262df256781f01 100644 +index 50a9903367f49ece2a267d10944b1515c7b93859..ceabb277bef2633de8f55e16431dbb4d0869817b 100644 --- a/net/minecraft/world/level/chunk/ChunkAccess.java +++ b/net/minecraft/world/level/chunk/ChunkAccess.java @@ -76,8 +76,8 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh @@ -24,7 +24,7 @@ index 50a9903367f49ece2a267d10944b1515c7b93859..db6828ec94682c16b5b7ec6fc6262df2 public Map getAllStarts() { - return Collections.unmodifiableMap(this.structureStarts); -+ return it.unimi.dsi.fastutil.objects.Object2ObjectMaps.unmodifiable((it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap) this.structureStarts); // Leaf ++ return this.structureStarts; // Leaf } public void setAllStarts(Map structureStarts) { @@ -40,7 +40,7 @@ index 50a9903367f49ece2a267d10944b1515c7b93859..db6828ec94682c16b5b7ec6fc6262df2 @Override public Map getAllReferences() { - return Collections.unmodifiableMap(this.structuresRefences); -+ return it.unimi.dsi.fastutil.objects.Object2ObjectMaps.unmodifiable((it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap) this.structuresRefences); // Leaf ++ return this.structuresRefences; // Leaf } @Override diff --git a/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch b/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch new file mode 100644 index 00000000..c2e3ab5a --- /dev/null +++ b/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 21:34:25 +0900 +Subject: [PATCH] throttle mob spawning + + +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index 450f17badaa3f6c8f1cdb9e6dc76828b70afe6fc..8db4fd335e661111c52721be2f5ffc65a2c843d2 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -160,6 +160,18 @@ public final class NaturalSpawner { + // Paper start - Optional per player mob spawns + final boolean canSpawn; + int maxSpawns = Integer.MAX_VALUE; ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.opt.ThrottleNaturalSpawnMob.enabled) { ++ int spawnChance = org.dreeam.leaf.config.modules.opt.ThrottleNaturalSpawnMob.spawnChance[mobCategory.ordinal()]; ++ long failedAttempt = org.dreeam.leaf.config.modules.opt.ThrottleNaturalSpawnMob.failedAttempts[mobCategory.ordinal()]; ++ if (failedAttempt >= 0L ++ && spawnChance >= 0 ++ && chunk.failedSpawnAttempts[mobCategory.ordinal()] >= failedAttempt ++ && (level.random.nextInt() & 1023) > spawnChance) { ++ continue; ++ } ++ } ++ // Leaf end + if (level.paperConfig().entities.spawning.perPlayerMobSpawns) { + // Copied from getFilteredSpawningCategories + int limit = mobCategory.getMaxInstancesPerChunk(); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java new file mode 100644 index 00000000..3287f98c --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java @@ -0,0 +1,17 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class OptimizeBiome extends ConfigModules { + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".cache-mob-spawning-biome"; + } + + public static boolean enabled = false; + + @Override + public void onLoaded() { + enabled = config().getBoolean(getBasePath(), enabled); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java new file mode 100644 index 00000000..2be54108 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java @@ -0,0 +1,28 @@ +package org.dreeam.leaf.config.modules.opt; + +import net.minecraft.world.entity.MobCategory; +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class ThrottleNaturalSpawnMob extends ConfigModules { + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".throttled-mob-spawning"; + } + + public static boolean enabled = false; + public static long[] failedAttempts; + public static int[] spawnChance; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".enabled", enabled); + MobCategory[] categories = MobCategory.values(); + failedAttempts = new long[categories.length]; + spawnChance = new int[categories.length]; + for (int i = 0; i < categories.length; i++) { + String category = getBasePath() + "." + categories[i].getSerializedName(); + failedAttempts[i] = config.getLong(category + ".failed-attempts", -1); + spawnChance[i] = (int) Math.round(config.getDouble(category + ".spawn-chance", 100.0) * 10.24); + } + } +} From 2cd4838b7e9c80b8361105b40c713c2b8985d08d Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 4 Jun 2025 00:39:42 +0900 Subject: [PATCH 08/57] shutdown mob spawning thread on exit --- ...004-Pufferfish-Optimize-mob-spawning.patch | 17 +++++- .../pufferfish/util/AsyncExecutor.java | 61 +++++++------------ 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch b/leaf-server/minecraft-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch index ab709291..bb16fab5 100644 --- a/leaf-server/minecraft-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch +++ b/leaf-server/minecraft-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch @@ -22,7 +22,7 @@ and, in my opinion, worth the low risk of minor mob-spawning-related inconsistencies. diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 4ba85d704ffebae38f7a76a97a182e3674730c6f..a76b67a846b12a7b3d0c41b6ac4833d4f0372531 100644 +index 4ba85d704ffebae38f7a76a97a182e3674730c6f..d19e31d3b280325defcaf46e7168eafb3d6587d2 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -286,6 +286,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system +@@ -1055,6 +1056,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop jobs = Queues.newArrayDeque(); - private final Lock mutex = new ReentrantLock(); - private final Condition cond = mutex.newCondition(); + private final PriorityQueue jobs = PriorityQueues.synchronize(new ObjectArrayFIFOQueue<>()); private final Thread thread; private volatile boolean killswitch = false; public AsyncExecutor(String threadName) { - this.thread = new Thread(this, threadName); + this.thread = Thread.ofPlatform() + .name(threadName) + .priority(Thread.NORM_PRIORITY - 1) + .daemon(false) + .unstarted(this); } public void start() { thread.start(); } - public void kill() { + public void kill() throws InterruptedException { killswitch = true; - cond.signalAll(); + LockSupport.unpark(thread); + thread.join(); } public void submit(Runnable runnable) { - mutex.lock(); - try { - jobs.offer(runnable); - cond.signalAll(); - } finally { - mutex.unlock(); - } + jobs.enqueue(runnable); + LockSupport.unpark(thread); } @Override public void run() { while (!killswitch) { try { - Runnable runnable = takeRunnable(); - if (runnable != null) { - runnable.run(); + Runnable runnable; + try { + runnable = jobs.dequeue(); + } catch (NoSuchElementException e) { + LockSupport.park(); + continue; } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + runnable.run(); } catch (Exception e) { LOGGER.error("Failed to execute async job for thread {}", thread.getName(), e); } } } - - private Runnable takeRunnable() throws InterruptedException { - mutex.lock(); - try { - while (jobs.isEmpty() && !killswitch) { - cond.await(); - } - - if (jobs.isEmpty()) return null; // We've set killswitch - - return jobs.remove(); - } finally { - mutex.unlock(); - } - } } From 6ab9b5d5d524a9b4b1b27d1f15b367791d364ac7 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 4 Jun 2025 00:39:57 +0900 Subject: [PATCH 09/57] delay to next tick when mob spawning not ready --- ...ext-tick-when-mob-spawning-not-ready.patch | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch diff --git a/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch b/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch new file mode 100644 index 00000000..7349ecb8 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Wed, 4 Jun 2025 00:31:39 +0900 +Subject: [PATCH] delay to next tick when mob spawning not ready + + +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index d61da0fbe7f6c181e4084ce60bfe7dab86f361ad..d9f74ac79e67ed7b9619041cce763b60a8f9a929 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -70,7 +70,8 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + private final Set chunkHoldersToBroadcast = new ReferenceOpenHashSet<>(); + @Nullable + @VisibleForDebug +- private NaturalSpawner.SpawnState lastSpawnState; ++ private volatile NaturalSpawner.SpawnState lastSpawnState; // Leaf ++ private final it.unimi.dsi.fastutil.longs.LongArrayList delaySpawn = new it.unimi.dsi.fastutil.longs.LongArrayList(); // Leaf + // Paper start + private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); + public int getFullChunksCount() { +@@ -656,13 +657,30 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + filteredSpawningCategories = List.of(); + } + +- for (LevelChunk levelChunk : chunks) { +- ChunkPos pos = levelChunk.getPos(); +- levelChunk.incrementInhabitedTime(timeInhabited); +- if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && lastSpawnState != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot // Pufferfish // Leaf - Don't spawn if lastSpawnState is null +- NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState, filteredSpawningCategories); // Pufferfish ++ // Leaf start ++ var lastSpawnState1 = this.lastSpawnState; ++ if (lastSpawnState1 != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get())) { ++ int delaySpawnSize = delaySpawn.size(); ++ for (LevelChunk levelChunk : chunks) { ++ ChunkPos pos = levelChunk.getPos(); ++ levelChunk.incrementInhabitedTime(timeInhabited); ++ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot ++ for (int i = 0; i < delaySpawnSize; i++) { ++ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, delaySpawn.getLong(i)); ++ } ++ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories); // Pufferfish ++ } + } ++ delaySpawn.clear(); ++ } else { ++ for (LevelChunk levelChunk : chunks) { ++ levelChunk.incrementInhabitedTime(timeInhabited); ++ } ++ delaySpawn.add(level.getGameTime()); ++ } ++ // Leaf end + ++ for (LevelChunk levelChunk : chunks) { // Leaf + if (true) { // Paper - rewrite chunk system + this.level.tickChunk(levelChunk, _int); + } +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index 8db4fd335e661111c52721be2f5ffc65a2c843d2..336bb6dd39a4678ea5d992b8857e79d69d673dd8 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -156,6 +156,11 @@ public final class NaturalSpawner { + } + + public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories) { ++ // Leaf start ++ spawnForChunk(level, chunk, spawnState, categories, level.getGameTime()); ++ } ++ public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories, long gameTime) { ++ // Leaf end + for (MobCategory mobCategory : categories) { + // Paper start - Optional per player mob spawns + final boolean canSpawn; +@@ -186,7 +191,7 @@ public final class NaturalSpawner { + } + // Paper end - throttle failed spawn attempts + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { +- spawnThisTick = ticksPerSpawnTmp != 0 && level.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts ++ spawnThisTick = ticksPerSpawnTmp != 0 && gameTime % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts // Leaf + limit = level.getWorld().getSpawnLimit(spawnCategory); + } + From 100d8bb1e62fc5efce7e61d979e223bd4fbb3529 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 4 Jun 2025 00:40:45 +0900 Subject: [PATCH 10/57] rebuild patches --- .../0007-Purpur-Server-Minecraft-Changes.patch | 14 +++++++------- .../0008-Fix-Pufferfish-and-Purpur-patches.patch | 6 +++--- .../0009-Purpur-Configurable-server-mod-name.patch | 4 ++-- .../features/0021-Leaves-Protocol-Core.patch | 4 ++-- .../features/0025-Leaves-Replay-Mod-API.patch | 8 ++++---- ...r-Skip-EntityScheduler-s-executeTick-chec.patch | 6 +++--- .../0045-Virtual-thread-for-chat-executor.patch | 4 ++-- .../0086-Nitori-Async-playerdata-saving.patch | 10 +++++----- .../features/0095-TT20-Lag-compensation.patch | 4 ++-- .../0134-SparklyPaper-Parallel-world-ticking.patch | 8 ++++---- .../0135-SparklyPaper-Track-each-world-MSPT.patch | 4 ++-- .../features/0153-Async-target-finding.patch | 4 ++-- .../features/0163-Protocol-Core.patch | 4 ++-- 13 files changed, 40 insertions(+), 40 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch index 86de64ae..6701dfc9 100644 --- a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch +++ b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch @@ -309,7 +309,7 @@ index 344d5ead46c33e303ac375922aad298481253ff2..c5e8eff72f9f9fb49885bc21f6103399 io.papermc.paper.plugin.PluginInitializerManager.load(optionSet); // Paper Bootstrap.bootStrap(); diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index a76b67a846b12a7b3d0c41b6ac4833d4f0372531..5fb7a76faf72f7d91122e5bf01c51853164a73c0 100644 +index d19e31d3b280325defcaf46e7168eafb3d6587d2..68442c7d3d34596b0b0bdc47bcf56d44090fcbce 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -266,6 +266,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - Add EntityMoveEvent serverLevel.updateLagCompensationTick(); // Paper - lag compensation net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers diff --git a/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch b/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch index 5bdde608..1daa95ac 100644 --- a/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch +++ b/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fix Pufferfish and Purpur patches diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 5fb7a76faf72f7d91122e5bf01c51853164a73c0..011b1a8c13d41c6f1d54d9247b7e61a548f43382 100644 +index 68442c7d3d34596b0b0bdc47bcf56d44090fcbce..61f9e55d6cfeecaf01d6c63a1e7db1c1706bfc62 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -277,7 +277,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system -@@ -1250,9 +1250,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= resultLimit) { return players; diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index dddbb18992348fb7e8a6552423d134809cd7fdbc..0e6e71030e3fd1335fff796b861524a48cb0a507 100644 +index b370bf88d790f8022927be6022c7fdf676215e8c..1ebd311978edbb0b786caaac13f41a7a06ee04e1 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1650,7 +1650,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop !playerList.isOp(player.getGameProfile())) .map(player -> player.getGameProfile().getName()), diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index b49dd636e730f0c5b609df68ee51bcd12efc1eaa..5e971bca365c692d4ce0c58693592002ce01471c 100644 +index 2a978aa96f4ba932e30f921e2e54bc40bad4b13e..7eff4de2a491033d84a192c96af1fc2942845e37 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -216,6 +216,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -178,7 +178,7 @@ index b49dd636e730f0c5b609df68ee51bcd12efc1eaa..5e971bca365c692d4ce0c58693592002 } diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index d44c3baa2ef30d5cd4c46e491ff9198fa558513c..f89d28595fa9ca12e414f7b3cc86085ff0769e72 100644 +index 4160a1c6c063804f23c29c66231fa004bade3caa..16a6aba187fa00fd7c3f739e46bc632987c1378f 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -195,7 +195,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc diff --git a/leaf-server/minecraft-patches/features/0038-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch b/leaf-server/minecraft-patches/features/0038-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch index f3ddd0b8..49d1d196 100644 --- a/leaf-server/minecraft-patches/features/0038-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch +++ b/leaf-server/minecraft-patches/features/0038-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch @@ -13,7 +13,7 @@ To avoid the hefty ArrayDeque's size() call, we check if we *really* need to exe Most entities won't have any scheduled tasks, so this is a nice performance bonus. These optimizations, however, wouldn't work in a Folia environment, but because in SparklyPaper executeTick is always executed on the main thread, it ain't an issue for us (yay). diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 30747b30596208bc02dfb4a6c31f8afb5c1aba8e..ed8d4f54ea49123cd60eda4fec4d0612e1478c38 100644 +index 4baef697c806993e3f8b09a2b73e3a581d2e598b..8aeb6e84ed9436bd4511350493314143aa3d56b6 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -290,6 +290,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system -@@ -1674,6 +1675,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { for (final net.minecraft.world.entity.Entity entity : level.getEntities().getAll()) { if (entity.isRemoved()) { -@@ -1685,6 +1702,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop serverPlayer1.connection.suspendFlushing()); this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit -@@ -1743,28 +1774,50 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop, ServerLevel> oldLevels = this.levels; Map, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels); newLevels.remove(level.dimension()); diff --git a/leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch b/leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch index f16beee9..6e5008e0 100644 --- a/leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch +++ b/leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch @@ -6,10 +6,10 @@ Subject: [PATCH] SparklyPaper: Track each world MSPT Original project: https://github.com/SparklyPower/SparklyPaper diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 7f791ff5bdf6c29c78f863d21af16270a74d8e7e..1431a0fac3c8a846535c1bd2f60a1279d08f14ea 100644 +index f57ca213010892e6a2f5ec84871969be51c9c9f5..97e5adf229ba514656d670521198b1f4c34c7d05 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1699,7 +1699,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system -@@ -1094,6 +1095,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop Date: Wed, 4 Jun 2025 01:10:00 +0900 Subject: [PATCH 11/57] update default config value --- .../features/0187-throttle-mob-spawning.patch | 5 ++--- ...88-delay-to-next-tick-when-mob-spawning-not-ready.patch | 4 ++-- .../leaf/config/modules/opt/ThrottleNaturalSpawnMob.java | 7 +++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch b/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch index c2e3ab5a..171c3123 100644 --- a/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch +++ b/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch @@ -5,10 +5,10 @@ Subject: [PATCH] throttle mob spawning diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index 450f17badaa3f6c8f1cdb9e6dc76828b70afe6fc..8db4fd335e661111c52721be2f5ffc65a2c843d2 100644 +index 450f17badaa3f6c8f1cdb9e6dc76828b70afe6fc..2f709cca7180e2f4874a7f80ed32498aba6dcac0 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java -@@ -160,6 +160,18 @@ public final class NaturalSpawner { +@@ -160,6 +160,17 @@ public final class NaturalSpawner { // Paper start - Optional per player mob spawns final boolean canSpawn; int maxSpawns = Integer.MAX_VALUE; @@ -17,7 +17,6 @@ index 450f17badaa3f6c8f1cdb9e6dc76828b70afe6fc..8db4fd335e661111c52721be2f5ffc65 + int spawnChance = org.dreeam.leaf.config.modules.opt.ThrottleNaturalSpawnMob.spawnChance[mobCategory.ordinal()]; + long failedAttempt = org.dreeam.leaf.config.modules.opt.ThrottleNaturalSpawnMob.failedAttempts[mobCategory.ordinal()]; + if (failedAttempt >= 0L -+ && spawnChance >= 0 + && chunk.failedSpawnAttempts[mobCategory.ordinal()] >= failedAttempt + && (level.random.nextInt() & 1023) > spawnChance) { + continue; diff --git a/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch b/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch index 7349ecb8..ce40d12e 100644 --- a/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch +++ b/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch @@ -55,7 +55,7 @@ index d61da0fbe7f6c181e4084ce60bfe7dab86f361ad..d9f74ac79e67ed7b9619041cce763b60 this.level.tickChunk(levelChunk, _int); } diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index 8db4fd335e661111c52721be2f5ffc65a2c843d2..336bb6dd39a4678ea5d992b8857e79d69d673dd8 100644 +index 2f709cca7180e2f4874a7f80ed32498aba6dcac0..32c9106b70ef7ab85077d6ec4bd7d7b84d9f8c00 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java @@ -156,6 +156,11 @@ public final class NaturalSpawner { @@ -70,7 +70,7 @@ index 8db4fd335e661111c52721be2f5ffc65a2c843d2..336bb6dd39a4678ea5d992b8857e79d6 for (MobCategory mobCategory : categories) { // Paper start - Optional per player mob spawns final boolean canSpawn; -@@ -186,7 +191,7 @@ public final class NaturalSpawner { +@@ -185,7 +190,7 @@ public final class NaturalSpawner { } // Paper end - throttle failed spawn attempts if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java index 2be54108..73bb8eb7 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java @@ -21,8 +21,11 @@ public class ThrottleNaturalSpawnMob extends ConfigModules { spawnChance = new int[categories.length]; for (int i = 0; i < categories.length; i++) { String category = getBasePath() + "." + categories[i].getSerializedName(); - failedAttempts[i] = config.getLong(category + ".failed-attempts", -1); - spawnChance[i] = (int) Math.round(config.getDouble(category + ".spawn-chance", 100.0) * 10.24); + long attempts = config.getLong(category + ".min-failed-attempts", 8); + double chance = config.getDouble(category + ".spawn-chance", 25.0); + + failedAttempts[i] = Math.max(-1, attempts); + spawnChance[i] = Math.clamp(0, (int) Math.round(chance * 10.24), 1024); } } } From 9d6e3ae5ff6dbab1d72dc890bd25737b6c9cedf9 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 4 Jun 2025 21:34:07 +0900 Subject: [PATCH 12/57] preload mob spawning position --- .../features/0184-optimise-getBiome.patch | 10 +- ...patch => 0185-optimize-mob-spawning.patch} | 108 +++++++++++++++--- .../features/0187-throttle-mob-spawning.patch | 12 +- ...ext-tick-when-mob-spawning-not-ready.patch | 81 ------------- .../0188-preload-mob-spawning-position.patch | 108 ++++++++++++++++++ .../config/modules/opt/OptimizeBiome.java | 8 +- .../opt/PreloadNaturalMobSpawning.java | 17 +++ ...b.java => ThrottleNaturalMobSpawning.java} | 6 +- 8 files changed, 238 insertions(+), 112 deletions(-) rename leaf-server/minecraft-patches/features/{0185-optimise-NaturalSpawner-spawnForChunk.patch => 0185-optimize-mob-spawning.patch} (53%) delete mode 100644 leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch create mode 100644 leaf-server/minecraft-patches/features/0188-preload-mob-spawning-position.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/PreloadNaturalMobSpawning.java rename leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/{ThrottleNaturalSpawnMob.java => ThrottleNaturalMobSpawning.java} (91%) diff --git a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch index 4eaf4697..bd1a671c 100644 --- a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch +++ b/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch @@ -5,7 +5,7 @@ Subject: [PATCH] optimise getBiome diff --git a/net/minecraft/advancements/critereon/LocationPredicate.java b/net/minecraft/advancements/critereon/LocationPredicate.java -index a26a5311f87873e0d4d26fda9cb8956a32ee81e8..9405f1f211db3fa2d313429866de454ede64d95b 100644 +index a26a5311f87873e0d4d26fda9cb8956a32ee81e8..65e4315cce35814c60b21bbd5baea2ffac82162c 100644 --- a/net/minecraft/advancements/critereon/LocationPredicate.java +++ b/net/minecraft/advancements/critereon/LocationPredicate.java @@ -49,7 +49,7 @@ public record LocationPredicate( @@ -13,7 +13,7 @@ index a26a5311f87873e0d4d26fda9cb8956a32ee81e8..9405f1f211db3fa2d313429866de454e BlockPos blockPos = BlockPos.containing(x, y, z); boolean isLoaded = level.isLoaded(blockPos); - return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(level.getBiome(blockPos))) -+ return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(level.getBiomeCached(blockPos))) // Leaf - cache getBiome ++ return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(org.dreeam.leaf.config.modules.opt.OptimizeBiome.advancement ? level.getBiomeCached(blockPos) : level.getBiome(blockPos))) // Leaf - cache getBiome && (!this.structures.isPresent() || isLoaded && level.structureManager().getStructureWithPieceAt(blockPos, this.structures.get()).isValid()) && (!this.smokey.isPresent() || isLoaded && this.smokey.get() == CampfireBlock.isSmokeyPos(level, blockPos)) && (!this.light.isPresent() || this.light.get().matches(level, blockPos)) @@ -35,7 +35,7 @@ index 26c8c1e5598daf3550aef05b12218c47bda6618b..91b2fc5b2da6566b05fef1111665b895 int floor = Mth.floor(aabb.minX); int floor1 = Mth.floor(aabb.maxX); diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..c0d941af10ffe8c158dab9db40c7c5767b6cfd6e 100644 +index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..9b37b763c6555705f3e256010f508b5a0c2cdb66 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java @@ -445,7 +445,7 @@ public final class NaturalSpawner { @@ -43,7 +43,7 @@ index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..c0d941af10ffe8c158dab9db40c7c576 ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, RandomSource random, BlockPos pos ) { - Holder biome = level.getBiome(pos); -+ Holder biome = level.getBiomeCached(pos); // Leaf - cache getBiome ++ Holder biome = org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(pos) : level.getBiome(pos); // Leaf - cache getBiome return category == MobCategory.WATER_AMBIENT && biome.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F ? Optional.empty() : mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random); @@ -52,7 +52,7 @@ index ce2621a87dec1befb016b3437ceb2d02ed6d0b75..c0d941af10ffe8c158dab9db40c7c576 return isInNetherFortressBounds(pos, level, category, structureManager) ? NetherFortressStructure.FORTRESS_ENEMIES - : generator.getMobsAt(biome != null ? biome : level.getBiome(pos), structureManager, category, pos); -+ : generator.getMobsAt(biome != null ? biome : level.getBiomeCached(pos), structureManager, category, pos); // Leaf - cache getBiome ++ : generator.getMobsAt(biome != null ? biome : (org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(pos) : level.getBiome(pos)), structureManager, category, pos); // Leaf - cache getBiome } public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) { diff --git a/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch b/leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch similarity index 53% rename from leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch rename to leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch index 6601e40d..c4f91deb 100644 --- a/leaf-server/minecraft-patches/features/0185-optimise-NaturalSpawner-spawnForChunk.patch +++ b/leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch @@ -1,14 +1,95 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 3 Jun 2025 15:20:59 +0900 -Subject: [PATCH] optimise NaturalSpawner#spawnForChunk +Subject: [PATCH] optimize mob spawning +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index d61da0fbe7f6c181e4084ce60bfe7dab86f361ad..1ce1b21a77cd727b52bd937b65ce97517fed9c68 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -70,7 +70,9 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + private final Set chunkHoldersToBroadcast = new ReferenceOpenHashSet<>(); + @Nullable + @VisibleForDebug +- private NaturalSpawner.SpawnState lastSpawnState; ++ private volatile NaturalSpawner.SpawnState lastSpawnState; // Leaf ++ private long delayTimeInhabited = 0L; // Leaf ++ private long delaySpawn = -1L; // Leaf + // Paper start + private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); + public int getFullChunksCount() { +@@ -656,13 +658,37 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + filteredSpawningCategories = List.of(); + } + +- for (LevelChunk levelChunk : chunks) { +- ChunkPos pos = levelChunk.getPos(); +- levelChunk.incrementInhabitedTime(timeInhabited); +- if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && lastSpawnState != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot // Pufferfish // Leaf - Don't spawn if lastSpawnState is null +- NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState, filteredSpawningCategories); // Pufferfish ++ // Leaf start ++ var lastSpawnState1 = this.lastSpawnState; ++ if (lastSpawnState1 != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get())) { ++ long sumTimeInhabited = timeInhabited + delayTimeInhabited; ++ long time = level.getGameTime(); ++ for (LevelChunk levelChunk : chunks) { ++ ChunkPos pos = levelChunk.getPos(); ++ levelChunk.incrementInhabitedTime(sumTimeInhabited); ++ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot ++ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish ++ } ++ } ++ if (delaySpawn != -1L) { ++ time = delaySpawn; ++ for (LevelChunk levelChunk : chunks) { ++ ChunkPos pos = levelChunk.getPos(); ++ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot ++ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish ++ } ++ } + } ++ delaySpawn = -1L; ++ delayTimeInhabited = 0L; ++ } else { ++ // unlikely ++ delayTimeInhabited += timeInhabited; ++ delaySpawn = level.getGameTime(); ++ } ++ // Leaf end + ++ for (LevelChunk levelChunk : chunks) { // Leaf - split to 2 loop + if (true) { // Paper - rewrite chunk system + this.level.tickChunk(levelChunk, _int); + } diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..450f17badaa3f6c8f1cdb9e6dc76828b70afe6fc 100644 +index 9b37b763c6555705f3e256010f508b5a0c2cdb66..7bfc636fb442036f742903c4e69a8a9fcd0e6fc2 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java -@@ -238,10 +238,13 @@ public final class NaturalSpawner { +@@ -155,7 +155,13 @@ public final class NaturalSpawner { + return list; + } + ++ @Deprecated(forRemoval = true) // Leaf + public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories) { ++ // Leaf start ++ spawnForChunk(level, chunk, spawnState, categories, level.getGameTime()); ++ } ++ public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories, long gameTime) { ++ // Leaf end + for (MobCategory mobCategory : categories) { + // Paper start - Optional per player mob spawns + final boolean canSpawn; +@@ -174,7 +180,7 @@ public final class NaturalSpawner { + } + // Paper end - throttle failed spawn attempts + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { +- spawnThisTick = ticksPerSpawnTmp != 0 && level.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts ++ spawnThisTick = ticksPerSpawnTmp != 0 && gameTime % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts // Leaf + limit = level.getWorld().getSpawnLimit(spawnCategory); + } + +@@ -238,12 +244,14 @@ public final class NaturalSpawner { // Paper end - throttle failed spawn attempts ) { // Paper end - Optional per player mob spawns @@ -18,27 +99,24 @@ index c0d941af10ffe8c158dab9db40c7c5767b6cfd6e..450f17badaa3f6c8f1cdb9e6dc76828b + // Leaf start + BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); + mutableRandomPosWithin(pos, level, chunk); -+ if (pos.getY() >= level.getMinY() + 1) { -+ return spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts ++ if (pos.getY() < level.getMinY() + 1) { ++ return 0; } +- +- return 0; // Paper - throttle failed spawn attempts ++ return spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts + // Leaf end - - return 0; // Paper - throttle failed spawn attempts } -@@ -275,31 +278,60 @@ public final class NaturalSpawner { + + @VisibleForDebug +@@ -275,31 +283,55 @@ public final class NaturalSpawner { StructureManager structureManager = level.structureManager(); ChunkGenerator generator = level.getChunkSource().getGenerator(); int y = pos.getY(); + int posX = pos.getX(); // Leaf + int posZ = pos.getZ(); // Leaf int i = 0; // Paper - throttle failed spawn attempts -- BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn -+ // Leaf start -+ if (!level.getWorldBorder().isWithinBounds(pos) || level.isOutsideBuildHeight(pos)) { -+ return i; -+ } -+ BlockState blockState = chunk.getPos().longKey == ChunkPos.asLong(pos) ? chunk.getBlockState(posX, y, posZ) : level.getBlockStateIfLoaded(pos); -+ // Leaf end + BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn - BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + BlockPos.MutableBlockPos mutableBlockPos = pos instanceof BlockPos.MutableBlockPos pos2 ? pos2 : new BlockPos.MutableBlockPos(); // Leaf diff --git a/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch b/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch index 171c3123..d4079d19 100644 --- a/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch +++ b/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch @@ -1,21 +1,21 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: hayanesuru -Date: Tue, 3 Jun 2025 21:34:25 +0900 +Date: Wed, 4 Jun 2025 20:54:03 +0900 Subject: [PATCH] throttle mob spawning diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index 450f17badaa3f6c8f1cdb9e6dc76828b70afe6fc..2f709cca7180e2f4874a7f80ed32498aba6dcac0 100644 +index 7bfc636fb442036f742903c4e69a8a9fcd0e6fc2..762bdf1fe19546f89d34b9efdad66b00dab80006 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java -@@ -160,6 +160,17 @@ public final class NaturalSpawner { +@@ -166,6 +166,17 @@ public final class NaturalSpawner { // Paper start - Optional per player mob spawns final boolean canSpawn; int maxSpawns = Integer.MAX_VALUE; + // Leaf start -+ if (org.dreeam.leaf.config.modules.opt.ThrottleNaturalSpawnMob.enabled) { -+ int spawnChance = org.dreeam.leaf.config.modules.opt.ThrottleNaturalSpawnMob.spawnChance[mobCategory.ordinal()]; -+ long failedAttempt = org.dreeam.leaf.config.modules.opt.ThrottleNaturalSpawnMob.failedAttempts[mobCategory.ordinal()]; ++ if (org.dreeam.leaf.config.modules.opt.ThrottleNaturalMobSpawning.enabled) { ++ int spawnChance = org.dreeam.leaf.config.modules.opt.ThrottleNaturalMobSpawning.spawnChance[mobCategory.ordinal()]; ++ long failedAttempt = org.dreeam.leaf.config.modules.opt.ThrottleNaturalMobSpawning.failedAttempts[mobCategory.ordinal()]; + if (failedAttempt >= 0L + && chunk.failedSpawnAttempts[mobCategory.ordinal()] >= failedAttempt + && (level.random.nextInt() & 1023) > spawnChance) { diff --git a/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch b/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch deleted file mode 100644 index ce40d12e..00000000 --- a/leaf-server/minecraft-patches/features/0188-delay-to-next-tick-when-mob-spawning-not-ready.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: hayanesuru -Date: Wed, 4 Jun 2025 00:31:39 +0900 -Subject: [PATCH] delay to next tick when mob spawning not ready - - -diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index d61da0fbe7f6c181e4084ce60bfe7dab86f361ad..d9f74ac79e67ed7b9619041cce763b60a8f9a929 100644 ---- a/net/minecraft/server/level/ServerChunkCache.java -+++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -70,7 +70,8 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - private final Set chunkHoldersToBroadcast = new ReferenceOpenHashSet<>(); - @Nullable - @VisibleForDebug -- private NaturalSpawner.SpawnState lastSpawnState; -+ private volatile NaturalSpawner.SpawnState lastSpawnState; // Leaf -+ private final it.unimi.dsi.fastutil.longs.LongArrayList delaySpawn = new it.unimi.dsi.fastutil.longs.LongArrayList(); // Leaf - // Paper start - private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); - public int getFullChunksCount() { -@@ -656,13 +657,30 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - filteredSpawningCategories = List.of(); - } - -- for (LevelChunk levelChunk : chunks) { -- ChunkPos pos = levelChunk.getPos(); -- levelChunk.incrementInhabitedTime(timeInhabited); -- if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && lastSpawnState != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot // Pufferfish // Leaf - Don't spawn if lastSpawnState is null -- NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState, filteredSpawningCategories); // Pufferfish -+ // Leaf start -+ var lastSpawnState1 = this.lastSpawnState; -+ if (lastSpawnState1 != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get())) { -+ int delaySpawnSize = delaySpawn.size(); -+ for (LevelChunk levelChunk : chunks) { -+ ChunkPos pos = levelChunk.getPos(); -+ levelChunk.incrementInhabitedTime(timeInhabited); -+ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot -+ for (int i = 0; i < delaySpawnSize; i++) { -+ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, delaySpawn.getLong(i)); -+ } -+ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories); // Pufferfish -+ } - } -+ delaySpawn.clear(); -+ } else { -+ for (LevelChunk levelChunk : chunks) { -+ levelChunk.incrementInhabitedTime(timeInhabited); -+ } -+ delaySpawn.add(level.getGameTime()); -+ } -+ // Leaf end - -+ for (LevelChunk levelChunk : chunks) { // Leaf - if (true) { // Paper - rewrite chunk system - this.level.tickChunk(levelChunk, _int); - } -diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index 2f709cca7180e2f4874a7f80ed32498aba6dcac0..32c9106b70ef7ab85077d6ec4bd7d7b84d9f8c00 100644 ---- a/net/minecraft/world/level/NaturalSpawner.java -+++ b/net/minecraft/world/level/NaturalSpawner.java -@@ -156,6 +156,11 @@ public final class NaturalSpawner { - } - - public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories) { -+ // Leaf start -+ spawnForChunk(level, chunk, spawnState, categories, level.getGameTime()); -+ } -+ public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories, long gameTime) { -+ // Leaf end - for (MobCategory mobCategory : categories) { - // Paper start - Optional per player mob spawns - final boolean canSpawn; -@@ -185,7 +190,7 @@ public final class NaturalSpawner { - } - // Paper end - throttle failed spawn attempts - if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { -- spawnThisTick = ticksPerSpawnTmp != 0 && level.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts -+ spawnThisTick = ticksPerSpawnTmp != 0 && gameTime % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts // Leaf - limit = level.getWorld().getSpawnLimit(spawnCategory); - } - diff --git a/leaf-server/minecraft-patches/features/0188-preload-mob-spawning-position.patch b/leaf-server/minecraft-patches/features/0188-preload-mob-spawning-position.patch new file mode 100644 index 00000000..a48c0781 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0188-preload-mob-spawning-position.patch @@ -0,0 +1,108 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Wed, 4 Jun 2025 20:54:32 +0900 +Subject: [PATCH] preload mob spawning position + + +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index 762bdf1fe19546f89d34b9efdad66b00dab80006..0443df34de4f940f64e563ea76453493cadf200b 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -257,9 +257,56 @@ public final class NaturalSpawner { + // Paper end - Optional per player mob spawns + // Leaf start + BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); +- mutableRandomPosWithin(pos, level, chunk); +- if (pos.getY() < level.getMinY() + 1) { +- return 0; ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.opt.PreloadNaturalMobSpawning.enabled) { ++ if (chunk.cacheSpawnPosIndex == 16 || chunk.cacheSpawnPosIndex == -1) { ++ if (chunk.cacheSpawnPos == null) { ++ chunk.cacheSpawnPos = new long[16]; ++ } ++ // cache friendly ++ for (int i = 0; i < 16; i++) { ++ mutableRandomPosWithin(pos, level, chunk); ++ if (pos.getY() >= level.getMinY() + 1 ++ && level.getWorldBorder().isWithinBounds(pos) ++ && !level.isOutsideBuildHeight(pos)) { ++ LevelChunk chunk1 = chunk.getPos().longKey == ChunkPos.asLong(pos) ++ ? chunk ++ : level.chunkSource.getChunkAtIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); ++ if (chunk1 != null) { ++ BlockState bs = chunk1.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); ++ if (bs != null && !bs.isRedstoneConductor(level, pos)) { ++ chunk.cacheSpawnPos[i] = BlockPos.asLong(pos.getX(), pos.getY(), pos.getZ()); ++ continue; ++ } ++ } ++ } ++ chunk.cacheSpawnPos[i] = -1; ++ } ++ chunk.cacheSpawnPosIndex = 0; ++ } ++ long cachePos = chunk.cacheSpawnPos[chunk.cacheSpawnPosIndex]; ++ chunk.cacheSpawnPosIndex++; ++ if (cachePos == -1) { ++ return 0; ++ } ++ pos.set(cachePos); ++ } else { ++ mutableRandomPosWithin(pos, level, chunk); ++ if (pos.getY() < level.getMinY() + 1 ++ || !level.getWorldBorder().isWithinBounds(pos) ++ || level.isOutsideBuildHeight(pos)) { ++ return 0; ++ } ++ LevelChunk chunk1 = chunk.getPos().longKey == ChunkPos.asLong(pos) ++ ? chunk ++ : level.chunkSource.getChunkAtIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); ++ if (chunk1 == null) { ++ return 0; ++ } ++ BlockState bs = chunk1.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); ++ if (bs == null || bs.isRedstoneConductor(level, pos)) { ++ return 0; ++ } + } + return spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts + // Leaf end +@@ -284,7 +331,12 @@ public final class NaturalSpawner { + MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer trackEntity + // Paper start - throttle failed spawn attempts + ) { +- spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); ++ // Leaf start ++ BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); ++ if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { ++ spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); ++ } ++ // Leaf end + } + public static int spawnCategoryForPosition( + MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer trackEntity, final boolean nothing +@@ -297,8 +349,8 @@ public final class NaturalSpawner { + int posX = pos.getX(); // Leaf + int posZ = pos.getZ(); // Leaf + int i = 0; // Paper - throttle failed spawn attempts +- BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn +- if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn ++ // BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn // Leaf ++ if (true /*blockState != null && !blockState.isRedstoneConductor(chunk, pos)*/) { // Paper - don't load chunks for mob spawn // Leaf + BlockPos.MutableBlockPos mutableBlockPos = pos instanceof BlockPos.MutableBlockPos pos2 ? pos2 : new BlockPos.MutableBlockPos(); // Leaf + //int i = 0; // Paper - throttle failed spawn attempts - move up + +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index e6eab6929b08503c49debbbd25497ffedad438e1..624a177695580510c0a49d4503dee72da7fd7114 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -106,6 +106,8 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + // Paper start - rewrite chunk system + private boolean postProcessingDone; + private net.minecraft.server.level.ServerChunkCache.ChunkAndHolder chunkAndHolder; ++ public long[] cacheSpawnPos = null; // Leaf ++ public int cacheSpawnPosIndex = -1; // Leaf + + @Override + public final boolean moonrise$isPostProcessingDone() { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java index 3287f98c..27d2a9d3 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java @@ -5,13 +5,17 @@ import org.dreeam.leaf.config.EnumConfigCategory; public class OptimizeBiome extends ConfigModules { public String getBasePath() { - return EnumConfigCategory.PERF.getBaseKeyName() + ".cache-mob-spawning-biome"; + return EnumConfigCategory.PERF.getBaseKeyName() + ".cache-biome"; } public static boolean enabled = false; + public static boolean mobSpawn = false; + public static boolean advancement = false; @Override public void onLoaded() { - enabled = config().getBoolean(getBasePath(), enabled); + enabled = config().getBoolean(getBasePath() + ".enabled", enabled); + mobSpawn = config.getBoolean(getBasePath() + ".mob-spawning", false); + advancement = config.getBoolean(getBasePath() + ".advancements", false); } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/PreloadNaturalMobSpawning.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/PreloadNaturalMobSpawning.java new file mode 100644 index 00000000..50a5742e --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/PreloadNaturalMobSpawning.java @@ -0,0 +1,17 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class PreloadNaturalMobSpawning extends ConfigModules { + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".preload-mob-spawning-position"; + } + + public static boolean enabled = false; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".enabled", enabled); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalMobSpawning.java similarity index 91% rename from leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java rename to leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalMobSpawning.java index 73bb8eb7..8dbcff11 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalSpawnMob.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalMobSpawning.java @@ -4,9 +4,9 @@ import net.minecraft.world.entity.MobCategory; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; -public class ThrottleNaturalSpawnMob extends ConfigModules { +public class ThrottleNaturalMobSpawning extends ConfigModules { public String getBasePath() { - return EnumConfigCategory.PERF.getBaseKeyName() + ".throttled-mob-spawning"; + return EnumConfigCategory.PERF.getBaseKeyName() + ".throttle-mob-spawning"; } public static boolean enabled = false; @@ -21,7 +21,7 @@ public class ThrottleNaturalSpawnMob extends ConfigModules { spawnChance = new int[categories.length]; for (int i = 0; i < categories.length; i++) { String category = getBasePath() + "." + categories[i].getSerializedName(); - long attempts = config.getLong(category + ".min-failed-attempts", 8); + long attempts = config.getLong(category + ".min-failed", 8); double chance = config.getDouble(category + ".spawn-chance", 25.0); failedAttempts[i] = Math.max(-1, attempts); From 5ad9ec1b59d6f4d39d06cb1802d8f6d54d738c3f Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 4 Jun 2025 22:05:19 +0900 Subject: [PATCH 13/57] move shutdown executor --- ...004-Pufferfish-Optimize-mob-spawning.patch | 25 +++++------- ...0007-Purpur-Server-Minecraft-Changes.patch | 14 +++---- ...08-Fix-Pufferfish-and-Purpur-patches.patch | 6 +-- ...-Purpur-Configurable-server-mod-name.patch | 4 +- .../features/0021-Leaves-Protocol-Core.patch | 4 +- .../features/0025-Leaves-Replay-Mod-API.patch | 4 +- ...p-EntityScheduler-s-executeTick-chec.patch | 6 +-- ...045-Virtual-thread-for-chat-executor.patch | 4 +- .../0086-Nitori-Async-playerdata-saving.patch | 17 -------- .../features/0095-TT20-Lag-compensation.patch | 4 +- ...-SparklyPaper-Parallel-world-ticking.patch | 8 ++-- ...5-SparklyPaper-Track-each-world-MSPT.patch | 4 +- .../features/0153-Async-target-finding.patch | 17 +------- .../features/0163-Protocol-Core.patch | 4 +- .../dreeam/leaf/async/ShutdownExecutors.java | 39 +++++++++++++++++++ .../async/tracker/MultithreadedTracker.java | 2 +- 16 files changed, 81 insertions(+), 81 deletions(-) create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java diff --git a/leaf-server/minecraft-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch b/leaf-server/minecraft-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch index bb16fab5..c1290ce2 100644 --- a/leaf-server/minecraft-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch +++ b/leaf-server/minecraft-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch @@ -22,7 +22,7 @@ and, in my opinion, worth the low risk of minor mob-spawning-related inconsistencies. diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 4ba85d704ffebae38f7a76a97a182e3674730c6f..d19e31d3b280325defcaf46e7168eafb3d6587d2 100644 +index 4ba85d704ffebae38f7a76a97a182e3674730c6f..48ca0ba4c18e596e0d122fdc533dfd65bde19db7 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -286,6 +286,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system -@@ -1055,6 +1056,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - Add EntityMoveEvent serverLevel.updateLagCompensationTick(); // Paper - lag compensation net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers diff --git a/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch b/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch index 1daa95ac..71162db2 100644 --- a/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch +++ b/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fix Pufferfish and Purpur patches diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 68442c7d3d34596b0b0bdc47bcf56d44090fcbce..61f9e55d6cfeecaf01d6c63a1e7db1c1706bfc62 100644 +index f444e53b8ad596cf64fb6ed33921e18e0d054fc0..719615a559fbcbb2ba78f2f8b333919f4875ae0d 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -277,7 +277,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system -@@ -1258,9 +1258,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= resultLimit) { return players; diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index b370bf88d790f8022927be6022c7fdf676215e8c..1ebd311978edbb0b786caaac13f41a7a06ee04e1 100644 +index ea5ce91b7114149f2e0f77978ecd1bb15062be71..0412b0183f44f6b7efe65c8d30f86d5ddf30912e 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1658,7 +1658,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system -@@ -1682,6 +1683,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { for (final net.minecraft.world.entity.Entity entity : level.getEntities().getAll()) { if (entity.isRemoved()) { -@@ -1693,6 +1710,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop serverPlayer1.connection.suspendFlushing()); this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit -@@ -1751,28 +1782,50 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop, ServerLevel> oldLevels = this.levels; Map, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels); newLevels.remove(level.dimension()); diff --git a/leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch b/leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch index 6e5008e0..84f9e266 100644 --- a/leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch +++ b/leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch @@ -6,10 +6,10 @@ Subject: [PATCH] SparklyPaper: Track each world MSPT Original project: https://github.com/SparklyPower/SparklyPaper diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index f57ca213010892e6a2f5ec84871969be51c9c9f5..97e5adf229ba514656d670521198b1f4c34c7d05 100644 +index 15de8904a43c0ee1e6d55d511ebd84df57774e32..fd1de4607b64860332432c8781f60d9933065d95 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1707,7 +1707,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system -@@ -1102,6 +1103,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop Date: Wed, 4 Jun 2025 23:14:00 +0900 Subject: [PATCH 14/57] improve LibsDisguises compatibility --- .../features/0085-Multithreaded-Tracker.patch | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index f18da995..31a1db9f 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -71,7 +71,7 @@ index 9c0c99b936b4a82ebfe924866e53ec71f7bbe9ad..2ccff968cb2065d34fad4d27573f9e30 .add( new ClientboundUpdateAttributesPacket.AttributeSnapshot( diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e96e9a139 100644 +index 5d9d233e3a568aa6297ed9c703fa450f98158602..c55c8e9b777e4999a8a8de6d821b53245dc578c2 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -248,6 +248,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -104,26 +104,51 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e // Paper start - optimise entity tracker if (true) { this.newTrackerTick(); -@@ -1073,7 +1089,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1073,7 +1089,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final Entity entity; private final int range; SectionPos lastSectionPos; - public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl + // Leaf start - Multithreaded tracker + public static final ServerPlayerConnection[] EMPTY_OBJECT_ARRAY = new ServerPlayerConnection[0]; -+ public final Set seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>()) : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl ++ private final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet nonSyncSeenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>() { ++ @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 seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? 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() { -+ return seenByArray; ++ if (!seenByUpdated) { ++ return seenByArray; ++ } else { ++ return seenBy.toArray(EMPTY_OBJECT_ARRAY); ++ } + } + public void seenByUpdated() { + this.seenByArray = this.seenBy.toArray(EMPTY_OBJECT_ARRAY); ++ seenByUpdated = false; + } + // Leaf end - Multithreaded tracker // Paper start - optimise entity tracker private long lastChunkUpdate = -1L; -@@ -1100,27 +1126,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1100,27 +1151,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.lastTrackedChunk = chunk; final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); @@ -145,13 +170,13 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e if (!players.contains(player)) { - this.removePlayer(player); + removed |= this.removePlayerMulti(player); - } - } ++ } ++ } + if (removed) { + this.seenByUpdated(); + } - } - } ++ } ++ } + // Leaf end - Multithreaded tracker + + // Leaf start - Multithreaded tracker @@ -199,8 +224,8 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e + } + if (removed) { + this.seenByUpdated(); -+ } -+ } + } + } + }; + + // Only update asynchronously for real player, and sync update for fake players @@ -212,8 +237,8 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e + } else { + updatePlayerTasks.run(); + return null; -+ } -+ } + } + } + // Leaf end - Multithreaded tracker @Override @@ -224,7 +249,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(conn.getPlayer())) { foundToRemove = true; break; -@@ -1131,12 +1225,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1131,12 +1250,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return; } @@ -240,7 +265,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e } @Override -@@ -1146,10 +1241,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1146,10 +1266,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (this.seenBy.isEmpty()) { return; } @@ -254,7 +279,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e } @Override -@@ -1176,7 +1272,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1176,7 +1297,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcast(Packet packet) { @@ -263,7 +288,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e serverPlayerConnection.send(packet); } } -@@ -1189,21 +1285,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1189,21 +1310,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcastRemoved() { @@ -301,7 +326,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e // Paper start - remove allocation of Vec3D here // Vec3 vec3 = player.position().subtract(this.entity.position()); double vec3_dx = player.getX() - this.entity.getX(); -@@ -1231,6 +1340,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1231,6 +1365,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // CraftBukkit end if (flag) { if (this.seenBy.add(player.connection)) { @@ -309,7 +334,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e // 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); -@@ -1239,6 +1349,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1239,6 +1374,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)) { From 672000867b14f450036e3aafe64dd5dfad9283e4 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Thu, 5 Jun 2025 00:22:17 +0900 Subject: [PATCH 15/57] cleanup --- .../features/0163-Protocol-Core.patch | 12 --- .../leaf/protocol/DoABarrelRollProtocol.java | 78 ++++++++----------- .../org/dreeam/leaf/protocol/Protocol.java | 5 +- .../org/dreeam/leaf/protocol/Protocols.java | 6 -- 4 files changed, 35 insertions(+), 66 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch index d4a08147..075bf1d7 100644 --- a/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch +++ b/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch @@ -33,18 +33,6 @@ index f5f0e4f5d7a4b34514e102020d2a7be313292f7f..0baa48054beead8f187b56ea8d719166 for (int i = 0; i < this.tickables.size(); i++) { this.tickables.get(i).run(); -diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index 5351d373aa0f4450386a6b50f88052c031949f5b..ea4067be7e800b58dd59f7ffec58c1e026f68c6e 100644 ---- a/net/minecraft/server/level/ServerEntity.java -+++ b/net/minecraft/server/level/ServerEntity.java -@@ -283,6 +283,7 @@ public class ServerEntity { - this.entity.hurtMarked = false; - this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity)); - } -+ if (entity instanceof ServerPlayer serverPlayer) org.dreeam.leaf.protocol.Protocols.tickTracker(serverPlayer); // Leaf - Protocol core - } - - // Purpur start diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java index 09f517059aa47ca67329bc913243d4fdee09abe5..50cf63666071f5d01a85dfc6c6c45c19b05d8ec2 100644 --- a/net/minecraft/server/level/ServerPlayer.java diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java index af88f866..8859fe24 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java @@ -1,14 +1,7 @@ package org.dreeam.leaf.protocol; import com.google.common.collect.ImmutableList; -import it.unimi.dsi.fastutil.objects.Reference2BooleanMap; -import it.unimi.dsi.fastutil.objects.Reference2BooleanMaps; -import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap; -import it.unimi.dsi.fastutil.objects.Reference2FloatMap; -import it.unimi.dsi.fastutil.objects.Reference2FloatMaps; -import it.unimi.dsi.fastutil.objects.Reference2FloatOpenHashMap; -import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; -import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.*; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; @@ -25,7 +18,6 @@ import org.dreeam.leaf.protocol.DoABarrelRollPackets.KineticDamage; import org.dreeam.leaf.protocol.DoABarrelRollPackets.ModConfigServer; import org.dreeam.leaf.protocol.DoABarrelRollPackets.RollSyncC2SPacket; import org.dreeam.leaf.protocol.DoABarrelRollPackets.RollSyncS2CPacket; -import org.jetbrains.annotations.NotNull; import org.bukkit.event.player.PlayerKickEvent; import java.util.List; @@ -54,8 +46,8 @@ public class DoABarrelRollProtocol implements Protocol { private ModConfigServer config = DEFAULT; private boolean configUpdated = false; - private final Reference2ReferenceMap syncStates = new Reference2ReferenceOpenHashMap<>(); - private final Reference2ReferenceMap scheduledKicks = new Reference2ReferenceOpenHashMap<>(); + private final Reference2ReferenceMap syncStates = Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>()); + private final Reference2ReferenceMap scheduledKicks = Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>()); public final Reference2BooleanMap isRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>()); public final Reference2FloatMap rollMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap<>()); public final Reference2BooleanMap lastIsRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>()); @@ -99,14 +91,14 @@ public class DoABarrelRollProtocol implements Protocol { } @Override - public void handle(ServerPlayer player, @NotNull LeafCustomPayload payload) { + public void handle(ServerPlayer player, LeafCustomPayload payload) { switch (payload) { case ConfigUpdateC2SPacket ignored -> player.connection.send(Protocols.createPacket(new ConfigUpdateAckS2CPacket(PROTOCOL_VERSION, false))); case ConfigResponseC2SPacket configResponseC2SPacket -> { var reply = clientReplied(player.connection, configResponseC2SPacket); if (reply == HandshakeState.RESEND) { - sendHandshake(player); + sendHandshake(player.connection); } } case RollSyncC2SPacket rollSyncC2SPacket -> { @@ -138,41 +130,39 @@ public class DoABarrelRollProtocol implements Protocol { } @Override - public void tickTracker(ServerPlayer player) { - if (!isRollingMap.containsKey(player.connection)) { + public void tickPlayer(ServerPlayer player) { + ServerGamePacketListenerImpl connection = player.connection; + if (getHandshakeState(connection).state == HandshakeState.NOT_SENT) { + sendHandshake(connection); + } + if (!isRollingMap.containsKey(connection)) { return; } + if (!isRollingMap.getBoolean(connection)) { + rollMap.put(connection, 0.0F); + } - var isRolling = isRollingMap.getBoolean(player.connection); - var roll = rollMap.getFloat(player.connection); - var lastIsRolling = lastIsRollingMap.getBoolean(player.connection); - var lastRoll = lastRollMap.getFloat(player.connection); + boolean isRolling = isRollingMap.getBoolean(connection); + float roll = rollMap.getFloat(connection); + boolean lastIsRolling = lastIsRollingMap.getBoolean(connection); + float lastRoll = lastRollMap.getFloat(connection); if (isRolling == lastIsRolling && roll == lastRoll) { return; } var payload = new RollSyncS2CPacket(player.getId(), isRolling, roll); var packet = Protocols.createPacket(payload); - for (ServerPlayerConnection seenBy : player.moonrise$getTrackedEntity().seenBy()) { + var tracked = player.moonrise$getTrackedEntity(); + if (tracked == null) { + return; + } + for (ServerPlayerConnection seenBy : tracked.seenBy()) { if (seenBy instanceof ServerGamePacketListenerImpl conn && getHandshakeState(conn).state == HandshakeState.ACCEPTED) { seenBy.send(packet); } } - lastIsRollingMap.put(player.connection, isRolling); - lastRollMap.put(player.connection, roll); - } - - @Override - public void tickPlayer(ServerPlayer player) { - if (getHandshakeState(player.connection).state == HandshakeState.NOT_SENT) { - sendHandshake(player); - } - if (!isRollingMap.containsKey(player.connection)) { - return; - } - if (!isRollingMap.getBoolean(player.connection)) { - rollMap.put(player.connection, 0.0F); - } + lastIsRollingMap.put(connection, isRolling); + lastRollMap.put(connection, roll); } @Override @@ -190,7 +180,7 @@ public class DoABarrelRollProtocol implements Protocol { if (configUpdated) { configUpdated = false; for (ServerPlayer player : server.getPlayerList().players) { - sendHandshake(player); + sendHandshake(player.connection); } } } @@ -199,9 +189,9 @@ public class DoABarrelRollProtocol implements Protocol { return config.forceInstalled() ? OptionalInt.of(config.installedTimeout()) : OptionalInt.empty(); } - private void sendHandshake(ServerPlayer player) { - player.connection.send(Protocols.createPacket(initiateConfigSync(player.connection))); - configSentToClient(player.connection); + private void sendHandshake(ServerGamePacketListenerImpl connection) { + connection.send(Protocols.createPacket(initiateConfigSync(connection))); + configSentToClient(connection); } private void configSentToClient(ServerGamePacketListenerImpl handler) { @@ -255,7 +245,7 @@ public class DoABarrelRollProtocol implements Protocol { return info.state; } - private boolean isLimited(ServerGamePacketListenerImpl net) { + private boolean isLimited(ServerGamePacketListenerImpl ignore) { return true; // return net.getPlayer().getBukkitEntity().hasPermission(DoABarrelRoll.MODID + ".configure"); } @@ -266,19 +256,19 @@ public class DoABarrelRollProtocol implements Protocol { private ConfigSyncS2CPacket initiateConfigSync(ServerGamePacketListenerImpl handler) { var isLimited = isLimited(handler); - getHandshakeState(handler).isLimited = isLimited; + // getHandshakeState(handler).isLimited = isLimited; return new ConfigSyncS2CPacket(PROTOCOL_VERSION, config, isLimited, isLimited ? DEFAULT : config); } private static class ClientInfo { private HandshakeState state; private int protocolVersion; - private boolean isLimited; + // private boolean isLimited; - private ClientInfo(HandshakeState state, int protocolVersion, boolean isLimited) { + private ClientInfo(HandshakeState state, int protocolVersion, boolean ignore) { this.state = state; this.protocolVersion = protocolVersion; - this.isLimited = isLimited; + // this.isLimited = isLimited; } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java index 61c5c529..f1b2bca7 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java @@ -3,7 +3,6 @@ package org.dreeam.leaf.protocol; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; -import org.jetbrains.annotations.NotNull; import java.util.List; @@ -19,9 +18,7 @@ interface Protocol { void tickPlayer(ServerPlayer player); - void tickTracker(ServerPlayer player); - void disconnected(ServerPlayer conn); - void handle(ServerPlayer player, @NotNull LeafCustomPayload payload); + void handle(ServerPlayer player, LeafCustomPayload payload); } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java index 000b6f93..89701f15 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java @@ -61,12 +61,6 @@ public class Protocols { } } - public static void tickTracker(ServerPlayer player) { - for (Protocol protocol : PROTOCOLS) { - protocol.tickTracker(player); - } - } - public static void disconnected(ServerPlayer conn) { for (Protocol protocol : PROTOCOLS) { protocol.disconnected(conn); From 74bc79d40852c11fbf340e4d99692b5599c371f1 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 4 Jun 2025 23:53:53 +0800 Subject: [PATCH 16/57] Cleanup sentry --- leaf-api/build.gradle.kts.patch | 2 +- .../sentry/PufferfishSentryAppender.java | 29 +++---------------- .../pufferfish/sentry/SentryManager.java | 4 +-- 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/leaf-api/build.gradle.kts.patch b/leaf-api/build.gradle.kts.patch index 4cc2450c..3e77c99e 100644 --- a/leaf-api/build.gradle.kts.patch +++ b/leaf-api/build.gradle.kts.patch @@ -37,7 +37,7 @@ api("org.apache.logging.log4j:log4j-api:$log4jVersion") api("org.slf4j:slf4j-api:$slf4jVersion") api("com.mojang:brigadier:1.3.10") -+ api("io.sentry:sentry:8.4.0") // Pufferfish ++ api("io.sentry:sentry:8.13.2") // Pufferfish // Deprecate bungeecord-chat in favor of adventure api("net.md-5:bungeecord-chat:$bungeeCordChatVersion-deprecated+build.19") { diff --git a/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java b/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java index 74e7cc82..be560786 100644 --- a/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java +++ b/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java @@ -13,21 +13,21 @@ import java.util.Map; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.filter.AbstractFilter; import org.dreeam.leaf.config.modules.misc.SentryDSN; public class PufferfishSentryAppender extends AbstractAppender { - private static final org.apache.logging.log4j.Logger LOGGER = LogManager.getLogger(PufferfishSentryAppender.class.getSimpleName()); + private static final Logger LOGGER = LogManager.getLogger(PufferfishSentryAppender.class.getSimpleName()); private static final Gson GSON = new Gson(); private final Level logLevel; public PufferfishSentryAppender(Level logLevel) { - super("PufferfishSentryAdapter", new SentryFilter(), null); + super("PufferfishSentryAdapter", new SentryFilter(), null, true, Property.EMPTY_ARRAY); this.logLevel = logLevel; } @@ -108,26 +108,5 @@ public class PufferfishSentryAppender extends AbstractAppender { } private static class SentryFilter extends AbstractFilter { - - @Override - public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, String msg, - Object... params) { - return this.filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, Object msg, Throwable t) { - return this.filter(logger.getName()); - } - - @Override - public Result filter(LogEvent event) { - return this.filter(event == null ? null : event.getLoggerName()); - } - - private Result filter(String loggerName) { - return loggerName != null && loggerName.startsWith("gg.castaway.pufferfish.sentry") ? Result.DENY - : Result.NEUTRAL; - } } } diff --git a/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java b/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java index 47558c45..75e26222 100644 --- a/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java +++ b/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java @@ -4,13 +4,13 @@ import io.sentry.Sentry; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dreeam.leaf.config.modules.misc.SentryDSN; public class SentryManager { private static final Logger LOGGER = LogManager.getLogger(SentryManager.class); private SentryManager() { - } private static boolean initialized = false; @@ -27,7 +27,7 @@ public class SentryManager { initialized = true; Sentry.init(options -> { - options.setDsn(org.dreeam.leaf.config.modules.misc.SentryDSN.sentryDsn); + options.setDsn(SentryDSN.sentryDsn); options.setMaxBreadcrumbs(100); }); From 0884f05e40018752d1e59ac9a2bc2a0f2e210e7c Mon Sep 17 00:00:00 2001 From: Pascalpex <68245106+Pascalpex@users.noreply.github.com> Date: Wed, 4 Jun 2025 18:57:52 +0200 Subject: [PATCH 17/57] Add BlockExplosionHitEvent (#349) * Add BlockExplosionHitEvent * Remove import --- .../leaf/event/BlockExplosionHitEvent.java | 63 +++++++++++++++++++ .../0189-Add-BlockExplosionHitEvent.patch | 27 ++++++++ 2 files changed, 90 insertions(+) create mode 100644 leaf-api/src/main/java/org/dreeam/leaf/event/BlockExplosionHitEvent.java create mode 100644 leaf-server/minecraft-patches/features/0189-Add-BlockExplosionHitEvent.patch diff --git a/leaf-api/src/main/java/org/dreeam/leaf/event/BlockExplosionHitEvent.java b/leaf-api/src/main/java/org/dreeam/leaf/event/BlockExplosionHitEvent.java new file mode 100644 index 00000000..510a54ac --- /dev/null +++ b/leaf-api/src/main/java/org/dreeam/leaf/event/BlockExplosionHitEvent.java @@ -0,0 +1,63 @@ +package org.dreeam.leaf.event; + +import org.bukkit.ExplosionResult; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.block.BlockEvent; +import org.jetbrains.annotations.NotNull; + +/** + * Called when a block executes its explosion hit actions. + * If the event is cancelled, the block will not execute the explosion hit actions. + */ +public class BlockExplosionHitEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Entity source; + private final ExplosionResult result; + + public BlockExplosionHitEvent(@NotNull Block block, Entity source, ExplosionResult result) { + super(block); + this.source = source; + this.result = result; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + /** + * Returns the entity responsible for the explosion. + * + * @return Entity responsible for the explosion + */ + public Entity getSource() { + return source; + } + + /** + * Returns the result of the explosion. + * + * @return the result of the explosion + */ + public ExplosionResult getResult() { + return result; + } +} diff --git a/leaf-server/minecraft-patches/features/0189-Add-BlockExplosionHitEvent.patch b/leaf-server/minecraft-patches/features/0189-Add-BlockExplosionHitEvent.patch new file mode 100644 index 00000000..92843555 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0189-Add-BlockExplosionHitEvent.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Pascalpex +Date: Wed, 4 Jun 2025 17:03:32 +0200 +Subject: [PATCH] Add BlockExplosionHitEvent + + +diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java +index 6030c4eefd77969a1a9251de76d4291dcb0a2092..ea9c641fe9a9685307b6de2999ea4ff5342269b7 100644 +--- a/net/minecraft/world/level/ServerExplosion.java ++++ b/net/minecraft/world/level/ServerExplosion.java +@@ -623,9 +623,13 @@ public class ServerExplosion implements Explosion { + } + // CraftBukkit end + +- this.level +- .getBlockState(blockPos) +- .onExplosionHit(this.level, blockPos, this, (itemStack, blockPos1) -> addOrAppendStack(list, itemStack, blockPos1)); ++ // Leaf start - BlockExplosionHitEvent ++ if(new org.dreeam.leaf.event.BlockExplosionHitEvent(CraftLocation.toBukkit(blockPos, bworld).getBlock(), this.source == null ? null : this.source.getBukkitEntity(), org.bukkit.craftbukkit.CraftExplosionResult.toBukkit(this.blockInteraction)).callEvent()) { ++ this.level ++ .getBlockState(blockPos) ++ .onExplosionHit(this.level, blockPos, this, (itemStack, blockPos1) -> addOrAppendStack(list, itemStack, blockPos1)); ++ } ++ // Leaf end + } + + for (ServerExplosion.StackCollector stackCollector : list) { From 6d4f04847424fd5f84ee0a213b682e125e1d81b7 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 5 Jun 2025 01:17:39 +0800 Subject: [PATCH 18/57] Remove old config `runAsyncTasksSync` --- ...-SparklyPaper-Parallel-world-ticking.patch | 61 ------------------- .../SparklyPaperParallelWorldTicking.java | 11 ++-- 2 files changed, 5 insertions(+), 67 deletions(-) diff --git a/leaf-server/paper-patches/features/0030-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/paper-patches/features/0030-SparklyPaper-Parallel-world-ticking.patch index 32ce99bb..19025c6e 100644 --- a/leaf-server/paper-patches/features/0030-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/paper-patches/features/0030-SparklyPaper-Parallel-world-ticking.patch @@ -237,23 +237,6 @@ index a4aa2615823d77920ff55b8aa0bcc27a54b8c3e1..2fb65ce228da94eb7d9364ee0f945823 } + // SparklyPaper end - parallel world ticking } -diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -index 548fcd9646dee0c40b6ba9b3dafb9ca157dfe324..d7af94890bfccd6ff665d920cecfa1e5be626aa4 100644 ---- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -@@ -40,6 +40,12 @@ class PaperEventManager { - if (listeners.length == 0) return; - // Leaf end - Skip event if no listeners - if (event.isAsynchronous() && this.server.isPrimaryThread()) { -+ // Leaf start - Parallel world ticking -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) { -+ org.dreeam.leaf.async.world.PWTEventScheduler.getScheduler().scheduleTask(event::callEvent); -+ return; -+ } -+ // Leaf end - Parallel world ticking - throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); - } else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) { - // Leaf start - Multithreaded tracker diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index af33cab59932f4ec135caf94dc5828930833daf6..caa92e48d031cb54950e6613a82f407d7ed2455a 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -1599,47 +1582,3 @@ index c2552c3706831f7012b5b449fa43c7d5990056a4..4e8a1d01a6c0afef92ae56cc4909af06 if (!event.callEvent()) { return itemStack; } -diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java -index e4e2e42d0ca25df7fe9f2dd4275610e45fcb2c84..e7c6b2ab5f2c68f3319ccd52785c8d3488a2eef7 100644 ---- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java -+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java -@@ -19,11 +19,39 @@ class CraftAsyncTask extends CraftTask { - - @Override - public boolean isSync() { -+ // Leaf start - Parallel world ticking -+ // Return true if we should run this task synchronously when parallel world ticking is enabled and runAsyncTasksSync is true -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && -+ org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) { -+ return true; -+ } -+ // Leaf end - Parallel world ticking - return false; - } - - @Override - public void run() { -+ // Leaf start - Parallel world ticking -+ // If parallel world ticking is enabled and we're configured to run async tasks sync, -+ // execute the task as if it were a sync task (directly on the main thread) -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && -+ org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) { -+ try { -+ super.run(); -+ } catch (final Throwable t) { -+ this.getOwner().getLogger().log( -+ Level.WARNING, -+ String.format( -+ "Plugin %s generated an exception while executing task %s (forced sync mode)", -+ this.getOwner().getDescription().getFullName(), -+ this.getTaskId()), -+ t); -+ } -+ return; -+ } -+ // Leaf end - Parallel world ticking -+ -+ // Original async implementation - final Thread thread = Thread.currentThread(); - // Paper start - name threads according to running plugin - final String nameBefore = thread.getName(); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/SparklyPaperParallelWorldTicking.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/SparklyPaperParallelWorldTicking.java index 304e234f..1d89ae82 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/SparklyPaperParallelWorldTicking.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/SparklyPaperParallelWorldTicking.java @@ -17,7 +17,7 @@ public class SparklyPaperParallelWorldTicking extends ConfigModules { public static boolean logContainerCreationStacktraces = false; public static boolean disableHardThrow = false; @Deprecated - public static boolean runAsyncTasksSync = false; + public static Boolean runAsyncTasksSync; // STRICT, BUFFERED, DISABLED public static String asyncUnsafeReadHandling = "BUFFERED"; @@ -52,15 +52,14 @@ public class SparklyPaperParallelWorldTicking extends ConfigModules { asyncUnsafeReadHandling = "DISABLED"; } - runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync", false); // Default to false now - if (runAsyncTasksSync) { - LeafConfig.LOGGER.warn("The setting '{}.run-async-tasks-sync' is deprecated. Use 'async-unsafe-read-handling: STRICT' for similar safety checks or 'BUFFERED' for buffered reads.", getBasePath()); + // Transfer old config + runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync"); + if (runAsyncTasksSync != null && runAsyncTasksSync) { + LeafConfig.LOGGER.warn("The setting '{}.run-async-tasks-sync' is deprecated, removed automatically. Use 'async-unsafe-read-handling: BUFFERED' for buffered reads instead.", getBasePath()); } if (enabled) { LeafConfig.LOGGER.info("Using {} threads for Parallel World Ticking", threads); } - - runAsyncTasksSync = enabled && runAsyncTasksSync; // Auto-disable if main feature is off } } From 01bf48b350afae70af0e244a5f67c2057143544f Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 5 Jun 2025 02:29:54 +0800 Subject: [PATCH 19/57] Valid session if current session is invalid --- .../0048-Cache-player-profileResult.patch | 51 ++++++++++++++++--- ...0054-Configurable-connection-message.patch | 6 +-- ...t-place-player-if-the-server-is-full.patch | 4 +- .../0100-Smooth-teleport-config.patch | 4 +- ...-SparklyPaper-Parallel-world-ticking.patch | 6 +-- .../0165-Async-switch-connection-state.patch | 4 +- 6 files changed, 57 insertions(+), 18 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch b/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch index 9c7603ff..aa59e724 100644 --- a/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch +++ b/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Cache player profileResult diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 069477e524a28b20a0289221858bdc802704a890..49f1743db193be1f10bfe6419231eb682e1068f7 100644 +index 069477e524a28b20a0289221858bdc802704a890..c25f5b92b16f45b960d9b405b2fe1c40ec4e1124 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -71,6 +71,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -71,6 +71,12 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, private net.minecraft.server.level.ServerPlayer player; // CraftBukkit public boolean iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation = false; // Paper - username validation overriding private int velocityLoginMessageId = -1; // Paper - Add Velocity IP Forwarding Support @@ -16,11 +16,12 @@ index 069477e524a28b20a0289221858bdc802704a890..49f1743db193be1f10bfe6419231eb68 + private static final com.github.benmanes.caffeine.cache.Cache playerProfileResultCache = com.github.benmanes.caffeine.cache.Caffeine.newBuilder() + .expireAfterWrite(org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResultTimeout, java.util.concurrent.TimeUnit.MINUTES) + .build(); ++ public static final java.util.Map playerSession = new java.util.concurrent.ConcurrentHashMap<>(); + // Leaf end - Cache player profileResult public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) { this.server = server; -@@ -304,9 +309,25 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -304,9 +310,30 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, String string1 = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized"); try { @@ -29,15 +30,20 @@ index 069477e524a28b20a0289221858bdc802704a890..49f1743db193be1f10bfe6419231eb68 - .hasJoinedServer(string1, string, this.getAddress()); + // Leaf start - Cache player profileResult + ProfileResult profileResult; -+ if (false) { // TODO ++ if (org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResult) { + profileResult = playerProfileResultCache.getIfPresent(string1); + -+ if (profileResult == null) { ++ InetAddress address = this.getAddress(); ++ InetAddress lastAddress = playerSession.get(string1); ++ if (isInvalidSession(address, lastAddress)) { ++ // Send request to mojang server to verify session ++ // Result will be null if is invalid and will do disconnect logic below + profileResult = ServerLoginPacketListenerImpl.this.server + .getSessionService() -+ .hasJoinedServer(string1, string, this.getAddress()); ++ .hasJoinedServer(string1, string, address); + if (profileResult != null) { + playerProfileResultCache.put(string1, profileResult); ++ playerSession.put(string1, address); + } + } + } else { @@ -49,3 +55,36 @@ index 069477e524a28b20a0289221858bdc802704a890..49f1743db193be1f10bfe6419231eb68 if (profileResult != null) { GameProfile gameProfile = profileResult.profile(); // CraftBukkit start - fire PlayerPreLoginEvent +@@ -351,6 +378,20 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, + // Paper end - Cache authenticator threads + } + ++ // Leaf start - Cache player profileResult ++ private static boolean isInvalidSession(@org.jetbrains.annotations.Nullable InetAddress currAddress, @org.jetbrains.annotations.Nullable InetAddress lastAddress) { ++ // Invalid address or non-public IP address ++ if (currAddress == null || ++ currAddress.isAnyLocalAddress() || ++ currAddress.isLinkLocalAddress() || ++ currAddress.isLoopbackAddress() || ++ currAddress.isSiteLocalAddress()) { ++ return true; ++ } ++ return !currAddress.equals(lastAddress); ++ } ++ // Leaf end - Cache player profileResult ++ + // CraftBukkit start + private GameProfile callPlayerPreLoginEvents(GameProfile gameprofile) throws Exception { // Paper - Add more fields to AsyncPlayerPreLoginEvent + // Paper start - Add Velocity IP Forwarding Support +diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java +index e8683f45823cac55e3e68ccc500f10f0632e72fd..f71debc88c1d16c09546c026817aa192faac8fc6 100644 +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -669,6 +669,7 @@ public abstract class PlayerList { + // Paper end - Fix kick event leave message not being sent + org.purpurmc.purpur.task.BossBarTask.removeFromAll(player.getBukkitEntity()); // Purpur - Implement TPSBar + net.minecraft.server.network.ServerGamePacketListenerImpl.afkCooldown.remove(player.getBukkitEntity().getUniqueId()); // Leaf - Improve Purpur AFK system ++ net.minecraft.server.network.ServerLoginPacketListenerImpl.playerSession.remove(player.getBukkitEntity().getName()); // Leaf - Cache player profileResult + ServerLevel serverLevel = player.serverLevel(); + player.awardStat(Stats.LEAVE_GAME); + // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it diff --git a/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch b/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch index 529a69d4..e16f7d4a 100644 --- a/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch +++ b/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable connection message diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index e8683f45823cac55e3e68ccc500f10f0632e72fd..5c88d51920481235145bb7fd8cf148607b2dbed0 100644 +index f71debc88c1d16c09546c026817aa192faac8fc6..4fa7952c30408e898cc2e3ce41d5b0f44f19ae31 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -434,7 +434,7 @@ public abstract class PlayerList { @@ -26,7 +26,7 @@ index e8683f45823cac55e3e68ccc500f10f0632e72fd..5c88d51920481235145bb7fd8cf14860 joinMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(jm); // Paper - Adventure this.server.getPlayerList().broadcastSystemMessage(joinMessage, false); // Paper - Adventure } -@@ -677,7 +677,7 @@ public abstract class PlayerList { +@@ -678,7 +678,7 @@ public abstract class PlayerList { player.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DISCONNECT); // Paper - Inventory close reason } @@ -35,7 +35,7 @@ index e8683f45823cac55e3e68ccc500f10f0632e72fd..5c88d51920481235145bb7fd8cf14860 this.cserver.getPluginManager().callEvent(playerQuitEvent); player.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); -@@ -1673,4 +1673,38 @@ public abstract class PlayerList { +@@ -1674,4 +1674,38 @@ public abstract class PlayerList { public boolean isAllowCommandsForAllPlayers() { return this.allowCommandsForAllPlayers; } diff --git a/leaf-server/minecraft-patches/features/0073-Do-not-place-player-if-the-server-is-full.patch b/leaf-server/minecraft-patches/features/0073-Do-not-place-player-if-the-server-is-full.patch index 698b73a2..ea9b96af 100644 --- a/leaf-server/minecraft-patches/features/0073-Do-not-place-player-if-the-server-is-full.patch +++ b/leaf-server/minecraft-patches/features/0073-Do-not-place-player-if-the-server-is-full.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Do not place player if the server is full Fix https://github.com/PaperMC/Paper/issues/10668 diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index c247b1dd9504b10ea73ec3bd96d2bf9e48fabf3e..119ffa8f10bdcc27ff4b7dc4e1ef18212607c4bf 100644 +index 4fa7952c30408e898cc2e3ce41d5b0f44f19ae31..6be59ddf2584455de0955b34d8ba1a053103f684 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -341,6 +341,13 @@ public abstract class PlayerList { @@ -23,7 +23,7 @@ index c247b1dd9504b10ea73ec3bd96d2bf9e48fabf3e..119ffa8f10bdcc27ff4b7dc4e1ef1821 org.bukkit.Location loc = ev.getSpawnLocation(); serverLevel = ((org.bukkit.craftbukkit.CraftWorld) loc.getWorld()).getHandle(); -@@ -835,7 +842,7 @@ public abstract class PlayerList { +@@ -836,7 +843,7 @@ public abstract class PlayerList { // return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile) // ? Component.translatable("multiplayer.disconnect.server_full") // : null; diff --git a/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch b/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch index aef1c4e6..7e86b89e 100644 --- a/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch +++ b/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch @@ -30,10 +30,10 @@ index 4f01b53bf801f99253efd27df6216912705d18af..82a1715fea41e6a41c4ff441ea89f424 level.addDuringTeleport(this); this.triggerDimensionChangeTriggers(serverLevel); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 75fb49f1596f475278d12c8c7aea9ad4952b6056..b17c8a2f5294ac28cc05fb05c84a041b2c6c8721 100644 +index 6be59ddf2584455de0955b34d8ba1a053103f684..66cb015de63949ca5add9dc3456db9c638b78048 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java -@@ -955,11 +955,11 @@ public abstract class PlayerList { +@@ -956,11 +956,11 @@ public abstract class PlayerList { byte b = (byte)(keepInventory ? 1 : 0); ServerLevel serverLevel = serverPlayer.serverLevel(); LevelData levelData = serverLevel.getLevelData(); diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch index 2b087735..5e1160e4 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch @@ -867,7 +867,7 @@ index 8362def0dc61496a087bd859052bd80ebba83185..09f517059aa47ca67329bc913243d4fd // Paper end - Inventory close reason this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..3591de34443069f3f163f8d17df6372c3068611d 100644 +index 66cb015de63949ca5add9dc3456db9c638b78048..1009421bfd289734b50a528bfca8d957e4610159 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -252,6 +252,8 @@ public abstract class PlayerList { @@ -879,7 +879,7 @@ index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..3591de34443069f3f163f8d17df6372c player.isRealPlayer = true; // Paper player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed GameProfile gameProfile = player.getGameProfile(); -@@ -891,6 +893,15 @@ public abstract class PlayerList { +@@ -892,6 +894,15 @@ public abstract class PlayerList { return this.respawn(player, keepInventory, reason, eventReason, null); } public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, org.bukkit.Location location) { @@ -895,7 +895,7 @@ index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..3591de34443069f3f163f8d17df6372c player.stopRiding(); // CraftBukkit this.players.remove(player); this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot -@@ -902,6 +913,7 @@ public abstract class PlayerList { +@@ -903,6 +914,7 @@ public abstract class PlayerList { ServerPlayer serverPlayer = player; Level fromWorld = player.level(); player.wonGame = false; diff --git a/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch index d02cdc9f..80676c50 100644 --- a/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch @@ -118,10 +118,10 @@ index 2e9eb04c7c4342393c05339906c267bca9ff29b1..53b9daa909c2b89046d5af515e17afe0 try { PlayerList playerList = this.server.getPlayerList(); diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 49f1743db193be1f10bfe6419231eb682e1068f7..8cbfe4abca6a3962376f723b94de86b4cab9d9e2 100644 +index c25f5b92b16f45b960d9b405b2fe1c40ec4e1124..005fee891cdabc7105033be051389557631fb6d2 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -472,11 +472,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -492,11 +492,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); } From e788a93e175c7f0c50f2805884260c74f978e531 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 5 Jun 2025 02:48:13 +0800 Subject: [PATCH 20/57] Fix --- .../0048-Cache-player-profileResult.patch | 28 ++++++------------- ...0054-Configurable-connection-message.patch | 6 ++-- ...t-place-player-if-the-server-is-full.patch | 4 +-- .../0100-Smooth-teleport-config.patch | 4 +-- ...-SparklyPaper-Parallel-world-ticking.patch | 6 ++-- .../0165-Async-switch-connection-state.patch | 4 +-- 6 files changed, 21 insertions(+), 31 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch b/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch index aa59e724..36ccec15 100644 --- a/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch +++ b/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Cache player profileResult diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 069477e524a28b20a0289221858bdc802704a890..c25f5b92b16f45b960d9b405b2fe1c40ec4e1124 100644 +index 069477e524a28b20a0289221858bdc802704a890..114b25f933c6a1b011523581a5a02a5a2c1e827e 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -71,6 +71,12 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -71,6 +71,14 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, private net.minecraft.server.level.ServerPlayer player; // CraftBukkit public boolean iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation = false; // Paper - username validation overriding private int velocityLoginMessageId = -1; // Paper - Add Velocity IP Forwarding Support @@ -16,12 +16,14 @@ index 069477e524a28b20a0289221858bdc802704a890..c25f5b92b16f45b960d9b405b2fe1c40 + private static final com.github.benmanes.caffeine.cache.Cache playerProfileResultCache = com.github.benmanes.caffeine.cache.Caffeine.newBuilder() + .expireAfterWrite(org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResultTimeout, java.util.concurrent.TimeUnit.MINUTES) + .build(); -+ public static final java.util.Map playerSession = new java.util.concurrent.ConcurrentHashMap<>(); ++ private static final com.github.benmanes.caffeine.cache.Cache playerSession = com.github.benmanes.caffeine.cache.Caffeine.newBuilder() ++ .expireAfterWrite(org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResultTimeout, java.util.concurrent.TimeUnit.MINUTES) ++ .build(); + // Leaf end - Cache player profileResult public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) { this.server = server; -@@ -304,9 +310,30 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -304,9 +312,30 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, String string1 = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized"); try { @@ -34,14 +36,14 @@ index 069477e524a28b20a0289221858bdc802704a890..c25f5b92b16f45b960d9b405b2fe1c40 + profileResult = playerProfileResultCache.getIfPresent(string1); + + InetAddress address = this.getAddress(); -+ InetAddress lastAddress = playerSession.get(string1); ++ InetAddress lastAddress = playerSession.getIfPresent(string1); + if (isInvalidSession(address, lastAddress)) { + // Send request to mojang server to verify session + // Result will be null if is invalid and will do disconnect logic below + profileResult = ServerLoginPacketListenerImpl.this.server + .getSessionService() + .hasJoinedServer(string1, string, address); -+ if (profileResult != null) { ++ if (profileResult != null && address != null) { + playerProfileResultCache.put(string1, profileResult); + playerSession.put(string1, address); + } @@ -55,7 +57,7 @@ index 069477e524a28b20a0289221858bdc802704a890..c25f5b92b16f45b960d9b405b2fe1c40 if (profileResult != null) { GameProfile gameProfile = profileResult.profile(); // CraftBukkit start - fire PlayerPreLoginEvent -@@ -351,6 +378,20 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -351,6 +380,20 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, // Paper end - Cache authenticator threads } @@ -76,15 +78,3 @@ index 069477e524a28b20a0289221858bdc802704a890..c25f5b92b16f45b960d9b405b2fe1c40 // CraftBukkit start private GameProfile callPlayerPreLoginEvents(GameProfile gameprofile) throws Exception { // Paper - Add more fields to AsyncPlayerPreLoginEvent // Paper start - Add Velocity IP Forwarding Support -diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index e8683f45823cac55e3e68ccc500f10f0632e72fd..f71debc88c1d16c09546c026817aa192faac8fc6 100644 ---- a/net/minecraft/server/players/PlayerList.java -+++ b/net/minecraft/server/players/PlayerList.java -@@ -669,6 +669,7 @@ public abstract class PlayerList { - // Paper end - Fix kick event leave message not being sent - org.purpurmc.purpur.task.BossBarTask.removeFromAll(player.getBukkitEntity()); // Purpur - Implement TPSBar - net.minecraft.server.network.ServerGamePacketListenerImpl.afkCooldown.remove(player.getBukkitEntity().getUniqueId()); // Leaf - Improve Purpur AFK system -+ net.minecraft.server.network.ServerLoginPacketListenerImpl.playerSession.remove(player.getBukkitEntity().getName()); // Leaf - Cache player profileResult - ServerLevel serverLevel = player.serverLevel(); - player.awardStat(Stats.LEAVE_GAME); - // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it diff --git a/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch b/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch index e16f7d4a..529a69d4 100644 --- a/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch +++ b/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable connection message diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index f71debc88c1d16c09546c026817aa192faac8fc6..4fa7952c30408e898cc2e3ce41d5b0f44f19ae31 100644 +index e8683f45823cac55e3e68ccc500f10f0632e72fd..5c88d51920481235145bb7fd8cf148607b2dbed0 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -434,7 +434,7 @@ public abstract class PlayerList { @@ -26,7 +26,7 @@ index f71debc88c1d16c09546c026817aa192faac8fc6..4fa7952c30408e898cc2e3ce41d5b0f4 joinMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(jm); // Paper - Adventure this.server.getPlayerList().broadcastSystemMessage(joinMessage, false); // Paper - Adventure } -@@ -678,7 +678,7 @@ public abstract class PlayerList { +@@ -677,7 +677,7 @@ public abstract class PlayerList { player.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DISCONNECT); // Paper - Inventory close reason } @@ -35,7 +35,7 @@ index f71debc88c1d16c09546c026817aa192faac8fc6..4fa7952c30408e898cc2e3ce41d5b0f4 this.cserver.getPluginManager().callEvent(playerQuitEvent); player.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); -@@ -1674,4 +1674,38 @@ public abstract class PlayerList { +@@ -1673,4 +1673,38 @@ public abstract class PlayerList { public boolean isAllowCommandsForAllPlayers() { return this.allowCommandsForAllPlayers; } diff --git a/leaf-server/minecraft-patches/features/0073-Do-not-place-player-if-the-server-is-full.patch b/leaf-server/minecraft-patches/features/0073-Do-not-place-player-if-the-server-is-full.patch index ea9b96af..1edfcc4f 100644 --- a/leaf-server/minecraft-patches/features/0073-Do-not-place-player-if-the-server-is-full.patch +++ b/leaf-server/minecraft-patches/features/0073-Do-not-place-player-if-the-server-is-full.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Do not place player if the server is full Fix https://github.com/PaperMC/Paper/issues/10668 diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 4fa7952c30408e898cc2e3ce41d5b0f44f19ae31..6be59ddf2584455de0955b34d8ba1a053103f684 100644 +index 5c88d51920481235145bb7fd8cf148607b2dbed0..75fb49f1596f475278d12c8c7aea9ad4952b6056 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -341,6 +341,13 @@ public abstract class PlayerList { @@ -23,7 +23,7 @@ index 4fa7952c30408e898cc2e3ce41d5b0f44f19ae31..6be59ddf2584455de0955b34d8ba1a05 org.bukkit.Location loc = ev.getSpawnLocation(); serverLevel = ((org.bukkit.craftbukkit.CraftWorld) loc.getWorld()).getHandle(); -@@ -836,7 +843,7 @@ public abstract class PlayerList { +@@ -835,7 +842,7 @@ public abstract class PlayerList { // return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile) // ? Component.translatable("multiplayer.disconnect.server_full") // : null; diff --git a/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch b/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch index 7e86b89e..aef1c4e6 100644 --- a/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch +++ b/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch @@ -30,10 +30,10 @@ index 4f01b53bf801f99253efd27df6216912705d18af..82a1715fea41e6a41c4ff441ea89f424 level.addDuringTeleport(this); this.triggerDimensionChangeTriggers(serverLevel); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 6be59ddf2584455de0955b34d8ba1a053103f684..66cb015de63949ca5add9dc3456db9c638b78048 100644 +index 75fb49f1596f475278d12c8c7aea9ad4952b6056..b17c8a2f5294ac28cc05fb05c84a041b2c6c8721 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java -@@ -956,11 +956,11 @@ public abstract class PlayerList { +@@ -955,11 +955,11 @@ public abstract class PlayerList { byte b = (byte)(keepInventory ? 1 : 0); ServerLevel serverLevel = serverPlayer.serverLevel(); LevelData levelData = serverLevel.getLevelData(); diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch index 5e1160e4..2b087735 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch @@ -867,7 +867,7 @@ index 8362def0dc61496a087bd859052bd80ebba83185..09f517059aa47ca67329bc913243d4fd // Paper end - Inventory close reason this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 66cb015de63949ca5add9dc3456db9c638b78048..1009421bfd289734b50a528bfca8d957e4610159 100644 +index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..3591de34443069f3f163f8d17df6372c3068611d 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -252,6 +252,8 @@ public abstract class PlayerList { @@ -879,7 +879,7 @@ index 66cb015de63949ca5add9dc3456db9c638b78048..1009421bfd289734b50a528bfca8d957 player.isRealPlayer = true; // Paper player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed GameProfile gameProfile = player.getGameProfile(); -@@ -892,6 +894,15 @@ public abstract class PlayerList { +@@ -891,6 +893,15 @@ public abstract class PlayerList { return this.respawn(player, keepInventory, reason, eventReason, null); } public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, org.bukkit.Location location) { @@ -895,7 +895,7 @@ index 66cb015de63949ca5add9dc3456db9c638b78048..1009421bfd289734b50a528bfca8d957 player.stopRiding(); // CraftBukkit this.players.remove(player); this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot -@@ -903,6 +914,7 @@ public abstract class PlayerList { +@@ -902,6 +913,7 @@ public abstract class PlayerList { ServerPlayer serverPlayer = player; Level fromWorld = player.level(); player.wonGame = false; diff --git a/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch index 80676c50..7b06eff0 100644 --- a/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch @@ -118,10 +118,10 @@ index 2e9eb04c7c4342393c05339906c267bca9ff29b1..53b9daa909c2b89046d5af515e17afe0 try { PlayerList playerList = this.server.getPlayerList(); diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index c25f5b92b16f45b960d9b405b2fe1c40ec4e1124..005fee891cdabc7105033be051389557631fb6d2 100644 +index 114b25f933c6a1b011523581a5a02a5a2c1e827e..5907f1c75002be5e2ef1f9875974e665f964db7a 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -492,11 +492,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -494,11 +494,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); } From 1d30370d7a5c507393d9cb90f870e3b659cbfe19 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Fri, 6 Jun 2025 01:28:17 +0800 Subject: [PATCH 21/57] [ci skip] Cleanup --- ...-SparklyPaper-Parallel-world-ticking.patch | 79 ++++--------------- .../features}/0002-Leaf-Bootstrap.patch | 2 +- ...ommands.patch => 0003-Leaf-Commands.patch} | 0 ...04-Pufferfish-Optimize-mob-spawning.patch} | 0 ...=> 0005-Purpur-Server-Paper-Changes.patch} | 2 +- ...6-Fix-Pufferfish-and-Purpur-patches.patch} | 0 ...imings.patch => 0007-Remove-Timings.patch} | 0 ...patch => 0008-KeYi-Player-Skull-API.patch} | 0 ...atch => 0009-Slice-Smooth-Teleports.patch} | 0 ....patch => 0010-Leaves-Protocol-Core.patch} | 0 ...patch => 0011-Leaves-Replay-Mod-API.patch} | 0 ... => 0012-Skip-event-if-no-listeners.patch} | 0 ...-EntityScheduler-s-executeTick-chec.patch} | 0 ...SparklyPaper-Optimize-canSee-checks.patch} | 0 ...atch => 0015-Including-5s-in-getTPS.patch} | 0 ...eption-on-missing-ResourceKey-value.patch} | 0 ...-Virtual-Thread-for-async-scheduler.patch} | 0 ...onfigurable-chat-message-signatures.patch} | 0 ...ed.patch => 0019-Matter-Secure-Seed.patch} | 0 ...tch => 0020-Faster-random-generator.patch} | 0 ...onfigurable-unknown-command-message.patch} | 0 ...world-map-with-optimized-collection.patch} | 0 ...ntityType-minecraftToBukkit-convert.patch} | 0 ...patch => 0024-Multithreaded-Tracker.patch} | 0 ....patch => 0025-Asynchronous-locator.patch} | 0 ...-snapshots-for-acquiring-blockstate.patch} | 0 ...CraftServer-getworlds-list-creation.patch} | 0 ...k-key.patch => 0028-Cache-chunk-key.patch} | 0 ... => 0029-Async-structure-locate-api.patch} | 0 ...> 0030-PlayerInventoryOverflowEvent.patch} | 0 ...SparklyPaper-Parallel-world-ticking.patch} | 22 +++--- ...r-PR-Throttle-failed-spawn-attempts.patch} | 0 ...tch => 0033-Async-playerdata-saving.patch} | 0 ...rPR-Fix-save-load-NaN-Entity-Motion.patch} | 0 ...erPR-Fix-unnecessary-map-data-saves.patch} | 0 ...send.patch => 0036-Async-chunk-send.patch} | 0 ...037-Optimise-player-movement-checks.patch} | 0 ...tion.patch => 0038-Paw-optimization.patch} | 0 ...atch => 0039-optimise-ReferenceList.patch} | 0 ...tBiome.patch => 0040-cache-getBiome.patch} | 0 .../java/org/dreeam/leaf/LeafBootstrap.java | 3 +- 41 files changed, 29 insertions(+), 79 deletions(-) rename {leaf-archived-patches/removed/hardfork/paperserver => leaf-server/paper-patches/features}/0002-Leaf-Bootstrap.patch (92%) rename leaf-server/paper-patches/features/{0002-Leaf-Commands.patch => 0003-Leaf-Commands.patch} (100%) rename leaf-server/paper-patches/features/{0003-Pufferfish-Optimize-mob-spawning.patch => 0004-Pufferfish-Optimize-mob-spawning.patch} (100%) rename leaf-server/paper-patches/features/{0004-Purpur-Server-Paper-Changes.patch => 0005-Purpur-Server-Paper-Changes.patch} (99%) rename leaf-server/paper-patches/features/{0005-Fix-Pufferfish-and-Purpur-patches.patch => 0006-Fix-Pufferfish-and-Purpur-patches.patch} (100%) rename leaf-server/paper-patches/features/{0006-Remove-Timings.patch => 0007-Remove-Timings.patch} (100%) rename leaf-server/paper-patches/features/{0007-KeYi-Player-Skull-API.patch => 0008-KeYi-Player-Skull-API.patch} (100%) rename leaf-server/paper-patches/features/{0008-Slice-Smooth-Teleports.patch => 0009-Slice-Smooth-Teleports.patch} (100%) rename leaf-server/paper-patches/features/{0009-Leaves-Protocol-Core.patch => 0010-Leaves-Protocol-Core.patch} (100%) rename leaf-server/paper-patches/features/{0010-Leaves-Replay-Mod-API.patch => 0011-Leaves-Replay-Mod-API.patch} (100%) rename leaf-server/paper-patches/features/{0011-Skip-event-if-no-listeners.patch => 0012-Skip-event-if-no-listeners.patch} (100%) rename leaf-server/paper-patches/features/{0012-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch => 0013-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch} (100%) rename leaf-server/paper-patches/features/{0013-SparklyPaper-Optimize-canSee-checks.patch => 0014-SparklyPaper-Optimize-canSee-checks.patch} (100%) rename leaf-server/paper-patches/features/{0014-Including-5s-in-getTPS.patch => 0015-Including-5s-in-getTPS.patch} (100%) rename leaf-server/paper-patches/features/{0015-Don-t-throw-exception-on-missing-ResourceKey-value.patch => 0016-Don-t-throw-exception-on-missing-ResourceKey-value.patch} (100%) rename leaf-server/paper-patches/features/{0016-Virtual-Thread-for-async-scheduler.patch => 0017-Virtual-Thread-for-async-scheduler.patch} (100%) rename leaf-server/paper-patches/features/{0017-Mirai-Configurable-chat-message-signatures.patch => 0018-Mirai-Configurable-chat-message-signatures.patch} (100%) rename leaf-server/paper-patches/features/{0018-Matter-Secure-Seed.patch => 0019-Matter-Secure-Seed.patch} (100%) rename leaf-server/paper-patches/features/{0019-Faster-random-generator.patch => 0020-Faster-random-generator.patch} (100%) rename leaf-server/paper-patches/features/{0020-Configurable-unknown-command-message.patch => 0021-Configurable-unknown-command-message.patch} (100%) rename leaf-server/paper-patches/features/{0021-Replace-world-map-with-optimized-collection.patch => 0022-Replace-world-map-with-optimized-collection.patch} (100%) rename leaf-server/paper-patches/features/{0022-Cache-CraftEntityType-minecraftToBukkit-convert.patch => 0023-Cache-CraftEntityType-minecraftToBukkit-convert.patch} (100%) rename leaf-server/paper-patches/features/{0023-Multithreaded-Tracker.patch => 0024-Multithreaded-Tracker.patch} (100%) rename leaf-server/paper-patches/features/{0024-Asynchronous-locator.patch => 0025-Asynchronous-locator.patch} (100%) rename leaf-server/paper-patches/features/{0025-EMC-Don-t-use-snapshots-for-acquiring-blockstate.patch => 0026-EMC-Don-t-use-snapshots-for-acquiring-blockstate.patch} (100%) rename leaf-server/paper-patches/features/{0026-Faster-CraftServer-getworlds-list-creation.patch => 0027-Faster-CraftServer-getworlds-list-creation.patch} (100%) rename leaf-server/paper-patches/features/{0027-Cache-chunk-key.patch => 0028-Cache-chunk-key.patch} (100%) rename leaf-server/paper-patches/features/{0028-Async-structure-locate-api.patch => 0029-Async-structure-locate-api.patch} (100%) rename leaf-server/paper-patches/features/{0029-PlayerInventoryOverflowEvent.patch => 0030-PlayerInventoryOverflowEvent.patch} (100%) rename leaf-server/paper-patches/features/{0030-SparklyPaper-Parallel-world-ticking.patch => 0031-SparklyPaper-Parallel-world-ticking.patch} (99%) rename leaf-server/paper-patches/features/{0031-Paper-PR-Throttle-failed-spawn-attempts.patch => 0032-Paper-PR-Throttle-failed-spawn-attempts.patch} (100%) rename leaf-server/paper-patches/features/{0032-Async-playerdata-saving.patch => 0033-Async-playerdata-saving.patch} (100%) rename leaf-server/paper-patches/features/{0033-PaperPR-Fix-save-load-NaN-Entity-Motion.patch => 0034-PaperPR-Fix-save-load-NaN-Entity-Motion.patch} (100%) rename leaf-server/paper-patches/features/{0034-PaperPR-Fix-unnecessary-map-data-saves.patch => 0035-PaperPR-Fix-unnecessary-map-data-saves.patch} (100%) rename leaf-server/paper-patches/features/{0035-Async-chunk-send.patch => 0036-Async-chunk-send.patch} (100%) rename leaf-server/paper-patches/features/{0036-Optimise-player-movement-checks.patch => 0037-Optimise-player-movement-checks.patch} (100%) rename leaf-server/paper-patches/features/{0037-Paw-optimization.patch => 0038-Paw-optimization.patch} (100%) rename leaf-server/paper-patches/features/{0038-optimise-ReferenceList.patch => 0039-optimise-ReferenceList.patch} (100%) rename leaf-server/paper-patches/features/{0039-cache-getBiome.patch => 0040-cache-getBiome.patch} (100%) diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch index 2b087735..42eeb075 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch @@ -1111,79 +1111,30 @@ index 0b4c4707139c9c72929799818ec1a1b25575d70e..acf8059017f4e45c307a113abed36c59 }); } diff --git a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -index d212f57c8c0b2086f567fd30237b110203d9e8cb..ed4df82581b5411e54068ccc59ea85a78404fa2b 100644 +index d212f57c8c0b2086f567fd30237b110203d9e8cb..3712a2143efe82d41bd62dcec826aede03b6448b 100644 --- a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java +++ b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -@@ -126,40 +126,48 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { +@@ -126,8 +126,10 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { Vec3 vec3 = this.oldPosition(); if (owner instanceof ServerPlayer serverPlayer) { if (serverPlayer.connection.isAcceptingMessages()) { -- // CraftBukkit start -- ServerPlayer serverPlayer1 = serverPlayer.teleport(new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL)); -- if (serverPlayer1 == null) { -- this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); -- return; -- } -- // CraftBukkit end -- if (this.random.nextFloat() < serverLevel.purpurConfig.enderPearlEndermiteChance && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { // Purpur - Configurable Ender Pearl RNG -- Endermite endermite = EntityType.ENDERMITE.create(serverLevel, EntitySpawnReason.TRIGGERED); -- if (endermite != null) { -- endermite.setPlayerSpawned(true); // Purpur - Add back player spawned endermite API -- endermite.moveTo(owner.getX(), owner.getY(), owner.getZ(), owner.getYRot(), owner.getXRot()); -- serverLevel.addFreshEntity(endermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL); + // Leaf start - SparklyPaper - parallel world ticking mod (handling for pearl teleportation cross-dimension) + java.util.function.Consumer teleportPlayerCrossDimensionTask = taskServerPlayer -> { -+ // CraftBukkit start -+ ServerPlayer serverPlayer1 = taskServerPlayer.teleport(new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL)); -+ if (serverPlayer1 == null) { -+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); -+ return; -+ } -+ // CraftBukkit end -+ if (this.random.nextFloat() < serverLevel.purpurConfig.enderPearlEndermiteChance && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { // Purpur - Configurable Ender Pearl RNG -+ Endermite endermite = EntityType.ENDERMITE.create(serverLevel, EntitySpawnReason.TRIGGERED); -+ if (endermite != null) { -+ endermite.setPlayerSpawned(true); // Purpur - Add back player spawned endermite API -+ endermite.moveTo(owner.getX(), owner.getY(), owner.getZ(), owner.getYRot(), owner.getXRot()); -+ serverLevel.addFreshEntity(endermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL); -+ } - } -- } - -- if (this.isOnPortalCooldown()) { -- owner.setPortalCooldown(); -- } -+ if (this.isOnPortalCooldown()) { -+ owner.setPortalCooldown(); -+ } - -- // CraftBukkit start - moved up -- // ServerPlayer serverPlayer1 = serverPlayer.teleport( -- // new TeleportTransition( -- // serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING -- // ) -- // ); -- // CraftBukkit end - moved up -- if (serverPlayer1 != null) { -- serverPlayer1.resetFallDistance(); -- serverPlayer1.resetCurrentImpulseContext(); + // CraftBukkit start +- ServerPlayer serverPlayer1 = serverPlayer.teleport(new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL)); ++ ServerPlayer serverPlayer1 = taskServerPlayer.teleport(new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL)); + if (serverPlayer1 == null) { + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); + return; +@@ -156,10 +158,16 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { + if (serverPlayer1 != null) { + serverPlayer1.resetFallDistance(); + serverPlayer1.resetCurrentImpulseContext(); - serverPlayer1.hurtServer(serverPlayer.serverLevel(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage -- } -+ // CraftBukkit start - moved up -+ // ServerPlayer serverPlayer1 = serverPlayer.teleport( -+ // new TeleportTransition( -+ // serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING -+ // ) -+ // ); -+ // CraftBukkit end - moved up -+ if (serverPlayer1 != null) { -+ serverPlayer1.resetFallDistance(); -+ serverPlayer1.resetCurrentImpulseContext(); -+ serverPlayer1.hurtServer(taskServerPlayer.serverLevel(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage -+ } ++ serverPlayer1.hurtServer(taskServerPlayer.serverLevel(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage + } -- this.playSound(serverLevel, vec3); -+ this.playSound(serverLevel, vec3); + this.playSound(serverLevel, vec3); + }; + if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) + serverPlayer.getBukkitEntity().taskScheduler.schedule(teleportPlayerCrossDimensionTask, entity -> {}, 0); diff --git a/leaf-archived-patches/removed/hardfork/paperserver/0002-Leaf-Bootstrap.patch b/leaf-server/paper-patches/features/0002-Leaf-Bootstrap.patch similarity index 92% rename from leaf-archived-patches/removed/hardfork/paperserver/0002-Leaf-Bootstrap.patch rename to leaf-server/paper-patches/features/0002-Leaf-Bootstrap.patch index 049a33d3..ec62e7ae 100644 --- a/leaf-archived-patches/removed/hardfork/paperserver/0002-Leaf-Bootstrap.patch +++ b/leaf-server/paper-patches/features/0002-Leaf-Bootstrap.patch @@ -8,7 +8,7 @@ Removed since Leaf 1.21.4, useless org.bukkit.craftbukkit.Main#main -> LeafBootstrap -> PaperBootstrap -> ... diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index ecb0fcd1f3b3f3d7751eded3cdf0977c1889c9ed..d0becb56a9911ef4cc55ae8d7c47832f442ad52f 100644 +index bf5343b0847e9f57ffbc7f33714ae6ca62f14332..70d093f71cca569d9da3ae82e738f8f3069f405e 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -278,7 +278,8 @@ public class Main { diff --git a/leaf-server/paper-patches/features/0002-Leaf-Commands.patch b/leaf-server/paper-patches/features/0003-Leaf-Commands.patch similarity index 100% rename from leaf-server/paper-patches/features/0002-Leaf-Commands.patch rename to leaf-server/paper-patches/features/0003-Leaf-Commands.patch diff --git a/leaf-server/paper-patches/features/0003-Pufferfish-Optimize-mob-spawning.patch b/leaf-server/paper-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch similarity index 100% rename from leaf-server/paper-patches/features/0003-Pufferfish-Optimize-mob-spawning.patch rename to leaf-server/paper-patches/features/0004-Pufferfish-Optimize-mob-spawning.patch diff --git a/leaf-server/paper-patches/features/0004-Purpur-Server-Paper-Changes.patch b/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch similarity index 99% rename from leaf-server/paper-patches/features/0004-Purpur-Server-Paper-Changes.patch rename to leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch index da1e28e2..385589c1 100644 --- a/leaf-server/paper-patches/features/0004-Purpur-Server-Paper-Changes.patch +++ b/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch @@ -761,7 +761,7 @@ index a92e0877669a92851c6d7f83de75ffb087c8e651..daede6da974beb5ff19877caa5e6f8b3 public Collection getStructures(int x, int z) { return this.getStructures(x, z, struct -> true); diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index bf5343b0847e9f57ffbc7f33714ae6ca62f14332..2e1b7f613de8876095ef39bb0341a3f9520c8d5d 100644 +index 70d093f71cca569d9da3ae82e738f8f3069f405e..7120ff469b0a0b638a4051b92f00f97da75edae0 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -176,6 +176,13 @@ public class Main { diff --git a/leaf-server/paper-patches/features/0005-Fix-Pufferfish-and-Purpur-patches.patch b/leaf-server/paper-patches/features/0006-Fix-Pufferfish-and-Purpur-patches.patch similarity index 100% rename from leaf-server/paper-patches/features/0005-Fix-Pufferfish-and-Purpur-patches.patch rename to leaf-server/paper-patches/features/0006-Fix-Pufferfish-and-Purpur-patches.patch diff --git a/leaf-server/paper-patches/features/0006-Remove-Timings.patch b/leaf-server/paper-patches/features/0007-Remove-Timings.patch similarity index 100% rename from leaf-server/paper-patches/features/0006-Remove-Timings.patch rename to leaf-server/paper-patches/features/0007-Remove-Timings.patch diff --git a/leaf-server/paper-patches/features/0007-KeYi-Player-Skull-API.patch b/leaf-server/paper-patches/features/0008-KeYi-Player-Skull-API.patch similarity index 100% rename from leaf-server/paper-patches/features/0007-KeYi-Player-Skull-API.patch rename to leaf-server/paper-patches/features/0008-KeYi-Player-Skull-API.patch diff --git a/leaf-server/paper-patches/features/0008-Slice-Smooth-Teleports.patch b/leaf-server/paper-patches/features/0009-Slice-Smooth-Teleports.patch similarity index 100% rename from leaf-server/paper-patches/features/0008-Slice-Smooth-Teleports.patch rename to leaf-server/paper-patches/features/0009-Slice-Smooth-Teleports.patch diff --git a/leaf-server/paper-patches/features/0009-Leaves-Protocol-Core.patch b/leaf-server/paper-patches/features/0010-Leaves-Protocol-Core.patch similarity index 100% rename from leaf-server/paper-patches/features/0009-Leaves-Protocol-Core.patch rename to leaf-server/paper-patches/features/0010-Leaves-Protocol-Core.patch diff --git a/leaf-server/paper-patches/features/0010-Leaves-Replay-Mod-API.patch b/leaf-server/paper-patches/features/0011-Leaves-Replay-Mod-API.patch similarity index 100% rename from leaf-server/paper-patches/features/0010-Leaves-Replay-Mod-API.patch rename to leaf-server/paper-patches/features/0011-Leaves-Replay-Mod-API.patch diff --git a/leaf-server/paper-patches/features/0011-Skip-event-if-no-listeners.patch b/leaf-server/paper-patches/features/0012-Skip-event-if-no-listeners.patch similarity index 100% rename from leaf-server/paper-patches/features/0011-Skip-event-if-no-listeners.patch rename to leaf-server/paper-patches/features/0012-Skip-event-if-no-listeners.patch diff --git a/leaf-server/paper-patches/features/0012-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch b/leaf-server/paper-patches/features/0013-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch similarity index 100% rename from leaf-server/paper-patches/features/0012-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch rename to leaf-server/paper-patches/features/0013-SparklyPaper-Skip-EntityScheduler-s-executeTick-chec.patch diff --git a/leaf-server/paper-patches/features/0013-SparklyPaper-Optimize-canSee-checks.patch b/leaf-server/paper-patches/features/0014-SparklyPaper-Optimize-canSee-checks.patch similarity index 100% rename from leaf-server/paper-patches/features/0013-SparklyPaper-Optimize-canSee-checks.patch rename to leaf-server/paper-patches/features/0014-SparklyPaper-Optimize-canSee-checks.patch diff --git a/leaf-server/paper-patches/features/0014-Including-5s-in-getTPS.patch b/leaf-server/paper-patches/features/0015-Including-5s-in-getTPS.patch similarity index 100% rename from leaf-server/paper-patches/features/0014-Including-5s-in-getTPS.patch rename to leaf-server/paper-patches/features/0015-Including-5s-in-getTPS.patch diff --git a/leaf-server/paper-patches/features/0015-Don-t-throw-exception-on-missing-ResourceKey-value.patch b/leaf-server/paper-patches/features/0016-Don-t-throw-exception-on-missing-ResourceKey-value.patch similarity index 100% rename from leaf-server/paper-patches/features/0015-Don-t-throw-exception-on-missing-ResourceKey-value.patch rename to leaf-server/paper-patches/features/0016-Don-t-throw-exception-on-missing-ResourceKey-value.patch diff --git a/leaf-server/paper-patches/features/0016-Virtual-Thread-for-async-scheduler.patch b/leaf-server/paper-patches/features/0017-Virtual-Thread-for-async-scheduler.patch similarity index 100% rename from leaf-server/paper-patches/features/0016-Virtual-Thread-for-async-scheduler.patch rename to leaf-server/paper-patches/features/0017-Virtual-Thread-for-async-scheduler.patch diff --git a/leaf-server/paper-patches/features/0017-Mirai-Configurable-chat-message-signatures.patch b/leaf-server/paper-patches/features/0018-Mirai-Configurable-chat-message-signatures.patch similarity index 100% rename from leaf-server/paper-patches/features/0017-Mirai-Configurable-chat-message-signatures.patch rename to leaf-server/paper-patches/features/0018-Mirai-Configurable-chat-message-signatures.patch diff --git a/leaf-server/paper-patches/features/0018-Matter-Secure-Seed.patch b/leaf-server/paper-patches/features/0019-Matter-Secure-Seed.patch similarity index 100% rename from leaf-server/paper-patches/features/0018-Matter-Secure-Seed.patch rename to leaf-server/paper-patches/features/0019-Matter-Secure-Seed.patch diff --git a/leaf-server/paper-patches/features/0019-Faster-random-generator.patch b/leaf-server/paper-patches/features/0020-Faster-random-generator.patch similarity index 100% rename from leaf-server/paper-patches/features/0019-Faster-random-generator.patch rename to leaf-server/paper-patches/features/0020-Faster-random-generator.patch diff --git a/leaf-server/paper-patches/features/0020-Configurable-unknown-command-message.patch b/leaf-server/paper-patches/features/0021-Configurable-unknown-command-message.patch similarity index 100% rename from leaf-server/paper-patches/features/0020-Configurable-unknown-command-message.patch rename to leaf-server/paper-patches/features/0021-Configurable-unknown-command-message.patch diff --git a/leaf-server/paper-patches/features/0021-Replace-world-map-with-optimized-collection.patch b/leaf-server/paper-patches/features/0022-Replace-world-map-with-optimized-collection.patch similarity index 100% rename from leaf-server/paper-patches/features/0021-Replace-world-map-with-optimized-collection.patch rename to leaf-server/paper-patches/features/0022-Replace-world-map-with-optimized-collection.patch diff --git a/leaf-server/paper-patches/features/0022-Cache-CraftEntityType-minecraftToBukkit-convert.patch b/leaf-server/paper-patches/features/0023-Cache-CraftEntityType-minecraftToBukkit-convert.patch similarity index 100% rename from leaf-server/paper-patches/features/0022-Cache-CraftEntityType-minecraftToBukkit-convert.patch rename to leaf-server/paper-patches/features/0023-Cache-CraftEntityType-minecraftToBukkit-convert.patch diff --git a/leaf-server/paper-patches/features/0023-Multithreaded-Tracker.patch b/leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch similarity index 100% rename from leaf-server/paper-patches/features/0023-Multithreaded-Tracker.patch rename to leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch diff --git a/leaf-server/paper-patches/features/0024-Asynchronous-locator.patch b/leaf-server/paper-patches/features/0025-Asynchronous-locator.patch similarity index 100% rename from leaf-server/paper-patches/features/0024-Asynchronous-locator.patch rename to leaf-server/paper-patches/features/0025-Asynchronous-locator.patch diff --git a/leaf-server/paper-patches/features/0025-EMC-Don-t-use-snapshots-for-acquiring-blockstate.patch b/leaf-server/paper-patches/features/0026-EMC-Don-t-use-snapshots-for-acquiring-blockstate.patch similarity index 100% rename from leaf-server/paper-patches/features/0025-EMC-Don-t-use-snapshots-for-acquiring-blockstate.patch rename to leaf-server/paper-patches/features/0026-EMC-Don-t-use-snapshots-for-acquiring-blockstate.patch diff --git a/leaf-server/paper-patches/features/0026-Faster-CraftServer-getworlds-list-creation.patch b/leaf-server/paper-patches/features/0027-Faster-CraftServer-getworlds-list-creation.patch similarity index 100% rename from leaf-server/paper-patches/features/0026-Faster-CraftServer-getworlds-list-creation.patch rename to leaf-server/paper-patches/features/0027-Faster-CraftServer-getworlds-list-creation.patch diff --git a/leaf-server/paper-patches/features/0027-Cache-chunk-key.patch b/leaf-server/paper-patches/features/0028-Cache-chunk-key.patch similarity index 100% rename from leaf-server/paper-patches/features/0027-Cache-chunk-key.patch rename to leaf-server/paper-patches/features/0028-Cache-chunk-key.patch diff --git a/leaf-server/paper-patches/features/0028-Async-structure-locate-api.patch b/leaf-server/paper-patches/features/0029-Async-structure-locate-api.patch similarity index 100% rename from leaf-server/paper-patches/features/0028-Async-structure-locate-api.patch rename to leaf-server/paper-patches/features/0029-Async-structure-locate-api.patch diff --git a/leaf-server/paper-patches/features/0029-PlayerInventoryOverflowEvent.patch b/leaf-server/paper-patches/features/0030-PlayerInventoryOverflowEvent.patch similarity index 100% rename from leaf-server/paper-patches/features/0029-PlayerInventoryOverflowEvent.patch rename to leaf-server/paper-patches/features/0030-PlayerInventoryOverflowEvent.patch diff --git a/leaf-server/paper-patches/features/0030-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch similarity index 99% rename from leaf-server/paper-patches/features/0030-SparklyPaper-Parallel-world-ticking.patch rename to leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch index 19025c6e..fccb0eec 100644 --- a/leaf-server/paper-patches/features/0030-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch @@ -315,7 +315,7 @@ index af33cab59932f4ec135caf94dc5828930833daf6..caa92e48d031cb54950e6613a82f407d } // Paper end diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java -index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911c39f80c0 100644 +index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a515b0a5764 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java @@ -74,13 +74,98 @@ public class CraftBlock implements Block { @@ -540,7 +540,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 } public Block getFace(final BlockFace face) { -@@ -282,51 +422,37 @@ public class CraftBlock implements Block { +@@ -282,51 +422,36 @@ public class CraftBlock implements Block { @Override public String toString() { @@ -577,7 +577,6 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 + case SOUTH -> BlockFace.SOUTH; + case WEST -> BlockFace.WEST; + case EAST -> BlockFace.EAST; -+ default -> BlockFace.SELF; + }; + // Leaf end - SparklyPaper - parallel world ticking - formatting } @@ -617,7 +616,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 } @Override -@@ -343,18 +469,65 @@ public class CraftBlock implements Block { +@@ -343,18 +468,65 @@ public class CraftBlock implements Block { @Override public Biome getBiome() { @@ -685,7 +684,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio); } -@@ -370,12 +543,50 @@ public class CraftBlock implements Block { +@@ -370,12 +542,50 @@ public class CraftBlock implements Block { @Override public boolean isBlockPowered() { @@ -738,7 +737,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 } @Override -@@ -397,46 +608,102 @@ public class CraftBlock implements Block { +@@ -397,46 +607,101 @@ public class CraftBlock implements Block { @Override public boolean isBlockFacePowered(BlockFace face) { @@ -829,7 +828,6 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 + // Requires Level for hasSignal and getBlockState + if (this.world instanceof net.minecraft.world.level.Level nmsLevel) { + int power = 0; -+ int x = this.getX(); int y = this.getY(); int z = this.getZ(); + BlockPos currentPos = this.position; // Use immutable position + + // Check neighbors using relative positions @@ -868,7 +866,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 @Override public int getBlockPower() { -@@ -479,105 +746,179 @@ public class CraftBlock implements Block { +@@ -479,105 +744,179 @@ public class CraftBlock implements Block { @Override public PistonMoveReaction getPistonMoveReaction() { @@ -1089,7 +1087,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 } @Override -@@ -592,31 +933,70 @@ public class CraftBlock implements Block { +@@ -592,31 +931,70 @@ public class CraftBlock implements Block { @Override public Collection getDrops(ItemStack item, Entity entity) { @@ -1170,7 +1168,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 @Override public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { this.getCraftWorld().getBlockMetadata().setMetadata(this, metadataKey, newMetadataValue); -@@ -639,57 +1019,147 @@ public class CraftBlock implements Block { +@@ -639,57 +1017,147 @@ public class CraftBlock implements Block { @Override public boolean isPassable() { @@ -1342,7 +1340,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 } @Override -@@ -700,7 +1170,10 @@ public class CraftBlock implements Block { +@@ -700,7 +1168,10 @@ public class CraftBlock implements Block { // Paper start @Override public com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup() { @@ -1354,7 +1352,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..ac45c5cbe547705e3e341011740cf911 } @Override -@@ -713,26 +1186,76 @@ public class CraftBlock implements Block { +@@ -713,26 +1184,76 @@ public class CraftBlock implements Block { return this.getNMS().getBlock().getDescriptionId(); } diff --git a/leaf-server/paper-patches/features/0031-Paper-PR-Throttle-failed-spawn-attempts.patch b/leaf-server/paper-patches/features/0032-Paper-PR-Throttle-failed-spawn-attempts.patch similarity index 100% rename from leaf-server/paper-patches/features/0031-Paper-PR-Throttle-failed-spawn-attempts.patch rename to leaf-server/paper-patches/features/0032-Paper-PR-Throttle-failed-spawn-attempts.patch diff --git a/leaf-server/paper-patches/features/0032-Async-playerdata-saving.patch b/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch similarity index 100% rename from leaf-server/paper-patches/features/0032-Async-playerdata-saving.patch rename to leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch diff --git a/leaf-server/paper-patches/features/0033-PaperPR-Fix-save-load-NaN-Entity-Motion.patch b/leaf-server/paper-patches/features/0034-PaperPR-Fix-save-load-NaN-Entity-Motion.patch similarity index 100% rename from leaf-server/paper-patches/features/0033-PaperPR-Fix-save-load-NaN-Entity-Motion.patch rename to leaf-server/paper-patches/features/0034-PaperPR-Fix-save-load-NaN-Entity-Motion.patch diff --git a/leaf-server/paper-patches/features/0034-PaperPR-Fix-unnecessary-map-data-saves.patch b/leaf-server/paper-patches/features/0035-PaperPR-Fix-unnecessary-map-data-saves.patch similarity index 100% rename from leaf-server/paper-patches/features/0034-PaperPR-Fix-unnecessary-map-data-saves.patch rename to leaf-server/paper-patches/features/0035-PaperPR-Fix-unnecessary-map-data-saves.patch diff --git a/leaf-server/paper-patches/features/0035-Async-chunk-send.patch b/leaf-server/paper-patches/features/0036-Async-chunk-send.patch similarity index 100% rename from leaf-server/paper-patches/features/0035-Async-chunk-send.patch rename to leaf-server/paper-patches/features/0036-Async-chunk-send.patch diff --git a/leaf-server/paper-patches/features/0036-Optimise-player-movement-checks.patch b/leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch similarity index 100% rename from leaf-server/paper-patches/features/0036-Optimise-player-movement-checks.patch rename to leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch diff --git a/leaf-server/paper-patches/features/0037-Paw-optimization.patch b/leaf-server/paper-patches/features/0038-Paw-optimization.patch similarity index 100% rename from leaf-server/paper-patches/features/0037-Paw-optimization.patch rename to leaf-server/paper-patches/features/0038-Paw-optimization.patch diff --git a/leaf-server/paper-patches/features/0038-optimise-ReferenceList.patch b/leaf-server/paper-patches/features/0039-optimise-ReferenceList.patch similarity index 100% rename from leaf-server/paper-patches/features/0038-optimise-ReferenceList.patch rename to leaf-server/paper-patches/features/0039-optimise-ReferenceList.patch diff --git a/leaf-server/paper-patches/features/0039-cache-getBiome.patch b/leaf-server/paper-patches/features/0040-cache-getBiome.patch similarity index 100% rename from leaf-server/paper-patches/features/0039-cache-getBiome.patch rename to leaf-server/paper-patches/features/0040-cache-getBiome.patch diff --git a/leaf-server/src/main/java/org/dreeam/leaf/LeafBootstrap.java b/leaf-server/src/main/java/org/dreeam/leaf/LeafBootstrap.java index 0ffa8fb1..06be488a 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/LeafBootstrap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/LeafBootstrap.java @@ -4,10 +4,11 @@ import io.papermc.paper.PaperBootstrap; import joptsimple.OptionSet; public class LeafBootstrap { + public static final boolean enableFMA = Boolean.parseBoolean(System.getProperty("Leaf.enableFMA", "false")); // Leaf - FMA feature public static void boot(final OptionSet options) { - runPreBootTasks(); + //runPreBootTasks(); PaperBootstrap.boot(options); } From ae1e3e83556d99d8888ab1eae9c9e183cb416ab6 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Fri, 6 Jun 2025 02:37:29 +0800 Subject: [PATCH 22/57] Paper: Optimise CraftWorld#getLoadedChunks --- build-data/leaf.at | 1 + .../features/0185-optimize-mob-spawning.patch | 4 +- ...-Optimise-CraftWorld-getLoadedChunks.patch | 44 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 leaf-server/paper-patches/features/0041-Paper-Optimise-CraftWorld-getLoadedChunks.patch diff --git a/build-data/leaf.at b/build-data/leaf.at index 5889428b..21a98f49 100644 --- a/build-data/leaf.at +++ b/build-data/leaf.at @@ -4,6 +4,7 @@ protected net.minecraft.world.entity.Entity dimensions protected net.minecraft.world.entity.ai.goal.RemoveBlockGoal blockToRemove protected net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal getTargetConditions()Lnet/minecraft/world/entity/ai/targeting/TargetingConditions; protected-f net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase$Cache largeCollisionShape +public net.minecraft.server.level.ServerChunkCache fullChunks public net.minecraft.server.level.ServerEntity sendDirtyEntityData()V public net.minecraft.util.Mth SIN public net.minecraft.world.entity.Entity updateInWaterStateAndDoWaterCurrentPushing()V diff --git a/leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch b/leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch index c4f91deb..9982aaaf 100644 --- a/leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch +++ b/leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch @@ -5,7 +5,7 @@ Subject: [PATCH] optimize mob spawning diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index d61da0fbe7f6c181e4084ce60bfe7dab86f361ad..1ce1b21a77cd727b52bd937b65ce97517fed9c68 100644 +index 39a31abe25d2bb56b28462cf25d2d09f7722526c..2f927b422c2c4f2f65d822befe3cbfd9e3bb3708 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -70,7 +70,9 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -17,7 +17,7 @@ index d61da0fbe7f6c181e4084ce60bfe7dab86f361ad..1ce1b21a77cd727b52bd937b65ce9751 + private long delayTimeInhabited = 0L; // Leaf + private long delaySpawn = -1L; // Leaf // Paper start - private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); + public final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); public int getFullChunksCount() { @@ -656,13 +658,37 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon filteredSpawningCategories = List.of(); diff --git a/leaf-server/paper-patches/features/0041-Paper-Optimise-CraftWorld-getLoadedChunks.patch b/leaf-server/paper-patches/features/0041-Paper-Optimise-CraftWorld-getLoadedChunks.patch new file mode 100644 index 00000000..eadc2d9e --- /dev/null +++ b/leaf-server/paper-patches/features/0041-Paper-Optimise-CraftWorld-getLoadedChunks.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 5 Jun 2025 08:16:25 -0700 +Subject: [PATCH] Paper: Optimise CraftWorld#getLoadedChunks + +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper + +https://github.com/PaperMC/Paper/commit/24cd24c8cc0df4bea0784e6919c9ba8f1852d46e + +We can use the existing full chunk map so that we do not need +to iterate over all ChunkHolders. Additionally, do not use streams +to do the iteration either. + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index caa92e48d031cb54950e6613a82f407d7ed2455a..236be1bb296d0f080d2a8c739d2678655e81e174 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -420,8 +420,23 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public Chunk[] getLoadedChunks() { +- List chunks = ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.world); // Paper +- return chunks.stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull).map(CraftChunk::new).toArray(Chunk[]::new); ++ // Paper start - Optimise CraftWorld#getLoadedChunks ++ net.minecraft.server.level.ServerChunkCache serverChunkCache = this.getHandle().chunkSource; ++ ca.spottedleaf.moonrise.common.list.ReferenceList chunks = new ca.spottedleaf.moonrise.common.list.ReferenceList<>(new Chunk[serverChunkCache.fullChunks.size()]); ++ ++ for (java.util.PrimitiveIterator.OfLong iterator = serverChunkCache.fullChunks.keyIterator(); iterator.hasNext();) { ++ long chunk = iterator.nextLong(); ++ chunks.add(new CraftChunk(this.world, ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(chunk), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(chunk))); ++ } ++ ++ Chunk[] raw = chunks.getRawDataUnchecked(); ++ int size = chunks.size(); ++ if (raw.length == size) { ++ // always true when on main ++ return raw; ++ } ++ return java.util.Arrays.copyOf(raw, size); ++ // Paper end - Optimise CraftWorld#getLoadedChunks + } + + @Override From 950be3d8cd986cc92e9b3a036de8bf3f2cb705af Mon Sep 17 00:00:00 2001 From: Dragin Date: Thu, 5 Jun 2025 18:43:59 -0500 Subject: [PATCH 23/57] [ci skip] Fix grammatical issues in README.md (#351) * Fix grammatical issues in README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b471e356..04813f4a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Discord](https://img.shields.io/discord/1145991395388162119?label=discord&style=for-the-badge&colorA=19201a&colorB=298046)](https://discord.gg/gfgAwdSEuM) [![Docs](https://img.shields.io/badge/Docs-leafmc.one/docs/-blue?label=docs&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/docs/) -**Leaf** is a [Paper](https://papermc.io/) fork designed to customized and high-performance, built on top of [Gale](https://github.com/Dreeam-qwq/Gale) with optimizations and fixes from other forks. +**Leaf** is a [Paper](https://papermc.io/) fork designed to be customizable and high-performance, built on top of [Gale](https://github.com/Dreeam-qwq/Gale) with optimizations and fixes from other forks. > [!WARNING] @@ -17,14 +17,14 @@ - **Async** pathfinding, mob spawning and entity tracker - **Various optimizations** blending from [other forks](https://github.com/Winds-Studio/Leaf#-credits) - **Fully compatible** with Bukkit, Spigot and Paper plugins - - **Latest dependencies**, keeping all dependencies in the newest version + - **Latest dependencies**, keeping all dependencies up-to-date - **Allows all characters in usernames**, including Chinese and other characters - **Fixes** some Minecraft bugs - **Configurable UseItem distance** for anarchy servers - **Mod Protocols** support - **More customized** relying on features of [Purpur](https://github.com/PurpurMC/Purpur) - Support for **Linear region file format** - - **Maintenance friendly**, integrating with [Sentry](https://sentry.io/welcome/) of [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) to easy track all errors coming from your server in excruciating detail + - **Maintenance friendly**, integrating with [Sentry](https://sentry.io/welcome/) of [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) to easily track all errors coming from your server in extreme detail - And more... ## 📈 bStats @@ -90,11 +90,11 @@ Paperweight files are licensed under [MIT](licenses/MIT.txt). Patches are licensed under [MIT](licenses/MIT.txt), unless indicated differently in their header. Binaries are licensed under [GPL-3.0](licenses/GPL-3.0.txt). -Also see [PaperMC/Paper](https://github.com/PaperMC/Paper) and [PaperMC/Paperweight](https://github.com/PaperMC/paperweight) for the license of some material used by this project. +Also see [PaperMC/Paper](https://github.com/PaperMC/Paper) and [PaperMC/Paperweight](https://github.com/PaperMC/paperweight) for the licenses of some materials used by this project. ## 📜 Credits Thanks to these projects below. Leaf includes some patches taken from them.
-If these excellent projects hadn't appeared, Leaf wouldn't have become great. +If these excellent projects hadn't existed, Leaf wouldn't have become great. - [Gale](https://github.com/Dreeam-qwq/Gale) ([Original Repo](https://github.com/GaleMC/Gale)) - [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) @@ -127,7 +127,7 @@ If these excellent projects hadn't appeared, Leaf wouldn't have become great. Jianke Cloud Host cloud of swordsman | 剑客云 -If you want to find a cheaper, high performance, stable with lower latency, then cloud of swordsman is a good choice! Registers and purchases in [here](https://cloud.swordsman.com.cn/?i8ab42c). +If you want to find a cheaper, high performance, stable, lower latency host, then cloud of swordsman is a good choice! Registers and purchases in [here](https://cloud.swordsman.com.cn/?i8ab42c). 如果你想找一个低价高性能, 低延迟的云服务商,剑客云是个不错的选择! 你可以在[这里](https://cloud.swordsman.com.cn/?i8ab42c)注册. From b70aaa04505e2cad4862321bc5b03e71c670ed93 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Fri, 6 Jun 2025 13:10:31 +0800 Subject: [PATCH 24/57] Add old Blast Protection explosion knockback behavior (#346) Added configurable old Blast Protection explosion knockback behavior that is from <=1.21.4 version. --- ...heck-inside-blocks-and-traverse-blo.patch} | 5 +- ...yList-implementation-to-BasicEntity.patch} | 4 +- ...Blast-Protection-explosion-knockback.patch | 81 +++++++++++++++++++ .../config/modules/gameplay/Knockback.java | 2 + .../leaf/util/map/BlockPosIterator.java | 2 +- 5 files changed, 89 insertions(+), 5 deletions(-) rename leaf-server/minecraft-patches/features/{0162-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch => 0161-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch} (96%) rename leaf-server/minecraft-patches/features/{0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch => 0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch} (97%) create mode 100644 leaf-server/minecraft-patches/features/0190-Old-Blast-Protection-explosion-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0162-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch b/leaf-server/minecraft-patches/features/0161-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch similarity index 96% rename from leaf-server/minecraft-patches/features/0162-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch rename to leaf-server/minecraft-patches/features/0161-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch index cc4289ed..1d857d46 100644 --- a/leaf-server/minecraft-patches/features/0162-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch +++ b/leaf-server/minecraft-patches/features/0161-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch @@ -1,8 +1,9 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik -Date: Thu, 1 May 2025 22:38:48 +0200 -Subject: [PATCH] Sakura: Optimise-check-inside-blocks-and-traverse-blocks +Date: Fri, 8 Nov 2024 19:35:49 +0000 +Subject: [PATCH] Sakura: Optimise check inside blocks and traverse blocks +Dreeam TODO: refactor checkinsideblcoks diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java index df4b26a6304df92f4eda27ac986ca78d5217ce6c..a4de10a32f49b7b361fc9dd1d142caeef8d7d148 100644 diff --git a/leaf-server/minecraft-patches/features/0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch similarity index 97% rename from leaf-server/minecraft-patches/features/0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch rename to leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch index e38ae491..8ce7b204 100644 --- a/leaf-server/minecraft-patches/features/0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch +++ b/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik -Date: Thu, 1 May 2025 22:28:50 +0200 -Subject: [PATCH] Sakura: copy-EntityList-implementation-to-BasicEntityList +Date: Sun, 2 Feb 2025 17:05:05 +0000 +Subject: [PATCH] Sakura: copy EntityList implementation to BasicEntityList diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java diff --git a/leaf-server/minecraft-patches/features/0190-Old-Blast-Protection-explosion-knockback.patch b/leaf-server/minecraft-patches/features/0190-Old-Blast-Protection-explosion-knockback.patch new file mode 100644 index 00000000..60d58d15 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0190-Old-Blast-Protection-explosion-knockback.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Mon, 2 Jun 2025 03:29:20 +0800 +Subject: [PATCH] Old Blast Protection explosion knockback + + +diff --git a/net/minecraft/world/entity/EquipmentSlot.java b/net/minecraft/world/entity/EquipmentSlot.java +index 0a5611b1ece4dbe2887e7fbdef45f58e7f4d53ad..9f6fc274525f2fe4e4e35e0feaa410bf5d8eba04 100644 +--- a/net/minecraft/world/entity/EquipmentSlot.java ++++ b/net/minecraft/world/entity/EquipmentSlot.java +@@ -21,6 +21,7 @@ public enum EquipmentSlot implements StringRepresentable { + public static final int NO_COUNT_LIMIT = 0; + public static final EquipmentSlot[] VALUES_ARRAY = values(); // Gale - JettPack - reduce array allocations + public static final List VALUES = List.of(VALUES_ARRAY); // Gale - JettPack - reduce array allocations ++ public static final EquipmentSlot[] ARMOR_SLOTS = new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET}; // Leaf - Old Blast Protection explosion knockback + public static final IntFunction BY_ID = ByIdMap.continuous(equipmentSlot -> equipmentSlot.id, values(), ByIdMap.OutOfBoundsStrategy.ZERO); + public static final StringRepresentable.EnumCodec CODEC = StringRepresentable.fromEnum(EquipmentSlot::values); + public static final StreamCodec STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, equipmentSlot -> equipmentSlot.id); +diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java +index ea9c641fe9a9685307b6de2999ea4ff5342269b7..ae0dab1f8470cf53031a2ba776fa70d8ae074a87 100644 +--- a/net/minecraft/world/level/ServerExplosion.java ++++ b/net/minecraft/world/level/ServerExplosion.java +@@ -532,7 +532,7 @@ public class ServerExplosion implements Explosion { + double d4 = (1.0 - d) * f1 * knockbackMultiplier; + double d5; + if (entity instanceof LivingEntity livingEntity) { +- d5 = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : d4 * (1.0 - livingEntity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE)); // Paper ++ d5 = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : getExplosionKnockback(livingEntity, d4); // Paper // Leaf - Old Blast Protection explosion knockback + } else { + d5 = d4; + } +@@ -564,6 +564,49 @@ public class ServerExplosion implements Explosion { + } + } + ++ // Leaf start - Old Blast Protection explosion knockback ++ private static double getExplosionKnockback(LivingEntity entity, double velocity) { ++ if (!org.dreeam.leaf.config.modules.gameplay.Knockback.oldBlastProtectionKnockbackBehavior) { ++ return velocity * (1.0 - entity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE)); ++ } ++ ++ // Old BLAST_PROTECTION logic ++ // BLAST_PROTECTION used ARMOR_SLOTS for slot types ++ // See 1.20.4's ProtectionEnchantment#getExplosionKnockbackAfterDampener, ++ // EnchantmentHelper#getEnchantmentLevel, Enchantment#getSlotItems, ++ // EnchantmentHelper#getItemEnchantmentLevel, Enchantments#BLAST_PROTECTION, ++ // these methods/fields for reference. ++ Map map = com.google.common.collect.Maps.newEnumMap(net.minecraft.world.entity.EquipmentSlot.class); ++ ++ for (net.minecraft.world.entity.EquipmentSlot slot : net.minecraft.world.entity.EquipmentSlot.ARMOR_SLOTS) { ++ ItemStack itemStack = entity.getItemBySlot(slot); ++ if (!itemStack.isEmpty()) { ++ map.put(slot, itemStack); ++ } ++ } ++ ++ Iterable items = map.values(); ++ int i = 0; ++ ++ if (items == null) { ++ return 0; ++ } ++ ++ for (ItemStack itemStack : items) { ++ int enchantmentLevel = net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.BLAST_PROTECTION, itemStack); ++ if (enchantmentLevel > i) { ++ i = enchantmentLevel; ++ } ++ } ++ ++ if (i > 0) { ++ velocity *= Mth.clamp(1.0 - (double) i * 0.15, 0.0, 1.0); ++ } ++ ++ return velocity; ++ } ++ // Leaf end - Old Blast Protection explosion knockback ++ + private void interactWithBlocks(List blocks) { + List list = new ArrayList<>(); + Util.shuffle(blocks, this.level.random); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java index b0610c61..0e794856 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java @@ -15,6 +15,7 @@ public class Knockback extends ConfigModules { public static boolean canPlayerKnockbackZombie = true; @Experimental public static boolean flushKnockback = false; + public static boolean oldBlastProtectionKnockbackBehavior = false; @Override public void onLoaded() { @@ -34,5 +35,6 @@ public class Knockback extends ConfigModules { "使玩家可以击退僵尸." )); flushKnockback = config.getBoolean(getBasePath() + ".flush-location-while-knockback-player", flushKnockback); + oldBlastProtectionKnockbackBehavior = config.getBoolean(getBasePath() + ".old-blast-protection-explosion-knockback", oldBlastProtectionKnockbackBehavior); } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/BlockPosIterator.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/BlockPosIterator.java index b867cc3b..7e4b0baf 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/BlockPosIterator.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/BlockPosIterator.java @@ -29,7 +29,7 @@ public final class BlockPosIterator extends AbstractIterator { Vec3 movement = vec.scale(toTravel); AABB fromBB = boundingBox.move(-vec.x, -vec.y, -vec.z); AABB searchArea = fromBB.expandTowards(movement); - return org.dreeam.leaf.util.map.BlockPosIterator.iterable(searchArea); + return iterable(searchArea); } public BlockPosIterator(AABB bb) { From 4062254bac5bc17a4caae28c6ce0534b09a433e6 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 6 Jun 2025 10:25:26 +0200 Subject: [PATCH 25/57] pwt fix respawn --- .../0134-SparklyPaper-Parallel-world-ticking.patch | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch index 42eeb075..d9f225d8 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch @@ -867,7 +867,7 @@ index 8362def0dc61496a087bd859052bd80ebba83185..09f517059aa47ca67329bc913243d4fd // Paper end - Inventory close reason this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..3591de34443069f3f163f8d17df6372c3068611d 100644 +index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..6148d5c575f4cdf2d1920b9585a5b32732511301 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -252,6 +252,8 @@ public abstract class PlayerList { @@ -884,12 +884,12 @@ index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..3591de34443069f3f163f8d17df6372c } public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, org.bukkit.Location location) { + // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { -+ if (location != null) // Leaf - THIS CAN BE NULL; see PlayerList::respawn(ServerPlayer, boolean, Entity.RemovalReason, PlayerRespawnEvent.RespawnReason) -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, from world " + player.serverLevel().getWorld().getName() + " to world " + location.getWorld().getName()); -+ else -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, respawning in world " + player.serverLevel().getWorld().getName()); -+ // SparklyPaper end - parallel world ticking (additional concurrency issues logs) ++ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && !this.server.isSameThread()) { ++ // Respawning is a complex operation that modifies global player lists and can interact with multiple ++ // worlds. It must be executed on the main server thread to ensure thread safety. We block the ++ // calling (world) thread to wait for the result, preserving the synchronous API contract of this method. ++ org.bukkit.Location finalLocation = location; ++ return this.server.submit(() -> this.respawn(player, keepInventory, reason, eventReason, finalLocation)).join(); + } + // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) player.stopRiding(); // CraftBukkit From 6cd4ebd40dd15bf893ae4cd983c94d17c0fbe1e6 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 6 Jun 2025 10:54:30 +0200 Subject: [PATCH 26/57] Paper: Update CraftWorld#getForceLoadedChunks to avoid using getChunkAt --- ...ftWorld-getForceLoadedChunks-to-avoi.patch | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 leaf-server/paper-patches/features/0042-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch diff --git a/leaf-server/paper-patches/features/0042-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch b/leaf-server/paper-patches/features/0042-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch new file mode 100644 index 00000000..77da8702 --- /dev/null +++ b/leaf-server/paper-patches/features/0042-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Fri, 6 Jun 2025 10:47:32 +0200 +Subject: [PATCH] Paper: Update CraftWorld#getForceLoadedChunks to avoid using + getChunkAt + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 236be1bb296d0f080d2a8c739d2678655e81e174..e5ed5bae80aad4ddbea9fca0c0fe00cf95bb6f47 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -657,7 +657,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + Set chunks = new HashSet<>(); + + for (long coord : this.getHandle().getForcedChunks()) { +- chunks.add(this.getChunkAt(ChunkPos.getX(coord), ChunkPos.getZ(coord))); ++ chunks.add(new CraftChunk(this.getHandle(), ChunkPos.getX(coord), ChunkPos.getZ(coord))); + } + + return Collections.unmodifiableCollection(chunks); From 87db40b4b14dc18384c2b8b256bd1eb4b376cf39 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 7 Jun 2025 00:13:40 +0200 Subject: [PATCH 27/57] PWT: fix chained updates --- ...-SparklyPaper-Parallel-world-ticking.patch | 114 +++++++++++++++++- 1 file changed, 111 insertions(+), 3 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch index d9f225d8..aaf2e279 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch @@ -41,6 +41,27 @@ index be820c6093dd2ae7642b9bee11edf65e3a8d7242..06ac3537f5655d048d770bb004243f20 boolean ret = false; final boolean canProcessFullUpdates = processFullUpdates & isTickThread; +diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java +index ff747a1ecdf3c888bca0d69de4f85dcd810b6139..62ecbbf7c167beaa3b67fc4c30e901c5d359d6b8 100644 +--- a/io/papermc/paper/redstone/RedstoneWireTurbo.java ++++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java +@@ -829,14 +829,8 @@ 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; ++ // This now correctly calls the (conditionally) thread-safe method in RedStoneWireBlock ++ final int k = wire.getBlockSignal(worldIn, upd.self); + + // 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/BoatDispenseItemBehavior.java b/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java index 4a881636ba21fae9e50950bbba2b4321b71d35ab..9b01cc21a7e7411b8d112b5b7d7061e66c9bde06 100644 --- a/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java @@ -1340,10 +1361,21 @@ index 904369f4d7db41026183f2de7c96c2f0f4dc204d..afd952ddc8942818ec01d1c750413776 return true; } else { diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java -index 12c9d60314c99fb65e640d255a2d0c6b7790ad4d..33a096ca7d175a714f874901d0152d0ecbc7f2a5 100644 +index 12c9d60314c99fb65e640d255a2d0c6b7790ad4d..5a60c5e4fe122d37a0aed1269128aa5e6e5e87b8 100644 --- a/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/net/minecraft/world/level/block/RedStoneWireBlock.java -@@ -308,7 +308,12 @@ public class RedStoneWireBlock extends Block { +@@ -91,7 +91,10 @@ public class RedStoneWireBlock extends Block { + private static final float PARTICLE_DENSITY = 0.2F; + private final BlockState crossState; + private final RedstoneWireEvaluator evaluator = new DefaultRedstoneWireEvaluator(this); ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) + public boolean shouldSignal = true; ++ private final ThreadLocal shouldSignalTL = ThreadLocal.withInitial(() -> true); ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + + @Override + public MapCodec codec() { +@@ -308,7 +311,12 @@ public class RedStoneWireBlock extends Block { if (orientation != null) { source = pos.relative(orientation.getFront().getOpposite()); } @@ -1357,7 +1389,7 @@ index 12c9d60314c99fb65e640d255a2d0c6b7790ad4d..33a096ca7d175a714f874901d0152d0e return; } updatePowerStrength(worldIn, pos, state, orientation, blockAdded); -@@ -336,7 +341,12 @@ public class RedStoneWireBlock extends Block { +@@ -336,7 +344,12 @@ public class RedStoneWireBlock extends Block { // [Space Walker] suppress shape updates and emit those manually to // bypass the new neighbor update stack. if (level.setBlock(pos, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS)) { @@ -1371,6 +1403,82 @@ index 12c9d60314c99fb65e640d255a2d0c6b7790ad4d..33a096ca7d175a714f874901d0152d0e } } } +@@ -353,10 +366,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; ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) ++ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { ++ 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; ++ } ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + } + + private void checkCornerChangeAt(Level level, BlockPos pos) { +@@ -450,24 +472,34 @@ 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); ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) ++ boolean signal = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.shouldSignalTL.get() : this.shouldSignal; ++ return !signal ? 0 : blockState.getSignal(blockAccess, pos, side); ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + } + + @Override + protected int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { +- if (this.shouldSignal && side != Direction.DOWN) { ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) ++ boolean signal; ++ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ++ signal = this.shouldSignalTL.get(); ++ else ++ signal = this.shouldSignal; ++ if (signal && side != Direction.DOWN) { + int powerValue = blockState.getValue(POWER); + if (powerValue == 0) { + return 0; + } else { + return side != Direction.UP +- && !this.getConnectionState(blockAccess, blockState, pos).getValue(PROPERTY_BY_DIRECTION.get(side.getOpposite())).isConnected() ++ && !this.getConnectionState(blockAccess, blockState, pos).getValue(PROPERTY_BY_DIRECTION.get(side.getOpposite())).isConnected() + ? 0 + : powerValue; + } + } else { + return 0; + } ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + } + + protected static boolean shouldConnectTo(BlockState state) { +@@ -487,7 +519,12 @@ public class RedStoneWireBlock extends Block { + + @Override + protected boolean isSignalSource(BlockState state) { +- return this.shouldSignal; ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) ++ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ++ return this.shouldSignalTL.get(); ++ else ++ return this.shouldSignal; ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + } + + public static int getColorForPower(int power) { diff --git a/net/minecraft/world/level/block/SaplingBlock.java b/net/minecraft/world/level/block/SaplingBlock.java index e014f052e9b0f5ca6b28044e2389782b7d0e0cb8..5485e27b881ab0e4d6d00c088bae094d1232eec7 100644 --- a/net/minecraft/world/level/block/SaplingBlock.java From 1e7a052aba059d1887cb05b27d794261fc6b6a37 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sat, 7 Jun 2025 12:17:33 +0800 Subject: [PATCH 28/57] [ci skip] Remove patch --- .../0110-Cache-ItemStack-max-stack-size.patch | 2 +- .../0042-Optimize-Minecart-collisions.patch | 2 + .../minecraft-patches/features/0191-aaa.patch | 285 ++++++++++++++++++ ...ftWorld-getForceLoadedChunks-to-avoi.patch | 17 +- 4 files changed, 301 insertions(+), 5 deletions(-) rename leaf-archived-patches/{work => removed/hardfork}/server/0110-Cache-ItemStack-max-stack-size.patch (97%) create mode 100644 leaf-server/minecraft-patches/features/0191-aaa.patch diff --git a/leaf-archived-patches/work/server/0110-Cache-ItemStack-max-stack-size.patch b/leaf-archived-patches/removed/hardfork/server/0110-Cache-ItemStack-max-stack-size.patch similarity index 97% rename from leaf-archived-patches/work/server/0110-Cache-ItemStack-max-stack-size.patch rename to leaf-archived-patches/removed/hardfork/server/0110-Cache-ItemStack-max-stack-size.patch index 55d9d72f..093e5879 100644 --- a/leaf-archived-patches/work/server/0110-Cache-ItemStack-max-stack-size.patch +++ b/leaf-archived-patches/removed/hardfork/server/0110-Cache-ItemStack-max-stack-size.patch @@ -3,7 +3,7 @@ From: Taiyou06 Date: Fri, 7 Feb 2025 21:41:51 +0100 Subject: [PATCH] Cache ItemStack max stack size -TODO: check this, fix get max stack size +Abandoned plan, maybe research deeper in this part in the future, maybe not diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java index fd7c1e800cbd4919a1a47f6c468c8776535bd028..fdd7e89bacc98e23f067ba17d0bd93ee84a388cb 100644 diff --git a/leaf-archived-patches/work/server/0042-Optimize-Minecart-collisions.patch b/leaf-archived-patches/work/server/0042-Optimize-Minecart-collisions.patch index 6a9bc4c4..d583555e 100644 --- a/leaf-archived-patches/work/server/0042-Optimize-Minecart-collisions.patch +++ b/leaf-archived-patches/work/server/0042-Optimize-Minecart-collisions.patch @@ -3,6 +3,8 @@ From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sat, 6 Apr 2024 22:57:41 -0400 Subject: [PATCH] Optimize Minecart collisions +TODO: need to re-design + Co-authored-by: MrHua269 Skip tick collisions to to prevent lag causing by massive stacked Minecart diff --git a/leaf-server/minecraft-patches/features/0191-aaa.patch b/leaf-server/minecraft-patches/features/0191-aaa.patch new file mode 100644 index 00000000..7431d1c6 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0191-aaa.patch @@ -0,0 +1,285 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Fri, 6 Jun 2025 19:05:40 +0800 +Subject: [PATCH] aaa + + +diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java +index 8e693028ef9e512094dbb97d6088d95ead03487d..dc5900903589bc806b89df45ea4de361eed6a9ca 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -1948,7 +1948,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc + this.awardStat(Stats.SWIM_ONE_CM, rounded); + this.causeFoodExhaustion(this.level().spigotConfig.swimMultiplier * (float) rounded * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.SWIM); // CraftBukkit - EntityExhaustionEvent // Spigot + } +- } else if (this.isEyeInFluid(FluidTags.WATER)) { ++ } else if (this.isEyeInFluid(1)) { + int rounded = Math.round((float)Math.sqrt(dx * dx + dy * dy + dz * dz) * 100.0F); + if (rounded > 0) { + this.awardStat(Stats.WALK_UNDER_WATER_ONE_CM, rounded); +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 9f581d5bdc3f658694bbd8c80abbce4e27e568d3..2c0417d14d276864c1a74f5264751105dde2f34f 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.objects.ReferenceArraySet; + import java.util.ArrayList; + import java.util.Arrays; + import java.util.HashSet; ++import java.util.Iterator; + import java.util.List; + import java.util.Locale; + import java.util.Objects; +@@ -265,6 +266,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + private static final int FLUID_WATER = 1; // Leaf - Optimize isEyeInFluid + private static final int FLUID_LAVA = 2; // Leaf - Optimize isEyeInFluid + private int fluidCache = 0; // Leaf - Optimize isEyeInFluid ++ private int fluidOnEyesCache = 0; + public int invulnerableTime; + protected boolean firstTick = true; + protected final SynchedEntityData entityData; +@@ -1985,8 +1987,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + private void updateFluidOnEyes() { +- this.wasEyeInWater = this.isEyeInFluid(FluidTags.WATER); +- if (org.dreeam.leaf.config.modules.opt.EyeFluidCache.enabled) fluidCache = 0; else this.fluidOnEyes.clear(); // Leaf - Optimize isEyeInFluid ++ this.wasEyeInWater = this.isEyeInFluid(1); ++ this.fluidOnEyesCache = 0; + + double eyeY = this.getEyeY(); + if (!( +@@ -1999,18 +2001,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + FluidState fluidState = this.level().getFluidState(blockPos); + double d = blockPos.getY() + fluidState.getHeight(this.level(), blockPos); + if (d > eyeY) { +- // Leaf start - Optimize isEyeInFluid +- if (org.dreeam.leaf.config.modules.opt.EyeFluidCache.enabled) { +- if (fluidState.is(FluidTags.WATER)) { +- setFluidStatus(FluidTags.WATER, true); +- } +- if (fluidState.is(FluidTags.LAVA)) { +- setFluidStatus(FluidTags.LAVA, true); +- } ++ TagKey[] tagArray = fluidState.getTagsArray(); ++ if (tagArray.length == 0) { ++ this.fluidOnEyesCache = 0; + } else { +- this.fluidOnEyes.addAll(fluidState.getTagsAsSet()); // Leaf - Remove stream in updateFluidOnEyes ++ for (int i = 0; i < tagArray.length; i++) { ++ TagKey tag = tagArray[i]; ++ if (tag == FluidTags.WATER || tag == FluidTags.LAVA) { ++ this.fluidOnEyesCache++; ++ } ++ } + } +- // Leaf end - Optimize isEyeInFluid + } + } + } +@@ -2108,6 +2109,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + + fluidCache = isInFluid ? (fluidCache | bit) : (fluidCache & ~bit); + } ++ ++ public boolean isEyeInFluid(int fluidBit) { ++ return fluidOnEyesCache >= fluidBit; ++ } + // Leaf end - Optimize isEyeInFluid + + public boolean isInLava() { +@@ -4442,7 +4447,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + + @Nullable + public Entity getFirstPassenger() { +- return this.passengers.isEmpty() ? null : this.passengers.get(0); ++ return this.passengers.isEmpty() ? null : this.passengers.getFirst(); + } + + public boolean hasPassenger(Entity entity) { +diff --git a/net/minecraft/world/entity/ExperienceOrb.java b/net/minecraft/world/entity/ExperienceOrb.java +index a43e5190c0f9ae14ccecccd5b58dc0e17f18b0a1..bd2c7fae6858ba446760f7e18390090434b660e0 100644 +--- a/net/minecraft/world/entity/ExperienceOrb.java ++++ b/net/minecraft/world/entity/ExperienceOrb.java +@@ -133,7 +133,7 @@ public class ExperienceOrb extends Entity { + this.xo = this.getX(); + this.yo = this.getY(); + this.zo = this.getZ(); +- if (this.isEyeInFluid(FluidTags.WATER)) { ++ if (this.isEyeInFluid(1)) { + this.setUnderwaterMovement(); + } else { + this.applyGravity(); +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 156d0c14eedf2f79e4276cc4065e19a43699b965..6072d713d08ea3172eee039b35d1c122813cffa3 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -483,7 +483,7 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf + } + } + +- if (this.isEyeInFluid(FluidTags.WATER) ++ if (this.isEyeInFluid(1) + && !this.level().getBlockState(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())).is(Blocks.BUBBLE_COLUMN)) { + boolean flag1 = !this.canBreatheUnderwater() + && !MobEffectUtil.hasWaterBreathing(this) +diff --git a/net/minecraft/world/entity/animal/AbstractFish.java b/net/minecraft/world/entity/animal/AbstractFish.java +index 28ae152125ed83d8917674b6068f227f87890f30..4b0a8743172b790fb42b47ecef33e52d0cb3c194 100644 +--- a/net/minecraft/world/entity/animal/AbstractFish.java ++++ b/net/minecraft/world/entity/animal/AbstractFish.java +@@ -179,7 +179,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { + + @Override + public void vanillaTick() { // Purpur - Ridables +- if (this.fish.isEyeInFluid(FluidTags.WATER)) { ++ if (this.fish.isEyeInFluid(1)) { + this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0, 0.005, 0.0)); + } + +diff --git a/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/net/minecraft/world/entity/animal/horse/AbstractHorse.java +index 56dc7011ed07f0bd5870fbadde2b5c0c630c5edd..8d02515297ead6073d5eb60d58ff040cd101f3d8 100644 +--- a/net/minecraft/world/entity/animal/horse/AbstractHorse.java ++++ b/net/minecraft/world/entity/animal/horse/AbstractHorse.java +@@ -71,6 +71,7 @@ import net.minecraft.world.level.ServerLevelAccessor; + import net.minecraft.world.level.block.Blocks; + import net.minecraft.world.level.block.SoundType; + import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.chunk.LevelChunk; + import net.minecraft.world.level.gameevent.GameEvent; + import net.minecraft.world.phys.AABB; + import net.minecraft.world.phys.Vec2; +@@ -1202,13 +1203,16 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, + double d1 = this.getBoundingBox().minY; + double d2 = this.getZ() + direction.z; + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); +- ++ net.minecraft.world.level.chunk.ChunkAccess chunk = this.level().getChunkIfLoadedImmediately((int) d >> 4, (int) d2 >> 4); ++ if (chunk == null) { ++ return null; ++ } + for (Pose pose : passenger.getDismountPoses()) { + mutableBlockPos.set(d, d1, d2); + double d3 = this.getBoundingBox().maxY + 0.75; + + do { +- double blockFloorHeight = this.level().getBlockFloorHeight(mutableBlockPos); ++ double blockFloorHeight = ((LevelChunk) chunk).getBlockFloorHeight(mutableBlockPos); + if (mutableBlockPos.getY() + blockFloorHeight > d3) { + break; + } +diff --git a/net/minecraft/world/entity/animal/horse/SkeletonHorse.java b/net/minecraft/world/entity/animal/horse/SkeletonHorse.java +index d5ee5b816efc1b3b17b94227f8ea05ab7a2ccad7..93702017d2769d8207864725e73c86cf54cda8ff 100644 +--- a/net/minecraft/world/entity/animal/horse/SkeletonHorse.java ++++ b/net/minecraft/world/entity/animal/horse/SkeletonHorse.java +@@ -108,7 +108,7 @@ public class SkeletonHorse extends AbstractHorse { + + @Override + protected SoundEvent getAmbientSound() { +- return this.isEyeInFluid(FluidTags.WATER) ? SoundEvents.SKELETON_HORSE_AMBIENT_WATER : SoundEvents.SKELETON_HORSE_AMBIENT; ++ return this.isEyeInFluid(1) ? SoundEvents.SKELETON_HORSE_AMBIENT_WATER : SoundEvents.SKELETON_HORSE_AMBIENT; + } + + @Override +diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index f4fa19c6352e44a624e81dc201b1d7d710c2d9d2..3b070af23802dcba9a356ea22e2898ab6099b86b 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -405,7 +405,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { + + @Override + protected boolean canAddPassenger(Entity passenger) { +- return !this.isVehicle() && !this.isEyeInFluid(FluidTags.LAVA); ++ return !this.isVehicle() && !this.isEyeInFluid(2); + } + + @Override +diff --git a/net/minecraft/world/entity/monster/Witch.java b/net/minecraft/world/entity/monster/Witch.java +index e4353c64732067198f082cdd266c1f1ee1fe4f4e..2a4a1d6a94e7712f6c861fe70f65a0a76014ad0b 100644 +--- a/net/minecraft/world/entity/monster/Witch.java ++++ b/net/minecraft/world/entity/monster/Witch.java +@@ -178,7 +178,7 @@ public class Witch extends Raider implements RangedAttackMob { + } + } else { + Holder holder = null; +- if (this.random.nextFloat() < 0.15F && this.isEyeInFluid(FluidTags.WATER) && !this.hasEffect(MobEffects.WATER_BREATHING)) { ++ if (this.random.nextFloat() < 0.15F && this.isEyeInFluid(1) && !this.hasEffect(MobEffects.WATER_BREATHING)) { + holder = Potions.WATER_BREATHING; + } else if (this.random.nextFloat() < 0.15F + && (this.isOnFire() || this.getLastDamageSource() != null && this.getLastDamageSource().is(DamageTypeTags.IS_FIRE)) +diff --git a/net/minecraft/world/entity/monster/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java +index 9c83f1406c0aaad36383a23cebf270b8dc6ced33..2a16248539718d3945d3b2b4d2da161f632831ff 100644 +--- a/net/minecraft/world/entity/monster/Zombie.java ++++ b/net/minecraft/world/entity/monster/Zombie.java +@@ -283,7 +283,7 @@ public class Zombie extends Monster { + this.doUnderWaterConversion(); + } + } else if (this.convertsInWater()) { +- if (this.isEyeInFluid(FluidTags.WATER)) { ++ if (this.isEyeInFluid(1)) { + this.inWaterTime++; + if (this.inWaterTime >= 600) { + this.startUnderWaterConversion(300); +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index 76d85632e1364e71a2aa15dbef41c62422ffd7af..335f99ecd313b4e1ad5b997a84b65331a2253574 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -374,7 +374,7 @@ public abstract class Player extends LivingEntity { + this.lastItemInMainHand = mainHandItem.copy(); + } + +- if (!this.isEyeInFluid(FluidTags.WATER) && this.isEquipped(Items.TURTLE_HELMET)) { ++ if (!this.isEyeInFluid(1) && this.isEquipped(Items.TURTLE_HELMET)) { + this.turtleHelmetTick(); + } + +@@ -414,7 +414,7 @@ public abstract class Player extends LivingEntity { + } + + protected boolean updateIsUnderwater() { +- this.wasUnderwater = this.isEyeInFluid(FluidTags.WATER); ++ this.wasUnderwater = this.isEyeInFluid(1); + return this.wasUnderwater; + } + +@@ -851,7 +851,7 @@ public abstract class Player extends LivingEntity { + } + + destroySpeed *= (float)this.getAttributeValue(Attributes.BLOCK_BREAK_SPEED); +- if (this.isEyeInFluid(FluidTags.WATER)) { ++ if (this.isEyeInFluid(1)) { + destroySpeed *= (float)this.getAttribute(Attributes.SUBMERGED_MINING_SPEED).getValue(); + } + +diff --git a/net/minecraft/world/entity/vehicle/AbstractBoat.java b/net/minecraft/world/entity/vehicle/AbstractBoat.java +index b1b312e45ed4514eaa6fb3941af64b641220c5bd..636b5dfe1761f4d13b6af7b11f610ff5c18b36d6 100644 +--- a/net/minecraft/world/entity/vehicle/AbstractBoat.java ++++ b/net/minecraft/world/entity/vehicle/AbstractBoat.java +@@ -847,7 +847,7 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable { + + @Override + protected boolean canAddPassenger(Entity passenger) { +- return this.getPassengers().size() < this.getMaxPassengers() && !this.isEyeInFluid(FluidTags.WATER); ++ return this.getPassengers().size() < this.getMaxPassengers() && !this.isEyeInFluid(1); + } + + protected int getMaxPassengers() { +diff --git a/net/minecraft/world/level/material/FluidState.java b/net/minecraft/world/level/material/FluidState.java +index 06581fe010ca722d62d0b6d3c44d845f9db0231f..38600ed61757da54295e712a37a3bba3c71fe0a1 100644 +--- a/net/minecraft/world/level/material/FluidState.java ++++ b/net/minecraft/world/level/material/FluidState.java +@@ -160,8 +160,14 @@ public final class FluidState extends StateHolder implements + } + + // Leaf start - Remove stream in updateFluidOnEyes +- public java.util.Set> getTagsAsSet() { +- return this.owner.builtInRegistryHolder().tagsAsSet(); ++ public TagKey[] getTagsArray() { ++ java.util.Set> tags = this.owner.builtInRegistryHolder().tagsAsSet(); ++ TagKey[] array = new TagKey[tags.size()]; ++ int i = 0; ++ for (TagKey tag : tags) { ++ array[i++] = tag; ++ } ++ return array; + } + // Leaf end - Remove stream in updateFluidOnEyes + } diff --git a/leaf-server/paper-patches/features/0042-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch b/leaf-server/paper-patches/features/0042-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch index 77da8702..ea542d1d 100644 --- a/leaf-server/paper-patches/features/0042-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch +++ b/leaf-server/paper-patches/features/0042-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch @@ -1,12 +1,21 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Fri, 6 Jun 2025 10:47:32 +0200 +From: Spottedleaf +Date: Thu, 5 Jun 2025 14:55:57 -0700 Subject: [PATCH] Paper: Update CraftWorld#getForceLoadedChunks to avoid using getChunkAt +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper + +https://github.com/PaperMC/Paper/commit/774c40e71297c6e6d7d417639e1ce61cc79cc5ba + +Usual methods of adding force loaded chunks sync load them, so +they should be loaded already. This should avoid the more expensive +getChunkAt and more importantly allow this function to properly work +on Folia due to avoiding thread checks. diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 236be1bb296d0f080d2a8c739d2678655e81e174..e5ed5bae80aad4ddbea9fca0c0fe00cf95bb6f47 100644 +index 236be1bb296d0f080d2a8c739d2678655e81e174..64d95bc035fbafa660e7f3d7393da4bf8839b2f9 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -657,7 +657,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -14,7 +23,7 @@ index 236be1bb296d0f080d2a8c739d2678655e81e174..e5ed5bae80aad4ddbea9fca0c0fe00cf for (long coord : this.getHandle().getForcedChunks()) { - chunks.add(this.getChunkAt(ChunkPos.getX(coord), ChunkPos.getZ(coord))); -+ chunks.add(new CraftChunk(this.getHandle(), ChunkPos.getX(coord), ChunkPos.getZ(coord))); ++ chunks.add(new CraftChunk(this.getHandle(), ChunkPos.getX(coord), ChunkPos.getZ(coord))); // Paper - Update CraftWorld#getForceLoadedChunks to avoid using getChunkAt } return Collections.unmodifiableCollection(chunks); From afef9c5d73c19ec9cd7a192e74cd174ed90b5fca Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 7 Jun 2025 14:05:08 +0900 Subject: [PATCH 29/57] fix PlayerDoll plugin change connection state --- .../0165-Async-switch-connection-state.patch | 32 +++++++------------ .../features/0179-Paw-optimization.patch | 6 ++-- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch index 7b06eff0..16dfdbb5 100644 --- a/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Async switch connection state diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..c83ee2137a57e62003b1d20c3ceea9f569350a53 100644 +index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..3dce0665e7438d2994a86450e31fb2a10431df9b 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java @@ -342,6 +342,11 @@ public class Connection extends SimpleChannelInboundHandler> { @@ -36,22 +36,11 @@ index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..c83ee2137a57e62003b1d20c3ceea9f5 } } -@@ -369,7 +381,38 @@ public class Connection extends SimpleChannelInboundHandler> { - } +@@ -373,6 +385,31 @@ public class Connection extends SimpleChannelInboundHandler> { + } + } - boolean flag = protocolInfo.id() == ConnectionProtocol.LOGIN; -- syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag))); -+ var cf = this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag)); -+ // Leaf start - Async switch connection state -+ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { -+ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { -+ throw new IllegalStateException("Thread failed netty thread check: Switching outbound protocol state use setupOutboundProtocolAsync instead"); -+ } -+ } -+ syncAfterConfigurationChange(cf); -+ } -+ } -+ ++ // Leaf start - Async switch connection state + public @Nullable ChannelFuture setupOutboundProtocolAsync(ProtocolInfo protocolInfo) { + if (protocolInfo.flow() != this.getSending()) { + throw new IllegalStateException("Invalid outbound protocol: " + protocolInfo.id()); @@ -72,10 +61,13 @@ index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..c83ee2137a57e62003b1d20c3ceea9f5 + return cf; + } + return null; -+ // Leaf end - Async switch connection state - } - } - ++ } ++ } ++ // Leaf end - Async switch connection state ++ + public void setListenerForServerboundHandshake(PacketListener packetListener) { + if (this.packetListener != null) { + throw new IllegalStateException("Listener already set"); diff --git a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java index 2e9eb04c7c4342393c05339906c267bca9ff29b1..53b9daa909c2b89046d5af515e17afe09ea7015a 100644 --- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java diff --git a/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch index 9f4d0724..211e4306 100644 --- a/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch @@ -10,10 +10,10 @@ Some random optimizations - Secret patches (WIP) diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index c83ee2137a57e62003b1d20c3ceea9f569350a53..de1f271d36c7daa10c398e146386b51e2622df9a 100644 +index 3dce0665e7438d2994a86450e31fb2a10431df9b..f9634a821fbfaf31a66e0e25973794149b1a5239 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java -@@ -660,13 +660,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -654,13 +654,7 @@ public class Connection extends SimpleChannelInboundHandler> { if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener) || loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING || Connection.joinAttemptsThisTick++ < MAX_PER_TICK) { @@ -77,7 +77,7 @@ index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194 - // Paper end - detailed watchdog information } diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index b1f1b596a597d559aa672a3cb46a03917ad746af..d61da0fbe7f6c181e4084ce60bfe7dab86f361ad 100644 +index f57f8e610dac80b8095bfc0c7e4b22ff5ad6b13c..39a31abe25d2bb56b28462cf25d2d09f7722526c 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -504,9 +504,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon From ffe613e0e9d636bd38a2568d1eed78b6345360ec Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 7 Jun 2025 14:33:28 +0900 Subject: [PATCH 30/57] fix pwt thread dead --- ...-SparklyPaper-Parallel-world-ticking.patch | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch index fccb0eec..b5a9f1f2 100644 --- a/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch @@ -315,10 +315,10 @@ index af33cab59932f4ec135caf94dc5828930833daf6..caa92e48d031cb54950e6613a82f407d } // Paper end diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java -index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a515b0a5764 100644 +index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933c44268c7 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java -@@ -74,13 +74,98 @@ public class CraftBlock implements Block { +@@ -74,13 +74,96 @@ public class CraftBlock implements Block { return new CraftBlock(world, position); } @@ -345,19 +345,17 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 + return defaultValue; + } + -+ java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); -+ + // Leaf start - SparklyPaper - parallel world ticking - Shutdown handling for async reads -+ if (level.isShuttingDown()) { -+ future.completeExceptionally(new IllegalStateException("World " + level.getWorld().getName() + " is shutting down. Cannot queue new buffered read: " + type)); -+ } else { ++ try { ++ if (level.isShuttingDown()) { ++ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "World " + level.getWorld().getName() + " is shutting down. Cannot queue new buffered read: " + type + " " + methodName, new Throwable()); ++ return defaultValue; ++ } ++ + org.dreeam.leaf.async.world.WorldReadRequest request = new org.dreeam.leaf.async.world.WorldReadRequest(type, params, future); + level.asyncReadRequestQueue.offer(request); // Assumes queue exists on ServerLevel -+ } ++ Object result = request.future().join(); // Block until tick thread completes it + // Leaf end - SparklyPaper - parallel world ticking - Shutdown handling for async reads -+ -+ try { -+ Object result = future.join(); // Block until tick thread completes it + if (result == null) { + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "PWT: Buffered async read returned null for " + methodName + " - returning default."); + return defaultValue; @@ -419,7 +417,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 } // Paper end -@@ -144,10 +229,12 @@ public class CraftBlock implements Block { +@@ -144,10 +227,12 @@ public class CraftBlock implements Block { return this.getWorld().getChunkAt(this); } @@ -432,7 +430,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 public void setData(final byte data, boolean applyPhysics) { if (applyPhysics) { this.setData(data, 3); -@@ -157,12 +244,18 @@ public class CraftBlock implements Block { +@@ -157,12 +242,18 @@ public class CraftBlock implements Block { } private void setData(final byte data, int flag) { @@ -452,7 +450,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 return CraftMagicNumbers.toLegacyData(blockData); } -@@ -179,6 +272,7 @@ public class CraftBlock implements Block { +@@ -179,6 +270,7 @@ public class CraftBlock implements Block { @Override public void setType(Material type, boolean applyPhysics) { Preconditions.checkArgument(type != null, "Material cannot be null"); @@ -460,7 +458,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 this.setBlockData(type.createBlockData(), applyPhysics); } -@@ -198,6 +292,12 @@ public class CraftBlock implements Block { +@@ -198,6 +290,12 @@ public class CraftBlock implements Block { } public static boolean setTypeAndData(LevelAccessor world, BlockPos position, net.minecraft.world.level.block.state.BlockState old, net.minecraft.world.level.block.state.BlockState blockData, boolean applyPhysics) { @@ -473,7 +471,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 // SPIGOT-611: need to do this to prevent glitchiness. Easier to handle this here (like /setblock) than to fix weirdness in tile entity cleanup if (old.hasBlockEntity() && blockData.getBlock() != old.getBlock()) { // SPIGOT-3725 remove old tile entity if block changes // SPIGOT-4612: faster - just clear tile -@@ -226,22 +326,62 @@ public class CraftBlock implements Block { +@@ -226,22 +324,62 @@ public class CraftBlock implements Block { @Override public Material getType() { @@ -540,7 +538,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 } public Block getFace(final BlockFace face) { -@@ -282,51 +422,36 @@ public class CraftBlock implements Block { +@@ -282,51 +420,36 @@ public class CraftBlock implements Block { @Override public String toString() { @@ -616,7 +614,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 } @Override -@@ -343,18 +468,65 @@ public class CraftBlock implements Block { +@@ -343,18 +466,65 @@ public class CraftBlock implements Block { @Override public Biome getBiome() { @@ -684,7 +682,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio); } -@@ -370,12 +542,50 @@ public class CraftBlock implements Block { +@@ -370,12 +540,50 @@ public class CraftBlock implements Block { @Override public boolean isBlockPowered() { @@ -737,7 +735,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 } @Override -@@ -397,46 +607,101 @@ public class CraftBlock implements Block { +@@ -397,46 +605,101 @@ public class CraftBlock implements Block { @Override public boolean isBlockFacePowered(BlockFace face) { @@ -866,7 +864,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 @Override public int getBlockPower() { -@@ -479,105 +744,179 @@ public class CraftBlock implements Block { +@@ -479,105 +742,179 @@ public class CraftBlock implements Block { @Override public PistonMoveReaction getPistonMoveReaction() { @@ -953,14 +951,14 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 + int eventData = (eventId == net.minecraft.world.level.block.LevelEvent.PARTICLES_DESTROY_BLOCK) + ? net.minecraft.world.level.block.Block.getId(iblockdata) : 0; + this.world.levelEvent(eventId, this.position, eventData); - } ++ } + // Drop experience using ServerLevel + if (dropExperience) { + int xp = block.getExpDrop(iblockdata, serverLevelForDrops, this.position, nmsItem, true); + if (xp > 0) { // Only pop if there's XP to drop + block.popExperience(serverLevelForDrops, this.position, xp); + } -+ } + } + droppedItems = true; + } + } else { @@ -1087,7 +1085,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 } @Override -@@ -592,31 +931,70 @@ public class CraftBlock implements Block { +@@ -592,31 +929,70 @@ public class CraftBlock implements Block { @Override public Collection getDrops(ItemStack item, Entity entity) { @@ -1168,7 +1166,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 @Override public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { this.getCraftWorld().getBlockMetadata().setMetadata(this, metadataKey, newMetadataValue); -@@ -639,57 +1017,147 @@ public class CraftBlock implements Block { +@@ -639,57 +1015,147 @@ public class CraftBlock implements Block { @Override public boolean isPassable() { @@ -1340,7 +1338,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 } @Override -@@ -700,7 +1168,10 @@ public class CraftBlock implements Block { +@@ -700,7 +1166,10 @@ public class CraftBlock implements Block { // Paper start @Override public com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup() { @@ -1352,7 +1350,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a51 } @Override -@@ -713,26 +1184,76 @@ public class CraftBlock implements Block { +@@ -713,26 +1182,76 @@ public class CraftBlock implements Block { return this.getNMS().getBlock().getDescriptionId(); } From 5e570748a7070847b7f77e517395ada3c3e20ece Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 7 Jun 2025 14:44:37 +0900 Subject: [PATCH 31/57] Revert "fix pwt thread dead" This reverts commit ffe613e0e9d636bd38a2568d1eed78b6345360ec. --- ...-SparklyPaper-Parallel-world-ticking.patch | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch index b5a9f1f2..fccb0eec 100644 --- a/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/paper-patches/features/0031-SparklyPaper-Parallel-world-ticking.patch @@ -315,10 +315,10 @@ index af33cab59932f4ec135caf94dc5828930833daf6..caa92e48d031cb54950e6613a82f407d } // Paper end diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java -index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933c44268c7 100644 +index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..839cfe99c028c596e15ba63c8a6e4a515b0a5764 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java -@@ -74,13 +74,96 @@ public class CraftBlock implements Block { +@@ -74,13 +74,98 @@ public class CraftBlock implements Block { return new CraftBlock(world, position); } @@ -345,17 +345,19 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 + return defaultValue; + } + -+ // Leaf start - SparklyPaper - parallel world ticking - Shutdown handling for async reads -+ try { -+ if (level.isShuttingDown()) { -+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "World " + level.getWorld().getName() + " is shutting down. Cannot queue new buffered read: " + type + " " + methodName, new Throwable()); -+ return defaultValue; -+ } ++ java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); + ++ // Leaf start - SparklyPaper - parallel world ticking - Shutdown handling for async reads ++ if (level.isShuttingDown()) { ++ future.completeExceptionally(new IllegalStateException("World " + level.getWorld().getName() + " is shutting down. Cannot queue new buffered read: " + type)); ++ } else { + org.dreeam.leaf.async.world.WorldReadRequest request = new org.dreeam.leaf.async.world.WorldReadRequest(type, params, future); + level.asyncReadRequestQueue.offer(request); // Assumes queue exists on ServerLevel -+ Object result = request.future().join(); // Block until tick thread completes it ++ } + // Leaf end - SparklyPaper - parallel world ticking - Shutdown handling for async reads ++ ++ try { ++ Object result = future.join(); // Block until tick thread completes it + if (result == null) { + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "PWT: Buffered async read returned null for " + methodName + " - returning default."); + return defaultValue; @@ -417,7 +419,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 } // Paper end -@@ -144,10 +227,12 @@ public class CraftBlock implements Block { +@@ -144,10 +229,12 @@ public class CraftBlock implements Block { return this.getWorld().getChunkAt(this); } @@ -430,7 +432,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 public void setData(final byte data, boolean applyPhysics) { if (applyPhysics) { this.setData(data, 3); -@@ -157,12 +242,18 @@ public class CraftBlock implements Block { +@@ -157,12 +244,18 @@ public class CraftBlock implements Block { } private void setData(final byte data, int flag) { @@ -450,7 +452,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 return CraftMagicNumbers.toLegacyData(blockData); } -@@ -179,6 +270,7 @@ public class CraftBlock implements Block { +@@ -179,6 +272,7 @@ public class CraftBlock implements Block { @Override public void setType(Material type, boolean applyPhysics) { Preconditions.checkArgument(type != null, "Material cannot be null"); @@ -458,7 +460,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 this.setBlockData(type.createBlockData(), applyPhysics); } -@@ -198,6 +290,12 @@ public class CraftBlock implements Block { +@@ -198,6 +292,12 @@ public class CraftBlock implements Block { } public static boolean setTypeAndData(LevelAccessor world, BlockPos position, net.minecraft.world.level.block.state.BlockState old, net.minecraft.world.level.block.state.BlockState blockData, boolean applyPhysics) { @@ -471,7 +473,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 // SPIGOT-611: need to do this to prevent glitchiness. Easier to handle this here (like /setblock) than to fix weirdness in tile entity cleanup if (old.hasBlockEntity() && blockData.getBlock() != old.getBlock()) { // SPIGOT-3725 remove old tile entity if block changes // SPIGOT-4612: faster - just clear tile -@@ -226,22 +324,62 @@ public class CraftBlock implements Block { +@@ -226,22 +326,62 @@ public class CraftBlock implements Block { @Override public Material getType() { @@ -538,7 +540,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 } public Block getFace(final BlockFace face) { -@@ -282,51 +420,36 @@ public class CraftBlock implements Block { +@@ -282,51 +422,36 @@ public class CraftBlock implements Block { @Override public String toString() { @@ -614,7 +616,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 } @Override -@@ -343,18 +466,65 @@ public class CraftBlock implements Block { +@@ -343,18 +468,65 @@ public class CraftBlock implements Block { @Override public Biome getBiome() { @@ -682,7 +684,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio); } -@@ -370,12 +540,50 @@ public class CraftBlock implements Block { +@@ -370,12 +542,50 @@ public class CraftBlock implements Block { @Override public boolean isBlockPowered() { @@ -735,7 +737,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 } @Override -@@ -397,46 +605,101 @@ public class CraftBlock implements Block { +@@ -397,46 +607,101 @@ public class CraftBlock implements Block { @Override public boolean isBlockFacePowered(BlockFace face) { @@ -864,7 +866,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 @Override public int getBlockPower() { -@@ -479,105 +742,179 @@ public class CraftBlock implements Block { +@@ -479,105 +744,179 @@ public class CraftBlock implements Block { @Override public PistonMoveReaction getPistonMoveReaction() { @@ -951,14 +953,14 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 + int eventData = (eventId == net.minecraft.world.level.block.LevelEvent.PARTICLES_DESTROY_BLOCK) + ? net.minecraft.world.level.block.Block.getId(iblockdata) : 0; + this.world.levelEvent(eventId, this.position, eventData); -+ } + } + // Drop experience using ServerLevel + if (dropExperience) { + int xp = block.getExpDrop(iblockdata, serverLevelForDrops, this.position, nmsItem, true); + if (xp > 0) { // Only pop if there's XP to drop + block.popExperience(serverLevelForDrops, this.position, xp); + } - } ++ } + droppedItems = true; + } + } else { @@ -1085,7 +1087,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 } @Override -@@ -592,31 +929,70 @@ public class CraftBlock implements Block { +@@ -592,31 +931,70 @@ public class CraftBlock implements Block { @Override public Collection getDrops(ItemStack item, Entity entity) { @@ -1166,7 +1168,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 @Override public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { this.getCraftWorld().getBlockMetadata().setMetadata(this, metadataKey, newMetadataValue); -@@ -639,57 +1015,147 @@ public class CraftBlock implements Block { +@@ -639,57 +1017,147 @@ public class CraftBlock implements Block { @Override public boolean isPassable() { @@ -1338,7 +1340,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 } @Override -@@ -700,7 +1166,10 @@ public class CraftBlock implements Block { +@@ -700,7 +1168,10 @@ public class CraftBlock implements Block { // Paper start @Override public com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup() { @@ -1350,7 +1352,7 @@ index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..1e6042dfc768e208b2fe5738bce2f933 } @Override -@@ -713,26 +1182,76 @@ public class CraftBlock implements Block { +@@ -713,26 +1184,76 @@ public class CraftBlock implements Block { return this.getNMS().getBlock().getDescriptionId(); } From 0c09ca7a6626b5e3dcdc73509552239594937f00 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sat, 7 Jun 2025 17:48:46 +0800 Subject: [PATCH 32/57] Drop configurable smooth teleport --- .../server}/0100-Smooth-teleport-config.patch | 2 + ...read-safe-ban-list-date-format-pars.patch} | 0 ...tartEachNonRunningBehavior-in-Brain.patch} | 0 ... => 0102-Lithium-equipment-tracking.patch} | 2 +- ...> 0103-C2ME-Optimize-world-gen-math.patch} | 0 ...k-key.patch => 0104-Cache-chunk-key.patch} | 0 ...0105-Cache-random-tick-block-status.patch} | 0 ...6-Cache-part-of-canHoldFluid-result.patch} | 0 ... => 0107-Configurable-tripwire-dupe.patch} | 0 ...7075-Block-Entities-Unload-Lag-Spik.patch} | 0 ...Rearrange-the-attackable-conditions.patch} | 0 ...-dirty-stats-copy-when-requesting-p.patch} | 0 ...t-dirty-flag-when-loading-maps-from.patch} | 2 +- ...hecking-nearby-players-for-spawning.patch} | 2 +- ...> 0113-Cache-supporting-block-check.patch} | 2 +- ...ue-clear-on-LevelTicks-cleanupAfter.patch} | 0 ...ivity-maps-with-optimized-collectio.patch} | 0 ... => 0116-Remove-stream-in-villagers.patch} | 0 ... 0117-Optimize-baby-villager-sensor.patch} | 0 ....patch => 0118-Only-player-pushable.patch} | 2 +- ...e-iterators-from-Inventory-contains.patch} | 0 ...=> 0120-Alternative-Brain-Behaviour.patch} | 0 ...eligible-players-for-despawn-checks.patch} | 4 +- ...-Slightly-optimise-getNearestPlayer.patch} | 0 ...y-to-pre-populate-the-size-of-ticki.patch} | 2 +- ...pre-filtered-ticking-chunks-list-as.patch} | 2 +- ...writeLongArray-during-chunk-loading.patch} | 0 ...6-Improve-sorting-in-SortedArraySet.patch} | 0 ... 0127-Make-removeIf-slightly-faster.patch} | 0 ...atch => 0128-Optimize-LinearPalette.patch} | 0 ...129-Slightly-optimized-VarInt-write.patch} | 0 ...te-ClientboundLightUpdatePacketData.patch} | 0 ...send.patch => 0131-Async-chunk-send.patch} | 0 ...atch => 0132-Spawner-Configurations.patch} | 0 ...SparklyPaper-Parallel-world-ticking.patch} | 8 +- ...-SparklyPaper-Track-each-world-MSPT.patch} | 0 ...lled-Projectile-Events-still-consum.patch} | 0 ...ndInteract-and-NearestVisibleLiving.patch} | 0 ...emove-streams-on-InsideBrownianWalk.patch} | 0 ...=> 0138-Use-BFS-on-getSlopeDistance.patch} | 0 ...r-PR-Throttle-failed-spawn-attempts.patch} | 0 ...BlockEntity-ticking-isRemoved-check.patch} | 0 ...1-Raytrace-AntiXray-SDK-integration.patch} | 0 ...timize-addOrUpdateTransientModifier.patch} | 2 +- ... => 0143-Optimize-ContextMap.create.patch} | 0 ...Micro-optimizations-for-random-tick.patch} | 0 ...n-updateConnectedPlayersWithinRange.patch} | 0 ...46-Remove-streams-on-PlayerDetector.patch} | 0 ...se-direct-iteration-on-Sensing.tick.patch} | 0 ...8-Optimise-non-flush-packet-sending.patch} | 0 ...unk-retrieving-in-entity-fluid-push.patch} | 0 ...-Null-handling-on-MultifaceSpreader.patch} | 0 .../features/0151-More-virtual-threads.patch | 52 ++++++++ ....patch => 0152-Async-target-finding.patch} | 0 .../features/0152-More-virtual-threads.patch | 112 ------------------ ...imize-ThreadedTicketLevelPropagator.patch} | 0 ...EffectUtil-getDigSpeedAmplification.patch} | 0 ...patch => 0155-Optimise-chunkUnloads.patch} | 0 ...56-Optimize-BlockEntityType-isValid.patch} | 0 ...t-on-player-join-to-avoid-chunk-loa.patch} | 2 +- ...rPR-Fix-save-load-NaN-Entity-Motion.patch} | 0 ...erPR-Fix-unnecessary-map-data-saves.patch} | 0 ...heck-inside-blocks-and-traverse-blo.patch} | 0 ...yList-implementation-to-BasicEntity.patch} | 0 ...ol-Core.patch => 0162-Protocol-Core.patch} | 4 +- ... => 0163-Reduce-PlayerChunk-Updates.patch} | 0 ... 0164-Async-switch-connection-state.patch} | 0 ...timise-BlockEntities-tickersInLevel.patch} | 0 ...e-cactus-can-even-survive-being-pla.patch} | 0 ...0167-Flush-location-while-knockback.patch} | 0 ...tch => 0168-Only-tick-items-at-hand.patch} | 2 +- ...art-sort-items-in-NearestItemSensor.patch} | 0 ...170-Optimise-player-movement-checks.patch} | 0 ...=> 0171-Remove-streams-in-MobSensor.patch} | 0 ...72-Remove-streams-in-TemptingSensor.patch} | 0 ...se-HashedList-on-WeightedRandomList.patch} | 0 ...-death-item-drop-knockback-settings.patch} | 4 +- ...-Optimize-getScaledTrackingDistance.patch} | 0 ...ptimize-SynchedEntityData-packDirty.patch} | 0 ...patch => 0177-Optimize-isEyeInFluid.patch} | 0 ...patch => 0178-Cache-block-path-type.patch} | 0 ...ch => 0179-optimize-getEntityStatus.patch} | 2 +- ...on-optimized-PoweredRailBlock-logic.patch} | 0 ...1-optimise-ChunkGenerator-getMobsAt.patch} | 0 ...ome.patch => 0182-optimise-getBiome.patch} | 0 ...patch => 0183-optimize-mob-spawning.patch} | 4 +- ...atch => 0184-optimize-structure-map.patch} | 0 ...patch => 0185-throttle-mob-spawning.patch} | 0 ... 0186-preload-mob-spawning-position.patch} | 0 ... => 0187-Add-BlockExplosionHitEvent.patch} | 0 ...last-Protection-explosion-knockback.patch} | 0 ...tion.patch => 0189-Paw-optimization.patch} | 38 ++++-- .../{0191-aaa.patch => 0190-aaa.patch} | 4 +- .../modules/gameplay/SmoothTeleport.java | 28 ----- .../config/modules/opt/VT4DownloadPool.java | 21 ---- 95 files changed, 109 insertions(+), 194 deletions(-) rename {leaf-server/minecraft-patches/features => leaf-archived-patches/removed/hardfork/server}/0100-Smooth-teleport-config.patch (99%) rename leaf-server/minecraft-patches/features/{0101-Use-faster-and-thread-safe-ban-list-date-format-pars.patch => 0100-Use-faster-and-thread-safe-ban-list-date-format-pars.patch} (100%) rename leaf-server/minecraft-patches/features/{0102-Collect-then-startEachNonRunningBehavior-in-Brain.patch => 0101-Collect-then-startEachNonRunningBehavior-in-Brain.patch} (100%) rename leaf-server/minecraft-patches/features/{0103-Lithium-equipment-tracking.patch => 0102-Lithium-equipment-tracking.patch} (99%) rename leaf-server/minecraft-patches/features/{0104-C2ME-Optimize-world-gen-math.patch => 0103-C2ME-Optimize-world-gen-math.patch} (100%) rename leaf-server/minecraft-patches/features/{0105-Cache-chunk-key.patch => 0104-Cache-chunk-key.patch} (100%) rename leaf-server/minecraft-patches/features/{0106-Cache-random-tick-block-status.patch => 0105-Cache-random-tick-block-status.patch} (100%) rename leaf-server/minecraft-patches/features/{0107-Cache-part-of-canHoldFluid-result.patch => 0106-Cache-part-of-canHoldFluid-result.patch} (100%) rename leaf-server/minecraft-patches/features/{0108-Configurable-tripwire-dupe.patch => 0107-Configurable-tripwire-dupe.patch} (100%) rename leaf-server/minecraft-patches/features/{0109-PaperPR-Fix-MC-117075-Block-Entities-Unload-Lag-Spik.patch => 0108-PaperPR-Fix-MC-117075-Block-Entities-Unload-Lag-Spik.patch} (100%) rename leaf-server/minecraft-patches/features/{0110-Sepals-Rearrange-the-attackable-conditions.patch => 0109-Sepals-Rearrange-the-attackable-conditions.patch} (100%) rename leaf-server/minecraft-patches/features/{0111-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch => 0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch} (100%) rename leaf-server/minecraft-patches/features/{0112-SparklyPaper-Reset-dirty-flag-when-loading-maps-from.patch => 0111-SparklyPaper-Reset-dirty-flag-when-loading-maps-from.patch} (91%) rename leaf-server/minecraft-patches/features/{0113-Optimize-checking-nearby-players-for-spawning.patch => 0112-Optimize-checking-nearby-players-for-spawning.patch} (97%) rename leaf-server/minecraft-patches/features/{0114-Cache-supporting-block-check.patch => 0113-Cache-supporting-block-check.patch} (96%) rename leaf-server/minecraft-patches/features/{0115-Avoid-useless-deque-clear-on-LevelTicks-cleanupAfter.patch => 0114-Avoid-useless-deque-clear-on-LevelTicks-cleanupAfter.patch} (100%) rename leaf-server/minecraft-patches/features/{0116-Replace-brain-activity-maps-with-optimized-collectio.patch => 0115-Replace-brain-activity-maps-with-optimized-collectio.patch} (100%) rename leaf-server/minecraft-patches/features/{0117-Remove-stream-in-villagers.patch => 0116-Remove-stream-in-villagers.patch} (100%) rename leaf-server/minecraft-patches/features/{0118-Optimize-baby-villager-sensor.patch => 0117-Optimize-baby-villager-sensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0119-Only-player-pushable.patch => 0118-Only-player-pushable.patch} (98%) rename leaf-server/minecraft-patches/features/{0120-Remove-iterators-from-Inventory-contains.patch => 0119-Remove-iterators-from-Inventory-contains.patch} (100%) rename leaf-server/minecraft-patches/features/{0121-Alternative-Brain-Behaviour.patch => 0120-Alternative-Brain-Behaviour.patch} (100%) rename leaf-server/minecraft-patches/features/{0122-Cache-eligible-players-for-despawn-checks.patch => 0121-Cache-eligible-players-for-despawn-checks.patch} (96%) rename leaf-server/minecraft-patches/features/{0123-Slightly-optimise-getNearestPlayer.patch => 0122-Slightly-optimise-getNearestPlayer.patch} (100%) rename leaf-server/minecraft-patches/features/{0124-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch => 0123-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch} (92%) rename leaf-server/minecraft-patches/features/{0125-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch => 0124-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch} (93%) rename leaf-server/minecraft-patches/features/{0126-Bulk-writes-to-writeLongArray-during-chunk-loading.patch => 0125-Bulk-writes-to-writeLongArray-during-chunk-loading.patch} (100%) rename leaf-server/minecraft-patches/features/{0127-Improve-sorting-in-SortedArraySet.patch => 0126-Improve-sorting-in-SortedArraySet.patch} (100%) rename leaf-server/minecraft-patches/features/{0128-Make-removeIf-slightly-faster.patch => 0127-Make-removeIf-slightly-faster.patch} (100%) rename leaf-server/minecraft-patches/features/{0129-Optimize-LinearPalette.patch => 0128-Optimize-LinearPalette.patch} (100%) rename leaf-server/minecraft-patches/features/{0130-Slightly-optimized-VarInt-write.patch => 0129-Slightly-optimized-VarInt-write.patch} (100%) rename leaf-server/minecraft-patches/features/{0131-Rewrite-ClientboundLightUpdatePacketData.patch => 0130-Rewrite-ClientboundLightUpdatePacketData.patch} (100%) rename leaf-server/minecraft-patches/features/{0132-Async-chunk-send.patch => 0131-Async-chunk-send.patch} (100%) rename leaf-server/minecraft-patches/features/{0133-Spawner-Configurations.patch => 0132-Spawner-Configurations.patch} (100%) rename leaf-server/minecraft-patches/features/{0134-SparklyPaper-Parallel-world-ticking.patch => 0133-SparklyPaper-Parallel-world-ticking.patch} (99%) rename leaf-server/minecraft-patches/features/{0135-SparklyPaper-Track-each-world-MSPT.patch => 0134-SparklyPaper-Track-each-world-MSPT.patch} (100%) rename leaf-server/minecraft-patches/features/{0136-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch => 0135-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch} (100%) rename leaf-server/minecraft-patches/features/{0137-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch => 0136-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch} (100%) rename leaf-server/minecraft-patches/features/{0138-Remove-streams-on-InsideBrownianWalk.patch => 0137-Remove-streams-on-InsideBrownianWalk.patch} (100%) rename leaf-server/minecraft-patches/features/{0139-Use-BFS-on-getSlopeDistance.patch => 0138-Use-BFS-on-getSlopeDistance.patch} (100%) rename leaf-server/minecraft-patches/features/{0140-Paper-PR-Throttle-failed-spawn-attempts.patch => 0139-Paper-PR-Throttle-failed-spawn-attempts.patch} (100%) rename leaf-server/minecraft-patches/features/{0141-Improve-BlockEntity-ticking-isRemoved-check.patch => 0140-Improve-BlockEntity-ticking-isRemoved-check.patch} (100%) rename leaf-server/minecraft-patches/features/{0142-Raytrace-AntiXray-SDK-integration.patch => 0141-Raytrace-AntiXray-SDK-integration.patch} (100%) rename leaf-server/minecraft-patches/features/{0143-Optimize-addOrUpdateTransientModifier.patch => 0142-Optimize-addOrUpdateTransientModifier.patch} (93%) rename leaf-server/minecraft-patches/features/{0144-Optimize-ContextMap.create.patch => 0143-Optimize-ContextMap.create.patch} (100%) rename leaf-server/minecraft-patches/features/{0145-Micro-optimizations-for-random-tick.patch => 0144-Micro-optimizations-for-random-tick.patch} (100%) rename leaf-server/minecraft-patches/features/{0146-Remove-streams-on-updateConnectedPlayersWithinRange.patch => 0145-Remove-streams-on-updateConnectedPlayersWithinRange.patch} (100%) rename leaf-server/minecraft-patches/features/{0147-Remove-streams-on-PlayerDetector.patch => 0146-Remove-streams-on-PlayerDetector.patch} (100%) rename leaf-server/minecraft-patches/features/{0148-Use-direct-iteration-on-Sensing.tick.patch => 0147-Use-direct-iteration-on-Sensing.tick.patch} (100%) rename leaf-server/minecraft-patches/features/{0149-Optimise-non-flush-packet-sending.patch => 0148-Optimise-non-flush-packet-sending.patch} (100%) rename leaf-server/minecraft-patches/features/{0150-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch => 0149-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch} (100%) rename leaf-server/minecraft-patches/features/{0151-Null-handling-on-MultifaceSpreader.patch => 0150-Null-handling-on-MultifaceSpreader.patch} (100%) create mode 100644 leaf-server/minecraft-patches/features/0151-More-virtual-threads.patch rename leaf-server/minecraft-patches/features/{0153-Async-target-finding.patch => 0152-Async-target-finding.patch} (100%) delete mode 100644 leaf-server/minecraft-patches/features/0152-More-virtual-threads.patch rename leaf-server/minecraft-patches/features/{0154-Optimize-ThreadedTicketLevelPropagator.patch => 0153-Optimize-ThreadedTicketLevelPropagator.patch} (100%) rename leaf-server/minecraft-patches/features/{0155-Optimise-MobEffectUtil-getDigSpeedAmplification.patch => 0154-Optimise-MobEffectUtil-getDigSpeedAmplification.patch} (100%) rename leaf-server/minecraft-patches/features/{0156-Optimise-chunkUnloads.patch => 0155-Optimise-chunkUnloads.patch} (100%) rename leaf-server/minecraft-patches/features/{0157-Optimize-BlockEntityType-isValid.patch => 0156-Optimize-BlockEntityType-isValid.patch} (100%) rename leaf-server/minecraft-patches/features/{0158-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch => 0157-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch} (97%) rename leaf-server/minecraft-patches/features/{0159-PaperPR-Fix-save-load-NaN-Entity-Motion.patch => 0158-PaperPR-Fix-save-load-NaN-Entity-Motion.patch} (100%) rename leaf-server/minecraft-patches/features/{0160-PaperPR-Fix-unnecessary-map-data-saves.patch => 0159-PaperPR-Fix-unnecessary-map-data-saves.patch} (100%) rename leaf-server/minecraft-patches/features/{0161-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch => 0160-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch} (100%) rename leaf-server/minecraft-patches/features/{0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch => 0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch} (100%) rename leaf-server/minecraft-patches/features/{0163-Protocol-Core.patch => 0162-Protocol-Core.patch} (95%) rename leaf-server/minecraft-patches/features/{0164-Reduce-PlayerChunk-Updates.patch => 0163-Reduce-PlayerChunk-Updates.patch} (100%) rename leaf-server/minecraft-patches/features/{0165-Async-switch-connection-state.patch => 0164-Async-switch-connection-state.patch} (100%) rename leaf-server/minecraft-patches/features/{0166-Optimise-BlockEntities-tickersInLevel.patch => 0165-Optimise-BlockEntities-tickersInLevel.patch} (100%) rename leaf-server/minecraft-patches/features/{0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch => 0166-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch} (100%) rename leaf-server/minecraft-patches/features/{0168-Flush-location-while-knockback.patch => 0167-Flush-location-while-knockback.patch} (100%) rename leaf-server/minecraft-patches/features/{0169-Only-tick-items-at-hand.patch => 0168-Only-tick-items-at-hand.patch} (96%) rename leaf-server/minecraft-patches/features/{0170-Smart-sort-items-in-NearestItemSensor.patch => 0169-Smart-sort-items-in-NearestItemSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0171-Optimise-player-movement-checks.patch => 0170-Optimise-player-movement-checks.patch} (100%) rename leaf-server/minecraft-patches/features/{0172-Remove-streams-in-MobSensor.patch => 0171-Remove-streams-in-MobSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0173-Remove-streams-in-TemptingSensor.patch => 0172-Remove-streams-in-TemptingSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0174-Use-HashedList-on-WeightedRandomList.patch => 0173-Use-HashedList-on-WeightedRandomList.patch} (100%) rename leaf-server/minecraft-patches/features/{0175-Add-configurable-death-item-drop-knockback-settings.patch => 0174-Add-configurable-death-item-drop-knockback-settings.patch} (93%) rename leaf-server/minecraft-patches/features/{0176-Optimize-getScaledTrackingDistance.patch => 0175-Optimize-getScaledTrackingDistance.patch} (100%) rename leaf-server/minecraft-patches/features/{0177-Optimize-SynchedEntityData-packDirty.patch => 0176-Optimize-SynchedEntityData-packDirty.patch} (100%) rename leaf-server/minecraft-patches/features/{0178-Optimize-isEyeInFluid.patch => 0177-Optimize-isEyeInFluid.patch} (100%) rename leaf-server/minecraft-patches/features/{0180-Cache-block-path-type.patch => 0178-Cache-block-path-type.patch} (100%) rename leaf-server/minecraft-patches/features/{0181-optimize-getEntityStatus.patch => 0179-optimize-getEntityStatus.patch} (96%) rename leaf-server/minecraft-patches/features/{0182-Rail-Optimization-optimized-PoweredRailBlock-logic.patch => 0180-Rail-Optimization-optimized-PoweredRailBlock-logic.patch} (100%) rename leaf-server/minecraft-patches/features/{0183-optimise-ChunkGenerator-getMobsAt.patch => 0181-optimise-ChunkGenerator-getMobsAt.patch} (100%) rename leaf-server/minecraft-patches/features/{0184-optimise-getBiome.patch => 0182-optimise-getBiome.patch} (100%) rename leaf-server/minecraft-patches/features/{0185-optimize-mob-spawning.patch => 0183-optimize-mob-spawning.patch} (98%) rename leaf-server/minecraft-patches/features/{0186-optimize-structure-map.patch => 0184-optimize-structure-map.patch} (100%) rename leaf-server/minecraft-patches/features/{0187-throttle-mob-spawning.patch => 0185-throttle-mob-spawning.patch} (100%) rename leaf-server/minecraft-patches/features/{0188-preload-mob-spawning-position.patch => 0186-preload-mob-spawning-position.patch} (100%) rename leaf-server/minecraft-patches/features/{0189-Add-BlockExplosionHitEvent.patch => 0187-Add-BlockExplosionHitEvent.patch} (100%) rename leaf-server/minecraft-patches/features/{0190-Old-Blast-Protection-explosion-knockback.patch => 0188-Old-Blast-Protection-explosion-knockback.patch} (100%) rename leaf-server/minecraft-patches/features/{0179-Paw-optimization.patch => 0189-Paw-optimization.patch} (84%) rename leaf-server/minecraft-patches/features/{0191-aaa.patch => 0190-aaa.patch} (98%) delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SmoothTeleport.java delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java diff --git a/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch b/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch similarity index 99% rename from leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch rename to leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch index aef1c4e6..30429f8d 100644 --- a/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch +++ b/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch @@ -3,6 +3,8 @@ From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Tue, 9 Nov 2077 00:00:00 +0800 Subject: [PATCH] Smooth teleport config +Removed since Leaf 1.21.4 + This abuses some of how Minecraft works and attempts to teleport a player to another world without triggering typical respawn packets. All of natural state of chunk resends, entity adds/removes, etc still happen but the visual "refresh" of a world change is hidden. Depending on the destination location/world, diff --git a/leaf-server/minecraft-patches/features/0101-Use-faster-and-thread-safe-ban-list-date-format-pars.patch b/leaf-server/minecraft-patches/features/0100-Use-faster-and-thread-safe-ban-list-date-format-pars.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0101-Use-faster-and-thread-safe-ban-list-date-format-pars.patch rename to leaf-server/minecraft-patches/features/0100-Use-faster-and-thread-safe-ban-list-date-format-pars.patch diff --git a/leaf-server/minecraft-patches/features/0102-Collect-then-startEachNonRunningBehavior-in-Brain.patch b/leaf-server/minecraft-patches/features/0101-Collect-then-startEachNonRunningBehavior-in-Brain.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0102-Collect-then-startEachNonRunningBehavior-in-Brain.patch rename to leaf-server/minecraft-patches/features/0101-Collect-then-startEachNonRunningBehavior-in-Brain.patch diff --git a/leaf-server/minecraft-patches/features/0103-Lithium-equipment-tracking.patch b/leaf-server/minecraft-patches/features/0102-Lithium-equipment-tracking.patch similarity index 99% rename from leaf-server/minecraft-patches/features/0103-Lithium-equipment-tracking.patch rename to leaf-server/minecraft-patches/features/0102-Lithium-equipment-tracking.patch index 25b5eb45..492f73c1 100644 --- a/leaf-server/minecraft-patches/features/0103-Lithium-equipment-tracking.patch +++ b/leaf-server/minecraft-patches/features/0102-Lithium-equipment-tracking.patch @@ -76,7 +76,7 @@ index a8c6549f772208cd543607224fef2c2389b14f24..709631db548a16a969a373e26ebbcd69 public boolean equals(Object other) { return this == other diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index eb79e6984810410c646d7b1910694d7df75dccba..69e91a732a497b2ffa906088a636947ce9a9ec36 100644 +index cbd68d1d5b92e426062776658a6bf525553ecb1b..668722c44bac3a9731d175a93aad8435e0c2c1c0 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java @@ -159,7 +159,7 @@ import org.bukkit.event.entity.EntityTeleportEvent; diff --git a/leaf-server/minecraft-patches/features/0104-C2ME-Optimize-world-gen-math.patch b/leaf-server/minecraft-patches/features/0103-C2ME-Optimize-world-gen-math.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0104-C2ME-Optimize-world-gen-math.patch rename to leaf-server/minecraft-patches/features/0103-C2ME-Optimize-world-gen-math.patch diff --git a/leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch b/leaf-server/minecraft-patches/features/0104-Cache-chunk-key.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch rename to leaf-server/minecraft-patches/features/0104-Cache-chunk-key.patch diff --git a/leaf-server/minecraft-patches/features/0106-Cache-random-tick-block-status.patch b/leaf-server/minecraft-patches/features/0105-Cache-random-tick-block-status.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0106-Cache-random-tick-block-status.patch rename to leaf-server/minecraft-patches/features/0105-Cache-random-tick-block-status.patch diff --git a/leaf-server/minecraft-patches/features/0107-Cache-part-of-canHoldFluid-result.patch b/leaf-server/minecraft-patches/features/0106-Cache-part-of-canHoldFluid-result.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0107-Cache-part-of-canHoldFluid-result.patch rename to leaf-server/minecraft-patches/features/0106-Cache-part-of-canHoldFluid-result.patch diff --git a/leaf-server/minecraft-patches/features/0108-Configurable-tripwire-dupe.patch b/leaf-server/minecraft-patches/features/0107-Configurable-tripwire-dupe.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0108-Configurable-tripwire-dupe.patch rename to leaf-server/minecraft-patches/features/0107-Configurable-tripwire-dupe.patch diff --git a/leaf-server/minecraft-patches/features/0109-PaperPR-Fix-MC-117075-Block-Entities-Unload-Lag-Spik.patch b/leaf-server/minecraft-patches/features/0108-PaperPR-Fix-MC-117075-Block-Entities-Unload-Lag-Spik.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0109-PaperPR-Fix-MC-117075-Block-Entities-Unload-Lag-Spik.patch rename to leaf-server/minecraft-patches/features/0108-PaperPR-Fix-MC-117075-Block-Entities-Unload-Lag-Spik.patch diff --git a/leaf-server/minecraft-patches/features/0110-Sepals-Rearrange-the-attackable-conditions.patch b/leaf-server/minecraft-patches/features/0109-Sepals-Rearrange-the-attackable-conditions.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0110-Sepals-Rearrange-the-attackable-conditions.patch rename to leaf-server/minecraft-patches/features/0109-Sepals-Rearrange-the-attackable-conditions.patch diff --git a/leaf-server/minecraft-patches/features/0111-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch b/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0111-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch rename to leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch diff --git a/leaf-server/minecraft-patches/features/0112-SparklyPaper-Reset-dirty-flag-when-loading-maps-from.patch b/leaf-server/minecraft-patches/features/0111-SparklyPaper-Reset-dirty-flag-when-loading-maps-from.patch similarity index 91% rename from leaf-server/minecraft-patches/features/0112-SparklyPaper-Reset-dirty-flag-when-loading-maps-from.patch rename to leaf-server/minecraft-patches/features/0111-SparklyPaper-Reset-dirty-flag-when-loading-maps-from.patch index c9472249..46fae274 100644 --- a/leaf-server/minecraft-patches/features/0112-SparklyPaper-Reset-dirty-flag-when-loading-maps-from.patch +++ b/leaf-server/minecraft-patches/features/0111-SparklyPaper-Reset-dirty-flag-when-loading-maps-from.patch @@ -9,7 +9,7 @@ By default, the server will start rewriting all map datas to the disk after load This also slows down world saving a lot if you have a lot of maps diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java -index 681dec447486138088fe5f705ef4fadab531139f..07f9287ff1f1dbd1795582c74102c072ea59b29f 100644 +index 27f8a22d798a17dbd5949d1b6ff0526837fe91d5..59829bb134555d96edcf4cbb844ccacb88c44961 100644 --- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java @@ -198,6 +198,7 @@ public class MapItemSavedData extends SavedData { diff --git a/leaf-server/minecraft-patches/features/0113-Optimize-checking-nearby-players-for-spawning.patch b/leaf-server/minecraft-patches/features/0112-Optimize-checking-nearby-players-for-spawning.patch similarity index 97% rename from leaf-server/minecraft-patches/features/0113-Optimize-checking-nearby-players-for-spawning.patch rename to leaf-server/minecraft-patches/features/0112-Optimize-checking-nearby-players-for-spawning.patch index 6fde26d4..1d46527e 100644 --- a/leaf-server/minecraft-patches/features/0113-Optimize-checking-nearby-players-for-spawning.patch +++ b/leaf-server/minecraft-patches/features/0112-Optimize-checking-nearby-players-for-spawning.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimize checking nearby players for spawning diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 8986c059e7aadb58ae8d9ab7b848de10f9faa6b2..546b20f8998c71ca1a701de7efcedd8d821105e4 100644 +index c55c8e9b777e4999a8a8de6d821b53245dc578c2..f3be481a92b4f5403809c38d3b3431f4096d7a2e 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -719,7 +719,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider diff --git a/leaf-server/minecraft-patches/features/0114-Cache-supporting-block-check.patch b/leaf-server/minecraft-patches/features/0113-Cache-supporting-block-check.patch similarity index 96% rename from leaf-server/minecraft-patches/features/0114-Cache-supporting-block-check.patch rename to leaf-server/minecraft-patches/features/0113-Cache-supporting-block-check.patch index 133644d2..044b101e 100644 --- a/leaf-server/minecraft-patches/features/0114-Cache-supporting-block-check.patch +++ b/leaf-server/minecraft-patches/features/0113-Cache-supporting-block-check.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Cache supporting block check diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 4544dd876d3cbcdb9b774b4a1f0c4737f3124bc5..6ca446fd9ab38329ba505526a56f8e4f64a9a639 100644 +index 80ad278eac81aac72d6ec7737287ad018eff7c54..9bc978ca290ca772b0367e89b69fe16b502b0cd2 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -1083,12 +1083,36 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess diff --git a/leaf-server/minecraft-patches/features/0115-Avoid-useless-deque-clear-on-LevelTicks-cleanupAfter.patch b/leaf-server/minecraft-patches/features/0114-Avoid-useless-deque-clear-on-LevelTicks-cleanupAfter.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0115-Avoid-useless-deque-clear-on-LevelTicks-cleanupAfter.patch rename to leaf-server/minecraft-patches/features/0114-Avoid-useless-deque-clear-on-LevelTicks-cleanupAfter.patch diff --git a/leaf-server/minecraft-patches/features/0116-Replace-brain-activity-maps-with-optimized-collectio.patch b/leaf-server/minecraft-patches/features/0115-Replace-brain-activity-maps-with-optimized-collectio.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0116-Replace-brain-activity-maps-with-optimized-collectio.patch rename to leaf-server/minecraft-patches/features/0115-Replace-brain-activity-maps-with-optimized-collectio.patch diff --git a/leaf-server/minecraft-patches/features/0117-Remove-stream-in-villagers.patch b/leaf-server/minecraft-patches/features/0116-Remove-stream-in-villagers.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0117-Remove-stream-in-villagers.patch rename to leaf-server/minecraft-patches/features/0116-Remove-stream-in-villagers.patch diff --git a/leaf-server/minecraft-patches/features/0118-Optimize-baby-villager-sensor.patch b/leaf-server/minecraft-patches/features/0117-Optimize-baby-villager-sensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0118-Optimize-baby-villager-sensor.patch rename to leaf-server/minecraft-patches/features/0117-Optimize-baby-villager-sensor.patch diff --git a/leaf-server/minecraft-patches/features/0119-Only-player-pushable.patch b/leaf-server/minecraft-patches/features/0118-Only-player-pushable.patch similarity index 98% rename from leaf-server/minecraft-patches/features/0119-Only-player-pushable.patch rename to leaf-server/minecraft-patches/features/0118-Only-player-pushable.patch index 1f472084..a7a9151f 100644 --- a/leaf-server/minecraft-patches/features/0119-Only-player-pushable.patch +++ b/leaf-server/minecraft-patches/features/0118-Only-player-pushable.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Only player pushable Useful for extreme cases like massive entities collide together in a small area diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index 69e91a732a497b2ffa906088a636947ce9a9ec36..d455406df1f3a4c3d1bd016d50d1d7025366ae80 100644 +index 668722c44bac3a9731d175a93aad8435e0c2c1c0..156d0c14eedf2f79e4276cc4065e19a43699b965 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java @@ -3631,7 +3631,7 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf diff --git a/leaf-server/minecraft-patches/features/0120-Remove-iterators-from-Inventory-contains.patch b/leaf-server/minecraft-patches/features/0119-Remove-iterators-from-Inventory-contains.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0120-Remove-iterators-from-Inventory-contains.patch rename to leaf-server/minecraft-patches/features/0119-Remove-iterators-from-Inventory-contains.patch diff --git a/leaf-server/minecraft-patches/features/0121-Alternative-Brain-Behaviour.patch b/leaf-server/minecraft-patches/features/0120-Alternative-Brain-Behaviour.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0121-Alternative-Brain-Behaviour.patch rename to leaf-server/minecraft-patches/features/0120-Alternative-Brain-Behaviour.patch diff --git a/leaf-server/minecraft-patches/features/0122-Cache-eligible-players-for-despawn-checks.patch b/leaf-server/minecraft-patches/features/0121-Cache-eligible-players-for-despawn-checks.patch similarity index 96% rename from leaf-server/minecraft-patches/features/0122-Cache-eligible-players-for-despawn-checks.patch rename to leaf-server/minecraft-patches/features/0121-Cache-eligible-players-for-despawn-checks.patch index cfe0faaf..d9fc4b67 100644 --- a/leaf-server/minecraft-patches/features/0122-Cache-eligible-players-for-despawn-checks.patch +++ b/leaf-server/minecraft-patches/features/0121-Cache-eligible-players-for-despawn-checks.patch @@ -38,10 +38,10 @@ index 61afe93ff7f6f6ac3967e948bf39b0ab559e2808..a66e5f6652d9633c856490de36d8d8fd .forEach( entity -> { diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 82a1715fea41e6a41c4ff441ea89f424f68ba190..8362def0dc61496a087bd859052bd80ebba83185 100644 +index 4f01b53bf801f99253efd27df6216912705d18af..89df0f6893775df01e1470bb04f0059cec65ff70 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -1570,6 +1570,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1569,6 +1569,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc this.containerMenu.broadcastChanges(); } diff --git a/leaf-server/minecraft-patches/features/0123-Slightly-optimise-getNearestPlayer.patch b/leaf-server/minecraft-patches/features/0122-Slightly-optimise-getNearestPlayer.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0123-Slightly-optimise-getNearestPlayer.patch rename to leaf-server/minecraft-patches/features/0122-Slightly-optimise-getNearestPlayer.patch diff --git a/leaf-server/minecraft-patches/features/0124-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch b/leaf-server/minecraft-patches/features/0123-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch similarity index 92% rename from leaf-server/minecraft-patches/features/0124-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch rename to leaf-server/minecraft-patches/features/0123-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch index fb95a3e9..f2134f69 100644 --- a/leaf-server/minecraft-patches/features/0124-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch +++ b/leaf-server/minecraft-patches/features/0123-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Use ensureCapacity to pre-populate the size of ticking chunks diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 6735f9e23c8972b7cf1438a2f3b49d780c1ff78c..c80464d333bd37a9b8bc7cea2291c8c72e6f9bd6 100644 +index 0fd51020ca9480be2855eb58a7d4d43511829512..b1400a1cc41717437ccb0f2b7854e54c5b985985 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -585,7 +585,11 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon diff --git a/leaf-server/minecraft-patches/features/0125-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch b/leaf-server/minecraft-patches/features/0124-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch similarity index 93% rename from leaf-server/minecraft-patches/features/0125-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch rename to leaf-server/minecraft-patches/features/0124-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch index 792385ff..0e88f4e9 100644 --- a/leaf-server/minecraft-patches/features/0125-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch +++ b/leaf-server/minecraft-patches/features/0124-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch @@ -7,7 +7,7 @@ Subject: [PATCH] Directly use the pre-filtered ticking chunks list as the This patch uses already pre filtered chunks, which completely skips the isChunkNearPlayer check diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index c80464d333bd37a9b8bc7cea2291c8c72e6f9bd6..b1f1b596a597d559aa672a3cb46a03917ad746af 100644 +index b1400a1cc41717437ccb0f2b7854e54c5b985985..f57f8e610dac80b8095bfc0c7e4b22ff5ad6b13c 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -592,14 +592,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon diff --git a/leaf-server/minecraft-patches/features/0126-Bulk-writes-to-writeLongArray-during-chunk-loading.patch b/leaf-server/minecraft-patches/features/0125-Bulk-writes-to-writeLongArray-during-chunk-loading.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0126-Bulk-writes-to-writeLongArray-during-chunk-loading.patch rename to leaf-server/minecraft-patches/features/0125-Bulk-writes-to-writeLongArray-during-chunk-loading.patch diff --git a/leaf-server/minecraft-patches/features/0127-Improve-sorting-in-SortedArraySet.patch b/leaf-server/minecraft-patches/features/0126-Improve-sorting-in-SortedArraySet.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0127-Improve-sorting-in-SortedArraySet.patch rename to leaf-server/minecraft-patches/features/0126-Improve-sorting-in-SortedArraySet.patch diff --git a/leaf-server/minecraft-patches/features/0128-Make-removeIf-slightly-faster.patch b/leaf-server/minecraft-patches/features/0127-Make-removeIf-slightly-faster.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0128-Make-removeIf-slightly-faster.patch rename to leaf-server/minecraft-patches/features/0127-Make-removeIf-slightly-faster.patch diff --git a/leaf-server/minecraft-patches/features/0129-Optimize-LinearPalette.patch b/leaf-server/minecraft-patches/features/0128-Optimize-LinearPalette.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0129-Optimize-LinearPalette.patch rename to leaf-server/minecraft-patches/features/0128-Optimize-LinearPalette.patch diff --git a/leaf-server/minecraft-patches/features/0130-Slightly-optimized-VarInt-write.patch b/leaf-server/minecraft-patches/features/0129-Slightly-optimized-VarInt-write.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0130-Slightly-optimized-VarInt-write.patch rename to leaf-server/minecraft-patches/features/0129-Slightly-optimized-VarInt-write.patch diff --git a/leaf-server/minecraft-patches/features/0131-Rewrite-ClientboundLightUpdatePacketData.patch b/leaf-server/minecraft-patches/features/0130-Rewrite-ClientboundLightUpdatePacketData.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0131-Rewrite-ClientboundLightUpdatePacketData.patch rename to leaf-server/minecraft-patches/features/0130-Rewrite-ClientboundLightUpdatePacketData.patch diff --git a/leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch b/leaf-server/minecraft-patches/features/0131-Async-chunk-send.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch rename to leaf-server/minecraft-patches/features/0131-Async-chunk-send.patch diff --git a/leaf-server/minecraft-patches/features/0133-Spawner-Configurations.patch b/leaf-server/minecraft-patches/features/0132-Spawner-Configurations.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0133-Spawner-Configurations.patch rename to leaf-server/minecraft-patches/features/0132-Spawner-Configurations.patch diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0133-SparklyPaper-Parallel-world-ticking.patch similarity index 99% rename from leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch rename to leaf-server/minecraft-patches/features/0133-SparklyPaper-Parallel-world-ticking.patch index aaf2e279..9f02149e 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0133-SparklyPaper-Parallel-world-ticking.patch @@ -834,7 +834,7 @@ index a66e5f6652d9633c856490de36d8d8fdf8a5298a..d6524d5c442555eaeb4d90f6a101262e // Paper start - extra debug info if (entity.valid) { diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 8362def0dc61496a087bd859052bd80ebba83185..09f517059aa47ca67329bc913243d4fdee09abe5 100644 +index 89df0f6893775df01e1470bb04f0059cec65ff70..941a73f32661dfa50dd381dc888bd3c04f3fad60 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -427,6 +427,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -862,7 +862,7 @@ index 8362def0dc61496a087bd859052bd80ebba83185..09f517059aa47ca67329bc913243d4fd // CraftBukkit start /* this.isChangingDimension = true; -@@ -1819,6 +1823,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1818,6 +1822,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc return OptionalInt.empty(); } else { // CraftBukkit start @@ -875,7 +875,7 @@ index 8362def0dc61496a087bd859052bd80ebba83185..09f517059aa47ca67329bc913243d4fd this.containerMenu = abstractContainerMenu; // Moved up if (!this.isImmobile()) this.connection -@@ -1883,6 +1893,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1882,6 +1892,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc } @Override public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { @@ -888,7 +888,7 @@ index 8362def0dc61496a087bd859052bd80ebba83185..09f517059aa47ca67329bc913243d4fd // Paper end - Inventory close reason this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..6148d5c575f4cdf2d1920b9585a5b32732511301 100644 +index 75fb49f1596f475278d12c8c7aea9ad4952b6056..de601491b7ecb83f1bb64a95989d6ed4dd52ab40 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -252,6 +252,8 @@ public abstract class PlayerList { diff --git a/leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Track-each-world-MSPT.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0135-SparklyPaper-Track-each-world-MSPT.patch rename to leaf-server/minecraft-patches/features/0134-SparklyPaper-Track-each-world-MSPT.patch diff --git a/leaf-server/minecraft-patches/features/0136-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch b/leaf-server/minecraft-patches/features/0135-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0136-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch rename to leaf-server/minecraft-patches/features/0135-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch diff --git a/leaf-server/minecraft-patches/features/0137-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch b/leaf-server/minecraft-patches/features/0136-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0137-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch rename to leaf-server/minecraft-patches/features/0136-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch diff --git a/leaf-server/minecraft-patches/features/0138-Remove-streams-on-InsideBrownianWalk.patch b/leaf-server/minecraft-patches/features/0137-Remove-streams-on-InsideBrownianWalk.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0138-Remove-streams-on-InsideBrownianWalk.patch rename to leaf-server/minecraft-patches/features/0137-Remove-streams-on-InsideBrownianWalk.patch diff --git a/leaf-server/minecraft-patches/features/0139-Use-BFS-on-getSlopeDistance.patch b/leaf-server/minecraft-patches/features/0138-Use-BFS-on-getSlopeDistance.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0139-Use-BFS-on-getSlopeDistance.patch rename to leaf-server/minecraft-patches/features/0138-Use-BFS-on-getSlopeDistance.patch diff --git a/leaf-server/minecraft-patches/features/0140-Paper-PR-Throttle-failed-spawn-attempts.patch b/leaf-server/minecraft-patches/features/0139-Paper-PR-Throttle-failed-spawn-attempts.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0140-Paper-PR-Throttle-failed-spawn-attempts.patch rename to leaf-server/minecraft-patches/features/0139-Paper-PR-Throttle-failed-spawn-attempts.patch diff --git a/leaf-server/minecraft-patches/features/0141-Improve-BlockEntity-ticking-isRemoved-check.patch b/leaf-server/minecraft-patches/features/0140-Improve-BlockEntity-ticking-isRemoved-check.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0141-Improve-BlockEntity-ticking-isRemoved-check.patch rename to leaf-server/minecraft-patches/features/0140-Improve-BlockEntity-ticking-isRemoved-check.patch diff --git a/leaf-server/minecraft-patches/features/0142-Raytrace-AntiXray-SDK-integration.patch b/leaf-server/minecraft-patches/features/0141-Raytrace-AntiXray-SDK-integration.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0142-Raytrace-AntiXray-SDK-integration.patch rename to leaf-server/minecraft-patches/features/0141-Raytrace-AntiXray-SDK-integration.patch diff --git a/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch b/leaf-server/minecraft-patches/features/0142-Optimize-addOrUpdateTransientModifier.patch similarity index 93% rename from leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch rename to leaf-server/minecraft-patches/features/0142-Optimize-addOrUpdateTransientModifier.patch index 7b58130e..52f87b30 100644 --- a/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch +++ b/leaf-server/minecraft-patches/features/0142-Optimize-addOrUpdateTransientModifier.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimize addOrUpdateTransientModifier diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -index ed5077708415a74da171b88fa1fb8b736446666b..62cadad97109247e65a550acc5955424b1f6fc5e 100644 +index b502c4a0f3695cc5bee8954f937f64584df1584d..26d97742310c054eebbee7f4dc2f535d3ee4cd2e 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java @@ -107,8 +107,13 @@ public class AttributeInstance { diff --git a/leaf-server/minecraft-patches/features/0144-Optimize-ContextMap.create.patch b/leaf-server/minecraft-patches/features/0143-Optimize-ContextMap.create.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0144-Optimize-ContextMap.create.patch rename to leaf-server/minecraft-patches/features/0143-Optimize-ContextMap.create.patch diff --git a/leaf-server/minecraft-patches/features/0145-Micro-optimizations-for-random-tick.patch b/leaf-server/minecraft-patches/features/0144-Micro-optimizations-for-random-tick.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0145-Micro-optimizations-for-random-tick.patch rename to leaf-server/minecraft-patches/features/0144-Micro-optimizations-for-random-tick.patch diff --git a/leaf-server/minecraft-patches/features/0146-Remove-streams-on-updateConnectedPlayersWithinRange.patch b/leaf-server/minecraft-patches/features/0145-Remove-streams-on-updateConnectedPlayersWithinRange.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0146-Remove-streams-on-updateConnectedPlayersWithinRange.patch rename to leaf-server/minecraft-patches/features/0145-Remove-streams-on-updateConnectedPlayersWithinRange.patch diff --git a/leaf-server/minecraft-patches/features/0147-Remove-streams-on-PlayerDetector.patch b/leaf-server/minecraft-patches/features/0146-Remove-streams-on-PlayerDetector.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0147-Remove-streams-on-PlayerDetector.patch rename to leaf-server/minecraft-patches/features/0146-Remove-streams-on-PlayerDetector.patch diff --git a/leaf-server/minecraft-patches/features/0148-Use-direct-iteration-on-Sensing.tick.patch b/leaf-server/minecraft-patches/features/0147-Use-direct-iteration-on-Sensing.tick.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0148-Use-direct-iteration-on-Sensing.tick.patch rename to leaf-server/minecraft-patches/features/0147-Use-direct-iteration-on-Sensing.tick.patch diff --git a/leaf-server/minecraft-patches/features/0149-Optimise-non-flush-packet-sending.patch b/leaf-server/minecraft-patches/features/0148-Optimise-non-flush-packet-sending.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0149-Optimise-non-flush-packet-sending.patch rename to leaf-server/minecraft-patches/features/0148-Optimise-non-flush-packet-sending.patch diff --git a/leaf-server/minecraft-patches/features/0150-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch b/leaf-server/minecraft-patches/features/0149-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0150-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch rename to leaf-server/minecraft-patches/features/0149-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch diff --git a/leaf-server/minecraft-patches/features/0151-Null-handling-on-MultifaceSpreader.patch b/leaf-server/minecraft-patches/features/0150-Null-handling-on-MultifaceSpreader.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0151-Null-handling-on-MultifaceSpreader.patch rename to leaf-server/minecraft-patches/features/0150-Null-handling-on-MultifaceSpreader.patch diff --git a/leaf-server/minecraft-patches/features/0151-More-virtual-threads.patch b/leaf-server/minecraft-patches/features/0151-More-virtual-threads.patch new file mode 100644 index 00000000..0315b848 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0151-More-virtual-threads.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sun, 6 Apr 2025 11:22:35 +0200 +Subject: [PATCH] More virtual threads + + +diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java +index b097f685e826e70008e3a096ee5f1d4fccf25680..25b6aaa71ff389094d0f44c341f0c6458fc644ff 100644 +--- a/net/minecraft/Util.java ++++ b/net/minecraft/Util.java +@@ -97,7 +97,8 @@ public class Util { + public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool + private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); + // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread +- public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { ++ // Leaf start - More virtual threads ++ public static final ExecutorService PROFILE_EXECUTOR = createProfileExecutor(); /* new Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { + + private final AtomicInteger count = new AtomicInteger(); + +@@ -110,7 +111,30 @@ public class Util { + }); + return ret; + } +- }); ++ }); */ ++ ++ private static ExecutorService createProfileExecutor() { ++ final java.util.concurrent.ThreadFactory factory; ++ if (org.dreeam.leaf.config.modules.opt.VT4ProfileExecutor.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported()) { ++ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory(); ++ } else { ++ factory = new java.util.concurrent.ThreadFactory() { ++ private final AtomicInteger count = new AtomicInteger(); ++ ++ @Override ++ public Thread newThread(Runnable run) { ++ Thread ret = new Thread(run); ++ ret.setName("Profile Lookup Executor #" + this.count.getAndIncrement()); ++ ret.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> { ++ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable); ++ }); ++ return ret; ++ } ++ }; ++ } ++ return Executors.newFixedThreadPool(2, factory); ++ } ++ // Leaf end - More virtual threads + // Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread + private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT); + public static final int LINEAR_LOOKUP_THRESHOLD = 8; diff --git a/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0152-Async-target-finding.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0153-Async-target-finding.patch rename to leaf-server/minecraft-patches/features/0152-Async-target-finding.patch diff --git a/leaf-server/minecraft-patches/features/0152-More-virtual-threads.patch b/leaf-server/minecraft-patches/features/0152-More-virtual-threads.patch deleted file mode 100644 index 74803bd2..00000000 --- a/leaf-server/minecraft-patches/features/0152-More-virtual-threads.patch +++ /dev/null @@ -1,112 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Sun, 6 Apr 2025 11:22:35 +0200 -Subject: [PATCH] More virtual threads - - -diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java -index b097f685e826e70008e3a096ee5f1d4fccf25680..aa79e95dc93927ce224c02e4382c7246cb48d51c 100644 ---- a/net/minecraft/Util.java -+++ b/net/minecraft/Util.java -@@ -97,7 +97,8 @@ public class Util { - public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool - private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); - // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread -- public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { -+ // Leaf start - More virtual threads -+ public static final ExecutorService PROFILE_EXECUTOR = createProfileExecutor(); /* new Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { - - private final AtomicInteger count = new AtomicInteger(); - -@@ -110,7 +111,30 @@ public class Util { - }); - return ret; - } -- }); -+ }); */ -+ -+ private static ExecutorService createProfileExecutor() { -+ final java.util.concurrent.ThreadFactory factory; -+ if (org.dreeam.leaf.config.modules.opt.VT4ProfileExecutor.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported()) { -+ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory(); -+ } else { -+ factory = new java.util.concurrent.ThreadFactory() { -+ private final AtomicInteger count = new AtomicInteger(); -+ -+ @Override -+ public Thread newThread(Runnable run) { -+ Thread ret = new Thread(run); -+ ret.setName("Profile Lookup Executor #" + this.count.getAndIncrement()); -+ ret.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> { -+ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable); -+ }); -+ return ret; -+ } -+ }; -+ } -+ return Executors.newFixedThreadPool(2, factory); -+ } -+ // Leaf end - More virtual threads - // Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread - private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT); - public static final int LINEAR_LOOKUP_THRESHOLD = 8; -@@ -254,16 +278,31 @@ public class Util { - } - - private static TracingExecutor makeIoExecutor(String name, boolean daemon) { -- AtomicInteger atomicInteger = new AtomicInteger(1); -- return new TracingExecutor(Executors.newCachedThreadPool(runnable -> { -- Thread thread = new Thread(runnable); -- String string = name + atomicInteger.getAndIncrement(); -- TracyClient.setThreadName(string, name.hashCode()); -- thread.setName(string); -- thread.setDaemon(daemon); -- thread.setUncaughtExceptionHandler(Util::onThreadException); -- return thread; -- })); -+ // Leaf start - More virtual threads -+ final java.util.concurrent.ThreadFactory factory; -+ final boolean useVirtualThreads; // Gale - virtual thread support -+ if (name.startsWith("Download-")) { // Gale - virtual thread support -+ useVirtualThreads = org.dreeam.leaf.config.modules.opt.VT4DownloadPool.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported(); // Gale - virtual thread support -+ } else { -+ useVirtualThreads = false; -+ } -+ -+ if (useVirtualThreads) { -+ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory(); -+ } else { -+ AtomicInteger atomicInteger = new AtomicInteger(1); -+ factory = runnable -> { -+ Thread thread = new Thread(runnable); -+ String string = name + atomicInteger.getAndIncrement(); -+ TracyClient.setThreadName(string, name.hashCode()); -+ thread.setName(string); -+ thread.setDaemon(daemon); -+ thread.setUncaughtExceptionHandler(Util::onThreadException); -+ return thread; -+ }; -+ } -+ return new TracingExecutor(Executors.newCachedThreadPool(factory)); -+ // Leaf end - More virtual threads - } - - // Paper start - Separate dimension data IO pool -@@ -1099,7 +1138,7 @@ public class Util { - } - - public static Typed readTypedOrThrow(Type type, Dynamic data, boolean partial) { -- DataResult> dataResult = type.readTyped(data).map(Pair::getFirst); -+ DataResult> dataResult = type.readTyped(data).map(Pair::getFirst); // Paper - Fix generics issue // Gale - Fix generics issue - - try { - return partial ? dataResult.getPartialOrThrow(IllegalStateException::new) : dataResult.getOrThrow(IllegalStateException::new); -@@ -1148,7 +1187,7 @@ public class Util { - } - - public void openUri(URI uri) { -- throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code -+ // throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code // Leaf - More virtual threads - This method is not useful on dedicated servers - } - - public void openFile(File file) { diff --git a/leaf-server/minecraft-patches/features/0154-Optimize-ThreadedTicketLevelPropagator.patch b/leaf-server/minecraft-patches/features/0153-Optimize-ThreadedTicketLevelPropagator.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0154-Optimize-ThreadedTicketLevelPropagator.patch rename to leaf-server/minecraft-patches/features/0153-Optimize-ThreadedTicketLevelPropagator.patch diff --git a/leaf-server/minecraft-patches/features/0155-Optimise-MobEffectUtil-getDigSpeedAmplification.patch b/leaf-server/minecraft-patches/features/0154-Optimise-MobEffectUtil-getDigSpeedAmplification.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0155-Optimise-MobEffectUtil-getDigSpeedAmplification.patch rename to leaf-server/minecraft-patches/features/0154-Optimise-MobEffectUtil-getDigSpeedAmplification.patch diff --git a/leaf-server/minecraft-patches/features/0156-Optimise-chunkUnloads.patch b/leaf-server/minecraft-patches/features/0155-Optimise-chunkUnloads.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0156-Optimise-chunkUnloads.patch rename to leaf-server/minecraft-patches/features/0155-Optimise-chunkUnloads.patch diff --git a/leaf-server/minecraft-patches/features/0157-Optimize-BlockEntityType-isValid.patch b/leaf-server/minecraft-patches/features/0156-Optimize-BlockEntityType-isValid.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0157-Optimize-BlockEntityType-isValid.patch rename to leaf-server/minecraft-patches/features/0156-Optimize-BlockEntityType-isValid.patch diff --git a/leaf-server/minecraft-patches/features/0158-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch b/leaf-server/minecraft-patches/features/0157-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch similarity index 97% rename from leaf-server/minecraft-patches/features/0158-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch rename to leaf-server/minecraft-patches/features/0157-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch index 29a2b6f7..d963ca20 100644 --- a/leaf-server/minecraft-patches/features/0158-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch +++ b/leaf-server/minecraft-patches/features/0157-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch @@ -27,7 +27,7 @@ index 2fef24acfaceab21aad6be50e6b29701fa460bfb..e0b61e2cde3010f8dcd2cc764814e94a public static final int MIN_VIEW_DISTANCE = 2; public static final int MAX_VIEW_DISTANCE = 32; diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 3591de34443069f3f163f8d17df6372c3068611d..842cb6efc8aa7c68b7b9cba144d8540679850f23 100644 +index de601491b7ecb83f1bb64a95989d6ed4dd52ab40..afa25f6adb7e3c50f4a71af10818b50d4f1be393 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -432,6 +432,13 @@ public abstract class PlayerList { diff --git a/leaf-server/minecraft-patches/features/0159-PaperPR-Fix-save-load-NaN-Entity-Motion.patch b/leaf-server/minecraft-patches/features/0158-PaperPR-Fix-save-load-NaN-Entity-Motion.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0159-PaperPR-Fix-save-load-NaN-Entity-Motion.patch rename to leaf-server/minecraft-patches/features/0158-PaperPR-Fix-save-load-NaN-Entity-Motion.patch diff --git a/leaf-server/minecraft-patches/features/0160-PaperPR-Fix-unnecessary-map-data-saves.patch b/leaf-server/minecraft-patches/features/0159-PaperPR-Fix-unnecessary-map-data-saves.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0160-PaperPR-Fix-unnecessary-map-data-saves.patch rename to leaf-server/minecraft-patches/features/0159-PaperPR-Fix-unnecessary-map-data-saves.patch diff --git a/leaf-server/minecraft-patches/features/0161-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch b/leaf-server/minecraft-patches/features/0160-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0161-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch rename to leaf-server/minecraft-patches/features/0160-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch diff --git a/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch rename to leaf-server/minecraft-patches/features/0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch diff --git a/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0162-Protocol-Core.patch similarity index 95% rename from leaf-server/minecraft-patches/features/0163-Protocol-Core.patch rename to leaf-server/minecraft-patches/features/0162-Protocol-Core.patch index 075bf1d7..ce3d6e13 100644 --- a/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch +++ b/leaf-server/minecraft-patches/features/0162-Protocol-Core.patch @@ -34,7 +34,7 @@ index f5f0e4f5d7a4b34514e102020d2a7be313292f7f..0baa48054beead8f187b56ea8d719166 for (int i = 0; i < this.tickables.size(); i++) { this.tickables.get(i).run(); diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 09f517059aa47ca67329bc913243d4fdee09abe5..50cf63666071f5d01a85dfc6c6c45c19b05d8ec2 100644 +index 941a73f32661dfa50dd381dc888bd3c04f3fad60..0ba7cf69eda2d952ac2ffecf9167f3f5d43c4c63 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -859,6 +859,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -59,7 +59,7 @@ index 96b70c1384834a8e22925c8e2af85ab7606dde20..9eb14bb38416330878959c4b095d057c final byte[] data = discardedPayload.data(); try { diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 842cb6efc8aa7c68b7b9cba144d8540679850f23..857e5823038192931b89a8b638a9a6b1fd0f9b5c 100644 +index afa25f6adb7e3c50f4a71af10818b50d4f1be393..6da7f7213130f19c7d95a173498e7375bfacc9a5 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -682,6 +682,7 @@ public abstract class PlayerList { diff --git a/leaf-server/minecraft-patches/features/0164-Reduce-PlayerChunk-Updates.patch b/leaf-server/minecraft-patches/features/0163-Reduce-PlayerChunk-Updates.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0164-Reduce-PlayerChunk-Updates.patch rename to leaf-server/minecraft-patches/features/0163-Reduce-PlayerChunk-Updates.patch diff --git a/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0164-Async-switch-connection-state.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch rename to leaf-server/minecraft-patches/features/0164-Async-switch-connection-state.patch diff --git a/leaf-server/minecraft-patches/features/0166-Optimise-BlockEntities-tickersInLevel.patch b/leaf-server/minecraft-patches/features/0165-Optimise-BlockEntities-tickersInLevel.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0166-Optimise-BlockEntities-tickersInLevel.patch rename to leaf-server/minecraft-patches/features/0165-Optimise-BlockEntities-tickersInLevel.patch diff --git a/leaf-server/minecraft-patches/features/0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch b/leaf-server/minecraft-patches/features/0166-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch rename to leaf-server/minecraft-patches/features/0166-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch diff --git a/leaf-server/minecraft-patches/features/0168-Flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0167-Flush-location-while-knockback.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0168-Flush-location-while-knockback.patch rename to leaf-server/minecraft-patches/features/0167-Flush-location-while-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0168-Only-tick-items-at-hand.patch similarity index 96% rename from leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch rename to leaf-server/minecraft-patches/features/0168-Only-tick-items-at-hand.patch index c2f62aee..b44c882d 100644 --- a/leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch +++ b/leaf-server/minecraft-patches/features/0168-Only-tick-items-at-hand.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Only tick items at hand diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 50cf63666071f5d01a85dfc6c6c45c19b05d8ec2..ef53b9f307572dd5dc99d02e017d6b2dafb04e3f 100644 +index 0ba7cf69eda2d952ac2ffecf9167f3f5d43c4c63..4d32ba245eb204231af791a65142932a18b4f3f6 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -888,12 +888,19 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc diff --git a/leaf-server/minecraft-patches/features/0170-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0169-Smart-sort-items-in-NearestItemSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0170-Smart-sort-items-in-NearestItemSensor.patch rename to leaf-server/minecraft-patches/features/0169-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0171-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0170-Optimise-player-movement-checks.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0171-Optimise-player-movement-checks.patch rename to leaf-server/minecraft-patches/features/0170-Optimise-player-movement-checks.patch diff --git a/leaf-server/minecraft-patches/features/0172-Remove-streams-in-MobSensor.patch b/leaf-server/minecraft-patches/features/0171-Remove-streams-in-MobSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0172-Remove-streams-in-MobSensor.patch rename to leaf-server/minecraft-patches/features/0171-Remove-streams-in-MobSensor.patch diff --git a/leaf-server/minecraft-patches/features/0173-Remove-streams-in-TemptingSensor.patch b/leaf-server/minecraft-patches/features/0172-Remove-streams-in-TemptingSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0173-Remove-streams-in-TemptingSensor.patch rename to leaf-server/minecraft-patches/features/0172-Remove-streams-in-TemptingSensor.patch diff --git a/leaf-server/minecraft-patches/features/0174-Use-HashedList-on-WeightedRandomList.patch b/leaf-server/minecraft-patches/features/0173-Use-HashedList-on-WeightedRandomList.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0174-Use-HashedList-on-WeightedRandomList.patch rename to leaf-server/minecraft-patches/features/0173-Use-HashedList-on-WeightedRandomList.patch diff --git a/leaf-server/minecraft-patches/features/0175-Add-configurable-death-item-drop-knockback-settings.patch b/leaf-server/minecraft-patches/features/0174-Add-configurable-death-item-drop-knockback-settings.patch similarity index 93% rename from leaf-server/minecraft-patches/features/0175-Add-configurable-death-item-drop-knockback-settings.patch rename to leaf-server/minecraft-patches/features/0174-Add-configurable-death-item-drop-knockback-settings.patch index 3e1c19dd..d39915b3 100644 --- a/leaf-server/minecraft-patches/features/0175-Add-configurable-death-item-drop-knockback-settings.patch +++ b/leaf-server/minecraft-patches/features/0174-Add-configurable-death-item-drop-knockback-settings.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add configurable death item drop knockback settings diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 4275caa2b4d636f646f185d67e1cdbc194bd68aa..cacceac0ee28fc045e97c0d062729390b51c4794 100644 +index 4d32ba245eb204231af791a65142932a18b4f3f6..e6414643d4c44bcf22d17bf185cc288d2a4f8399 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -1099,7 +1099,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -17,7 +17,7 @@ index 4275caa2b4d636f646f185d67e1cdbc194bd68aa..cacceac0ee28fc045e97c0d062729390 } } } -@@ -2852,9 +2852,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -2851,9 +2851,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc } if (dropAround) { diff --git a/leaf-server/minecraft-patches/features/0176-Optimize-getScaledTrackingDistance.patch b/leaf-server/minecraft-patches/features/0175-Optimize-getScaledTrackingDistance.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0176-Optimize-getScaledTrackingDistance.patch rename to leaf-server/minecraft-patches/features/0175-Optimize-getScaledTrackingDistance.patch diff --git a/leaf-server/minecraft-patches/features/0177-Optimize-SynchedEntityData-packDirty.patch b/leaf-server/minecraft-patches/features/0176-Optimize-SynchedEntityData-packDirty.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0177-Optimize-SynchedEntityData-packDirty.patch rename to leaf-server/minecraft-patches/features/0176-Optimize-SynchedEntityData-packDirty.patch diff --git a/leaf-server/minecraft-patches/features/0178-Optimize-isEyeInFluid.patch b/leaf-server/minecraft-patches/features/0177-Optimize-isEyeInFluid.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0178-Optimize-isEyeInFluid.patch rename to leaf-server/minecraft-patches/features/0177-Optimize-isEyeInFluid.patch diff --git a/leaf-server/minecraft-patches/features/0180-Cache-block-path-type.patch b/leaf-server/minecraft-patches/features/0178-Cache-block-path-type.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0180-Cache-block-path-type.patch rename to leaf-server/minecraft-patches/features/0178-Cache-block-path-type.patch diff --git a/leaf-server/minecraft-patches/features/0181-optimize-getEntityStatus.patch b/leaf-server/minecraft-patches/features/0179-optimize-getEntityStatus.patch similarity index 96% rename from leaf-server/minecraft-patches/features/0181-optimize-getEntityStatus.patch rename to leaf-server/minecraft-patches/features/0179-optimize-getEntityStatus.patch index 7d0a36aa..58447a60 100644 --- a/leaf-server/minecraft-patches/features/0181-optimize-getEntityStatus.patch +++ b/leaf-server/minecraft-patches/features/0179-optimize-getEntityStatus.patch @@ -42,7 +42,7 @@ index 7554c109c35397bc1a43dd80e87764fd78645bbf..151476fd036839a416c226599279d0d8 protected boolean addEntity(final Entity entity, final boolean fromDisk, final boolean event) { diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index df23d80d6b18e900414aa02e5c1812f0a10f0fb7..9f581d5bdc3f658694bbd8c80abbce4e27e568d3 100644 +index 64f24d3e0ecb91e0b4df6229354aeac549234f1b..80baa2dff5c1034a72271fc727fdb2acc1b69488 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -381,6 +381,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess diff --git a/leaf-server/minecraft-patches/features/0182-Rail-Optimization-optimized-PoweredRailBlock-logic.patch b/leaf-server/minecraft-patches/features/0180-Rail-Optimization-optimized-PoweredRailBlock-logic.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0182-Rail-Optimization-optimized-PoweredRailBlock-logic.patch rename to leaf-server/minecraft-patches/features/0180-Rail-Optimization-optimized-PoweredRailBlock-logic.patch diff --git a/leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch b/leaf-server/minecraft-patches/features/0181-optimise-ChunkGenerator-getMobsAt.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0183-optimise-ChunkGenerator-getMobsAt.patch rename to leaf-server/minecraft-patches/features/0181-optimise-ChunkGenerator-getMobsAt.patch diff --git a/leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch b/leaf-server/minecraft-patches/features/0182-optimise-getBiome.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0184-optimise-getBiome.patch rename to leaf-server/minecraft-patches/features/0182-optimise-getBiome.patch diff --git a/leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch b/leaf-server/minecraft-patches/features/0183-optimize-mob-spawning.patch similarity index 98% rename from leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch rename to leaf-server/minecraft-patches/features/0183-optimize-mob-spawning.patch index 9982aaaf..80ec7715 100644 --- a/leaf-server/minecraft-patches/features/0185-optimize-mob-spawning.patch +++ b/leaf-server/minecraft-patches/features/0183-optimize-mob-spawning.patch @@ -5,7 +5,7 @@ Subject: [PATCH] optimize mob spawning diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 39a31abe25d2bb56b28462cf25d2d09f7722526c..2f927b422c2c4f2f65d822befe3cbfd9e3bb3708 100644 +index f57f8e610dac80b8095bfc0c7e4b22ff5ad6b13c..55f20122732e88037d24be311469b6cab72c37ad 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -70,7 +70,9 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -19,7 +19,7 @@ index 39a31abe25d2bb56b28462cf25d2d09f7722526c..2f927b422c2c4f2f65d822befe3cbfd9 // Paper start public final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); public int getFullChunksCount() { -@@ -656,13 +658,37 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon +@@ -655,13 +657,37 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon filteredSpawningCategories = List.of(); } diff --git a/leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch b/leaf-server/minecraft-patches/features/0184-optimize-structure-map.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0186-optimize-structure-map.patch rename to leaf-server/minecraft-patches/features/0184-optimize-structure-map.patch diff --git a/leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch b/leaf-server/minecraft-patches/features/0185-throttle-mob-spawning.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0187-throttle-mob-spawning.patch rename to leaf-server/minecraft-patches/features/0185-throttle-mob-spawning.patch diff --git a/leaf-server/minecraft-patches/features/0188-preload-mob-spawning-position.patch b/leaf-server/minecraft-patches/features/0186-preload-mob-spawning-position.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0188-preload-mob-spawning-position.patch rename to leaf-server/minecraft-patches/features/0186-preload-mob-spawning-position.patch diff --git a/leaf-server/minecraft-patches/features/0189-Add-BlockExplosionHitEvent.patch b/leaf-server/minecraft-patches/features/0187-Add-BlockExplosionHitEvent.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0189-Add-BlockExplosionHitEvent.patch rename to leaf-server/minecraft-patches/features/0187-Add-BlockExplosionHitEvent.patch diff --git a/leaf-server/minecraft-patches/features/0190-Old-Blast-Protection-explosion-knockback.patch b/leaf-server/minecraft-patches/features/0188-Old-Blast-Protection-explosion-knockback.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0190-Old-Blast-Protection-explosion-knockback.patch rename to leaf-server/minecraft-patches/features/0188-Old-Blast-Protection-explosion-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0189-Paw-optimization.patch similarity index 84% rename from leaf-server/minecraft-patches/features/0179-Paw-optimization.patch rename to leaf-server/minecraft-patches/features/0189-Paw-optimization.patch index 211e4306..c584cf3f 100644 --- a/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0189-Paw-optimization.patch @@ -9,6 +9,28 @@ Some random optimizations - Only set shuffle random seed if is really used - Secret patches (WIP) +diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java +index 25b6aaa71ff389094d0f44c341f0c6458fc644ff..d899cde3d9964709cb9697eccac986526304b758 100644 +--- a/net/minecraft/Util.java ++++ b/net/minecraft/Util.java +@@ -95,7 +95,7 @@ public class Util { + private static final TracingExecutor BACKGROUND_EXECUTOR = makeExecutor("Main", -1); // Paper - Perf: add priority + private static final TracingExecutor IO_POOL = makeIoExecutor("IO-Worker-", false); + public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool +- private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); ++ //private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); // Leaf - paw optimization - Remove client-only code + // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread + // Leaf start - More virtual threads + public static final ExecutorService PROFILE_EXECUTOR = createProfileExecutor(); /* new Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { +@@ -269,7 +269,7 @@ public class Util { + } + + public static TracingExecutor nonCriticalIoPool() { +- return DOWNLOAD_POOL; ++ return null; // Leaf - paw optimization - Remove client-only code + } + + public static void shutdownExecutors() { diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java index 3dce0665e7438d2994a86450e31fb2a10431df9b..f9634a821fbfaf31a66e0e25973794149b1a5239 100644 --- a/net/minecraft/network/Connection.java @@ -77,10 +99,10 @@ index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194 - // Paper end - detailed watchdog information } diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index f57f8e610dac80b8095bfc0c7e4b22ff5ad6b13c..39a31abe25d2bb56b28462cf25d2d09f7722526c 100644 +index 55f20122732e88037d24be311469b6cab72c37ad..2f927b422c2c4f2f65d822befe3cbfd9e3bb3708 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -504,9 +504,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon +@@ -506,9 +506,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon try { this.collectTickingChunks(list); // Paper start - chunk tick iteration optimisation @@ -126,10 +148,10 @@ index 7955a8fa9c4de139b24c9d53018b055ff4008e02..eb849c57992658005e0f514c6f7923f8 private void tickPassenger(Entity ridingEntity, Entity passengerEntity, final boolean isActive) { // Paper - EAR 2 diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 64f24d3e0ecb91e0b4df6229354aeac549234f1b..df23d80d6b18e900414aa02e5c1812f0a10f0fb7 100644 +index 80baa2dff5c1034a72271fc727fdb2acc1b69488..9f581d5bdc3f658694bbd8c80abbce4e27e568d3 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -1145,31 +1145,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1147,31 +1147,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.onGround; } @@ -161,7 +183,7 @@ index 64f24d3e0ecb91e0b4df6229354aeac549234f1b..df23d80d6b18e900414aa02e5c1812f0 public void move(MoverType type, Vec3 movement) { // Gale start - VMP - skip entity move if movement is zero if (!this.boundingBoxChanged && movement.equals(Vec3.ZERO)) { -@@ -1177,16 +1152,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1179,16 +1154,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Gale end - VMP - skip entity move if movement is zero final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity @@ -178,7 +200,7 @@ index 64f24d3e0ecb91e0b4df6229354aeac549234f1b..df23d80d6b18e900414aa02e5c1812f0 if (this.noPhysics) { this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); } else { -@@ -1307,13 +1273,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1309,13 +1275,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // Gale end - skip negligible planar movement multiplication } } @@ -192,7 +214,7 @@ index 64f24d3e0ecb91e0b4df6229354aeac549234f1b..df23d80d6b18e900414aa02e5c1812f0 } private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) { -@@ -4879,9 +4838,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4881,9 +4840,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void setDeltaMovement(Vec3 deltaMovement) { @@ -202,7 +224,7 @@ index 64f24d3e0ecb91e0b4df6229354aeac549234f1b..df23d80d6b18e900414aa02e5c1812f0 } public void addDeltaMovement(Vec3 addend) { -@@ -4987,9 +4944,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4989,9 +4946,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end - Fix MC-4 if (this.position.x != x || this.position.y != y || this.position.z != z) { diff --git a/leaf-server/minecraft-patches/features/0191-aaa.patch b/leaf-server/minecraft-patches/features/0190-aaa.patch similarity index 98% rename from leaf-server/minecraft-patches/features/0191-aaa.patch rename to leaf-server/minecraft-patches/features/0190-aaa.patch index 7431d1c6..debf2687 100644 --- a/leaf-server/minecraft-patches/features/0191-aaa.patch +++ b/leaf-server/minecraft-patches/features/0190-aaa.patch @@ -5,10 +5,10 @@ Subject: [PATCH] aaa diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 8e693028ef9e512094dbb97d6088d95ead03487d..dc5900903589bc806b89df45ea4de361eed6a9ca 100644 +index e6414643d4c44bcf22d17bf185cc288d2a4f8399..d3889b644de4e9ee31c7a5896c99936287aff170 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -1948,7 +1948,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1947,7 +1947,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc this.awardStat(Stats.SWIM_ONE_CM, rounded); this.causeFoodExhaustion(this.level().spigotConfig.swimMultiplier * (float) rounded * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.SWIM); // CraftBukkit - EntityExhaustionEvent // Spigot } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SmoothTeleport.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SmoothTeleport.java deleted file mode 100644 index b9e6dfca..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SmoothTeleport.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.dreeam.leaf.config.modules.gameplay; - -import org.dreeam.leaf.config.ConfigModules; -import org.dreeam.leaf.config.EnumConfigCategory; -import org.dreeam.leaf.config.annotations.Experimental; - -public class SmoothTeleport extends ConfigModules { - - public String getBasePath() { - return EnumConfigCategory.GAMEPLAY.getBaseKeyName() + ".smooth-teleport"; - } - - @Experimental - public static boolean enabled = false; - - @Override - public void onLoaded() { - enabled = config.getBoolean(getBasePath(), enabled, config.pickStringRegionBased(""" - **Experimental feature** - Whether to make a "smooth teleport" when players changing dimension. - This requires original world and target world have same logical height to work.""", - """ - **实验性功能** - 是否在玩家切换世界时尝试使用 "平滑传送". - 此项要求源世界和目标世界逻辑高度相同才会生效.""" - )); - } -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java deleted file mode 100644 index 22fb0741..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.dreeam.leaf.config.modules.opt; - -import org.dreeam.leaf.config.ConfigModules; -import org.dreeam.leaf.config.EnumConfigCategory; - -public class VT4DownloadPool extends ConfigModules { - - public String getBasePath() { - return EnumConfigCategory.PERF.getBaseKeyName(); - } - - public static boolean enabled = true; - - @Override - public void onLoaded() { - enabled = config.getBoolean(getBasePath() + ".use-virtual-thread-for-download-pool", enabled, - config.pickStringRegionBased( - "Use the new Virtual Thread introduced in JDK 21 for download worker pool.", - "是否为下载工作线程池使用虚拟线程(如果可用)。")); - } -} From c6a9a37ee30ddf0e26349993983da1c84078ae08 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sat, 7 Jun 2025 18:05:18 +0800 Subject: [PATCH 33/57] Remove personal trash --- .../minecraft-patches/features/0190-aaa.patch | 285 ------------------ 1 file changed, 285 deletions(-) delete mode 100644 leaf-server/minecraft-patches/features/0190-aaa.patch diff --git a/leaf-server/minecraft-patches/features/0190-aaa.patch b/leaf-server/minecraft-patches/features/0190-aaa.patch deleted file mode 100644 index debf2687..00000000 --- a/leaf-server/minecraft-patches/features/0190-aaa.patch +++ /dev/null @@ -1,285 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> -Date: Fri, 6 Jun 2025 19:05:40 +0800 -Subject: [PATCH] aaa - - -diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index e6414643d4c44bcf22d17bf185cc288d2a4f8399..d3889b644de4e9ee31c7a5896c99936287aff170 100644 ---- a/net/minecraft/server/level/ServerPlayer.java -+++ b/net/minecraft/server/level/ServerPlayer.java -@@ -1947,7 +1947,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc - this.awardStat(Stats.SWIM_ONE_CM, rounded); - this.causeFoodExhaustion(this.level().spigotConfig.swimMultiplier * (float) rounded * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.SWIM); // CraftBukkit - EntityExhaustionEvent // Spigot - } -- } else if (this.isEyeInFluid(FluidTags.WATER)) { -+ } else if (this.isEyeInFluid(1)) { - int rounded = Math.round((float)Math.sqrt(dx * dx + dy * dy + dz * dz) * 100.0F); - if (rounded > 0) { - this.awardStat(Stats.WALK_UNDER_WATER_ONE_CM, rounded); -diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 9f581d5bdc3f658694bbd8c80abbce4e27e568d3..2c0417d14d276864c1a74f5264751105dde2f34f 100644 ---- a/net/minecraft/world/entity/Entity.java -+++ b/net/minecraft/world/entity/Entity.java -@@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.objects.ReferenceArraySet; - import java.util.ArrayList; - import java.util.Arrays; - import java.util.HashSet; -+import java.util.Iterator; - import java.util.List; - import java.util.Locale; - import java.util.Objects; -@@ -265,6 +266,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - private static final int FLUID_WATER = 1; // Leaf - Optimize isEyeInFluid - private static final int FLUID_LAVA = 2; // Leaf - Optimize isEyeInFluid - private int fluidCache = 0; // Leaf - Optimize isEyeInFluid -+ private int fluidOnEyesCache = 0; - public int invulnerableTime; - protected boolean firstTick = true; - protected final SynchedEntityData entityData; -@@ -1985,8 +1987,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - private void updateFluidOnEyes() { -- this.wasEyeInWater = this.isEyeInFluid(FluidTags.WATER); -- if (org.dreeam.leaf.config.modules.opt.EyeFluidCache.enabled) fluidCache = 0; else this.fluidOnEyes.clear(); // Leaf - Optimize isEyeInFluid -+ this.wasEyeInWater = this.isEyeInFluid(1); -+ this.fluidOnEyesCache = 0; - - double eyeY = this.getEyeY(); - if (!( -@@ -1999,18 +2001,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - FluidState fluidState = this.level().getFluidState(blockPos); - double d = blockPos.getY() + fluidState.getHeight(this.level(), blockPos); - if (d > eyeY) { -- // Leaf start - Optimize isEyeInFluid -- if (org.dreeam.leaf.config.modules.opt.EyeFluidCache.enabled) { -- if (fluidState.is(FluidTags.WATER)) { -- setFluidStatus(FluidTags.WATER, true); -- } -- if (fluidState.is(FluidTags.LAVA)) { -- setFluidStatus(FluidTags.LAVA, true); -- } -+ TagKey[] tagArray = fluidState.getTagsArray(); -+ if (tagArray.length == 0) { -+ this.fluidOnEyesCache = 0; - } else { -- this.fluidOnEyes.addAll(fluidState.getTagsAsSet()); // Leaf - Remove stream in updateFluidOnEyes -+ for (int i = 0; i < tagArray.length; i++) { -+ TagKey tag = tagArray[i]; -+ if (tag == FluidTags.WATER || tag == FluidTags.LAVA) { -+ this.fluidOnEyesCache++; -+ } -+ } - } -- // Leaf end - Optimize isEyeInFluid - } - } - } -@@ -2108,6 +2109,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - - fluidCache = isInFluid ? (fluidCache | bit) : (fluidCache & ~bit); - } -+ -+ public boolean isEyeInFluid(int fluidBit) { -+ return fluidOnEyesCache >= fluidBit; -+ } - // Leaf end - Optimize isEyeInFluid - - public boolean isInLava() { -@@ -4442,7 +4447,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - - @Nullable - public Entity getFirstPassenger() { -- return this.passengers.isEmpty() ? null : this.passengers.get(0); -+ return this.passengers.isEmpty() ? null : this.passengers.getFirst(); - } - - public boolean hasPassenger(Entity entity) { -diff --git a/net/minecraft/world/entity/ExperienceOrb.java b/net/minecraft/world/entity/ExperienceOrb.java -index a43e5190c0f9ae14ccecccd5b58dc0e17f18b0a1..bd2c7fae6858ba446760f7e18390090434b660e0 100644 ---- a/net/minecraft/world/entity/ExperienceOrb.java -+++ b/net/minecraft/world/entity/ExperienceOrb.java -@@ -133,7 +133,7 @@ public class ExperienceOrb extends Entity { - this.xo = this.getX(); - this.yo = this.getY(); - this.zo = this.getZ(); -- if (this.isEyeInFluid(FluidTags.WATER)) { -+ if (this.isEyeInFluid(1)) { - this.setUnderwaterMovement(); - } else { - this.applyGravity(); -diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index 156d0c14eedf2f79e4276cc4065e19a43699b965..6072d713d08ea3172eee039b35d1c122813cffa3 100644 ---- a/net/minecraft/world/entity/LivingEntity.java -+++ b/net/minecraft/world/entity/LivingEntity.java -@@ -483,7 +483,7 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf - } - } - -- if (this.isEyeInFluid(FluidTags.WATER) -+ if (this.isEyeInFluid(1) - && !this.level().getBlockState(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())).is(Blocks.BUBBLE_COLUMN)) { - boolean flag1 = !this.canBreatheUnderwater() - && !MobEffectUtil.hasWaterBreathing(this) -diff --git a/net/minecraft/world/entity/animal/AbstractFish.java b/net/minecraft/world/entity/animal/AbstractFish.java -index 28ae152125ed83d8917674b6068f227f87890f30..4b0a8743172b790fb42b47ecef33e52d0cb3c194 100644 ---- a/net/minecraft/world/entity/animal/AbstractFish.java -+++ b/net/minecraft/world/entity/animal/AbstractFish.java -@@ -179,7 +179,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { - - @Override - public void vanillaTick() { // Purpur - Ridables -- if (this.fish.isEyeInFluid(FluidTags.WATER)) { -+ if (this.fish.isEyeInFluid(1)) { - this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0, 0.005, 0.0)); - } - -diff --git a/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/net/minecraft/world/entity/animal/horse/AbstractHorse.java -index 56dc7011ed07f0bd5870fbadde2b5c0c630c5edd..8d02515297ead6073d5eb60d58ff040cd101f3d8 100644 ---- a/net/minecraft/world/entity/animal/horse/AbstractHorse.java -+++ b/net/minecraft/world/entity/animal/horse/AbstractHorse.java -@@ -71,6 +71,7 @@ import net.minecraft.world.level.ServerLevelAccessor; - import net.minecraft.world.level.block.Blocks; - import net.minecraft.world.level.block.SoundType; - import net.minecraft.world.level.block.state.BlockState; -+import net.minecraft.world.level.chunk.LevelChunk; - import net.minecraft.world.level.gameevent.GameEvent; - import net.minecraft.world.phys.AABB; - import net.minecraft.world.phys.Vec2; -@@ -1202,13 +1203,16 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, - double d1 = this.getBoundingBox().minY; - double d2 = this.getZ() + direction.z; - BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); -- -+ net.minecraft.world.level.chunk.ChunkAccess chunk = this.level().getChunkIfLoadedImmediately((int) d >> 4, (int) d2 >> 4); -+ if (chunk == null) { -+ return null; -+ } - for (Pose pose : passenger.getDismountPoses()) { - mutableBlockPos.set(d, d1, d2); - double d3 = this.getBoundingBox().maxY + 0.75; - - do { -- double blockFloorHeight = this.level().getBlockFloorHeight(mutableBlockPos); -+ double blockFloorHeight = ((LevelChunk) chunk).getBlockFloorHeight(mutableBlockPos); - if (mutableBlockPos.getY() + blockFloorHeight > d3) { - break; - } -diff --git a/net/minecraft/world/entity/animal/horse/SkeletonHorse.java b/net/minecraft/world/entity/animal/horse/SkeletonHorse.java -index d5ee5b816efc1b3b17b94227f8ea05ab7a2ccad7..93702017d2769d8207864725e73c86cf54cda8ff 100644 ---- a/net/minecraft/world/entity/animal/horse/SkeletonHorse.java -+++ b/net/minecraft/world/entity/animal/horse/SkeletonHorse.java -@@ -108,7 +108,7 @@ public class SkeletonHorse extends AbstractHorse { - - @Override - protected SoundEvent getAmbientSound() { -- return this.isEyeInFluid(FluidTags.WATER) ? SoundEvents.SKELETON_HORSE_AMBIENT_WATER : SoundEvents.SKELETON_HORSE_AMBIENT; -+ return this.isEyeInFluid(1) ? SoundEvents.SKELETON_HORSE_AMBIENT_WATER : SoundEvents.SKELETON_HORSE_AMBIENT; - } - - @Override -diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java -index f4fa19c6352e44a624e81dc201b1d7d710c2d9d2..3b070af23802dcba9a356ea22e2898ab6099b86b 100644 ---- a/net/minecraft/world/entity/monster/Strider.java -+++ b/net/minecraft/world/entity/monster/Strider.java -@@ -405,7 +405,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - - @Override - protected boolean canAddPassenger(Entity passenger) { -- return !this.isVehicle() && !this.isEyeInFluid(FluidTags.LAVA); -+ return !this.isVehicle() && !this.isEyeInFluid(2); - } - - @Override -diff --git a/net/minecraft/world/entity/monster/Witch.java b/net/minecraft/world/entity/monster/Witch.java -index e4353c64732067198f082cdd266c1f1ee1fe4f4e..2a4a1d6a94e7712f6c861fe70f65a0a76014ad0b 100644 ---- a/net/minecraft/world/entity/monster/Witch.java -+++ b/net/minecraft/world/entity/monster/Witch.java -@@ -178,7 +178,7 @@ public class Witch extends Raider implements RangedAttackMob { - } - } else { - Holder holder = null; -- if (this.random.nextFloat() < 0.15F && this.isEyeInFluid(FluidTags.WATER) && !this.hasEffect(MobEffects.WATER_BREATHING)) { -+ if (this.random.nextFloat() < 0.15F && this.isEyeInFluid(1) && !this.hasEffect(MobEffects.WATER_BREATHING)) { - holder = Potions.WATER_BREATHING; - } else if (this.random.nextFloat() < 0.15F - && (this.isOnFire() || this.getLastDamageSource() != null && this.getLastDamageSource().is(DamageTypeTags.IS_FIRE)) -diff --git a/net/minecraft/world/entity/monster/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java -index 9c83f1406c0aaad36383a23cebf270b8dc6ced33..2a16248539718d3945d3b2b4d2da161f632831ff 100644 ---- a/net/minecraft/world/entity/monster/Zombie.java -+++ b/net/minecraft/world/entity/monster/Zombie.java -@@ -283,7 +283,7 @@ public class Zombie extends Monster { - this.doUnderWaterConversion(); - } - } else if (this.convertsInWater()) { -- if (this.isEyeInFluid(FluidTags.WATER)) { -+ if (this.isEyeInFluid(1)) { - this.inWaterTime++; - if (this.inWaterTime >= 600) { - this.startUnderWaterConversion(300); -diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 76d85632e1364e71a2aa15dbef41c62422ffd7af..335f99ecd313b4e1ad5b997a84b65331a2253574 100644 ---- a/net/minecraft/world/entity/player/Player.java -+++ b/net/minecraft/world/entity/player/Player.java -@@ -374,7 +374,7 @@ public abstract class Player extends LivingEntity { - this.lastItemInMainHand = mainHandItem.copy(); - } - -- if (!this.isEyeInFluid(FluidTags.WATER) && this.isEquipped(Items.TURTLE_HELMET)) { -+ if (!this.isEyeInFluid(1) && this.isEquipped(Items.TURTLE_HELMET)) { - this.turtleHelmetTick(); - } - -@@ -414,7 +414,7 @@ public abstract class Player extends LivingEntity { - } - - protected boolean updateIsUnderwater() { -- this.wasUnderwater = this.isEyeInFluid(FluidTags.WATER); -+ this.wasUnderwater = this.isEyeInFluid(1); - return this.wasUnderwater; - } - -@@ -851,7 +851,7 @@ public abstract class Player extends LivingEntity { - } - - destroySpeed *= (float)this.getAttributeValue(Attributes.BLOCK_BREAK_SPEED); -- if (this.isEyeInFluid(FluidTags.WATER)) { -+ if (this.isEyeInFluid(1)) { - destroySpeed *= (float)this.getAttribute(Attributes.SUBMERGED_MINING_SPEED).getValue(); - } - -diff --git a/net/minecraft/world/entity/vehicle/AbstractBoat.java b/net/minecraft/world/entity/vehicle/AbstractBoat.java -index b1b312e45ed4514eaa6fb3941af64b641220c5bd..636b5dfe1761f4d13b6af7b11f610ff5c18b36d6 100644 ---- a/net/minecraft/world/entity/vehicle/AbstractBoat.java -+++ b/net/minecraft/world/entity/vehicle/AbstractBoat.java -@@ -847,7 +847,7 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable { - - @Override - protected boolean canAddPassenger(Entity passenger) { -- return this.getPassengers().size() < this.getMaxPassengers() && !this.isEyeInFluid(FluidTags.WATER); -+ return this.getPassengers().size() < this.getMaxPassengers() && !this.isEyeInFluid(1); - } - - protected int getMaxPassengers() { -diff --git a/net/minecraft/world/level/material/FluidState.java b/net/minecraft/world/level/material/FluidState.java -index 06581fe010ca722d62d0b6d3c44d845f9db0231f..38600ed61757da54295e712a37a3bba3c71fe0a1 100644 ---- a/net/minecraft/world/level/material/FluidState.java -+++ b/net/minecraft/world/level/material/FluidState.java -@@ -160,8 +160,14 @@ public final class FluidState extends StateHolder implements - } - - // Leaf start - Remove stream in updateFluidOnEyes -- public java.util.Set> getTagsAsSet() { -- return this.owner.builtInRegistryHolder().tagsAsSet(); -+ public TagKey[] getTagsArray() { -+ java.util.Set> tags = this.owner.builtInRegistryHolder().tagsAsSet(); -+ TagKey[] array = new TagKey[tags.size()]; -+ int i = 0; -+ for (TagKey tag : tags) { -+ array[i++] = tag; -+ } -+ return array; - } - // Leaf end - Remove stream in updateFluidOnEyes - } From 355cb791a6feac2955d12b41df375fd957a8cf34 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 7 Jun 2025 12:49:27 +0200 Subject: [PATCH 34/57] disable async target find when pwt enabled --- .../leaf/config/modules/async/AsyncTargetFinding.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java index c2ebeb42..c22f1ce4 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java @@ -1,8 +1,8 @@ - package org.dreeam.leaf.config.modules.async; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; +import org.dreeam.leaf.config.LeafConfig; public class AsyncTargetFinding extends ConfigModules { @@ -33,6 +33,11 @@ public class AsyncTargetFinding extends ConfigModules { asyncTargetFindingInitialized = true; enabled = config.getBoolean(getBasePath() + ".enabled", enabled); + // Disable if parallel world ticking is enabled, as they are incompatible. + if (enabled && SparklyPaperParallelWorldTicking.enabled) { + LeafConfig.LOGGER.warn("Async Target Finding is incompatible with Parallel World Ticking. Disabling Async Target Finding automatically."); + enabled = false; + } alertOther = config.getBoolean(getBasePath() + ".async-alert-other", true); searchBlock = config.getBoolean(getBasePath() + ".async-search-block", true); searchEntity = config.getBoolean(getBasePath() + ".async-search-entity", true); From d358a4b657517e422e4a87a647703a42389b34b7 Mon Sep 17 00:00:00 2001 From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Sun, 8 Jun 2025 00:56:07 +1400 Subject: [PATCH 35/57] Use UUID for cure reputation Dont merge into 1.21.5, I will do it --- .../0190-Use-UUID-for-cure-reputation.patch | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch diff --git a/leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch b/leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch new file mode 100644 index 00000000..d66b6c8a --- /dev/null +++ b/leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch @@ -0,0 +1,86 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> +Date: Tue, 9 Nov 2077 00:00:00 +0800 +Subject: [PATCH] Use UUID for cure reputation + + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index eb849c57992658005e0f514c6f7923f8ca43bebf..8043bc04fea74af4dac7fd3333353a08434b0b08 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -2529,6 +2529,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + host.onReputationEventFrom(type, target); + } + ++ // Leaf start - Use UUID for cure reputation ++ public void onReputationEvent(ReputationEventType type, UUID target, ReputationEventHandler host) { ++ host.onReputationEventFrom(type, target); ++ } ++ // Leaf end - Use UUID for cure reputation ++ + public void saveDebugReport(Path path) throws IOException { + ChunkMap chunkMap = this.getChunkSource().chunkMap; + +diff --git a/net/minecraft/world/entity/ReputationEventHandler.java b/net/minecraft/world/entity/ReputationEventHandler.java +index 2d704ba14ce676a3e721e8ce8dd3fe76acea7db1..9b47635584cd1326c13c23201a7d2c1a8541b18f 100644 +--- a/net/minecraft/world/entity/ReputationEventHandler.java ++++ b/net/minecraft/world/entity/ReputationEventHandler.java +@@ -4,4 +4,5 @@ import net.minecraft.world.entity.ai.village.ReputationEventType; + + public interface ReputationEventHandler { + void onReputationEventFrom(ReputationEventType type, Entity target); ++ void onReputationEventFrom(ReputationEventType type, java.util.UUID target); // Leaf - Use UUID for cure reputation + } +diff --git a/net/minecraft/world/entity/monster/ZombieVillager.java b/net/minecraft/world/entity/monster/ZombieVillager.java +index d4b6c93f9f0e109be300164c4fd9167aba2d951c..21b1ceb34973be6364c889f85c405717517ef462 100644 +--- a/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -312,7 +312,7 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + Player playerByUuid = serverLevel.getGlobalPlayerByUUID(this.conversionStarter); // Paper - check global player list where appropriate + if (playerByUuid instanceof ServerPlayer) { + CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((ServerPlayer)playerByUuid, this, villager); +- serverLevel.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, playerByUuid, villager); ++ serverLevel.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, this.conversionStarter, villager); // Leaf - Use UUID for cure reputation + } + } + +diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java +index acf8059017f4e45c307a113abed36c59b231d9a6..b46a94c78bb487a102f0156ee9329f768794d28b 100644 +--- a/net/minecraft/world/entity/npc/Villager.java ++++ b/net/minecraft/world/entity/npc/Villager.java +@@ -1091,6 +1091,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + + @Override + public void onReputationEventFrom(ReputationEventType type, Entity target) { ++ // Leaf start - Use UUID for reputation - Redirect to UUID method ++ if (true) { ++ this.onReputationEventFrom(type, target.getUUID()); ++ return; ++ } ++ // Leaf end - Use UUID for reputation - Redirect to UUID method + if (type == ReputationEventType.ZOMBIE_VILLAGER_CURED) { + this.gossips.add(target.getUUID(), GossipType.MAJOR_POSITIVE, 20); + this.gossips.add(target.getUUID(), GossipType.MINOR_POSITIVE, 25); +@@ -1103,6 +1109,22 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + } + ++ // Leaf start - Use UUID for cure reputation ++ @Override ++ public void onReputationEventFrom(ReputationEventType type, java.util.UUID target) { ++ if (type == ReputationEventType.ZOMBIE_VILLAGER_CURED) { ++ this.gossips.add(target, GossipType.MAJOR_POSITIVE, 20); ++ this.gossips.add(target, GossipType.MINOR_POSITIVE, 25); ++ } else if (type == ReputationEventType.TRADE) { ++ this.gossips.add(target, GossipType.TRADING, 2); ++ } else if (type == ReputationEventType.VILLAGER_HURT) { ++ this.gossips.add(target, GossipType.MINOR_NEGATIVE, 25); ++ } else if (type == ReputationEventType.VILLAGER_KILLED) { ++ this.gossips.add(target, GossipType.MAJOR_NEGATIVE, 25); ++ } ++ } ++ // Leaf end - Use UUID for cure reputation ++ + @Override + public int getVillagerXp() { + return this.villagerXp; From e2eaa9b746e86bc11261d1e2a93fb63cc3cf9ba8 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 7 Jun 2025 20:38:20 +0900 Subject: [PATCH 36/57] dump pwt thread --- .../features/0043-dump-pwt-thread.patch | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 leaf-server/paper-patches/features/0043-dump-pwt-thread.patch diff --git a/leaf-server/paper-patches/features/0043-dump-pwt-thread.patch b/leaf-server/paper-patches/features/0043-dump-pwt-thread.patch new file mode 100644 index 00000000..3af35014 --- /dev/null +++ b/leaf-server/paper-patches/features/0043-dump-pwt-thread.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Sat, 7 Jun 2025 20:33:16 +0900 +Subject: [PATCH] dump pwt thread + + +diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java +index e45d5da04428c26fc6aa97fba974bde3573d7950..745a9afcdb8ea3047c1fcdc91971ba8f034cbd39 100644 +--- a/src/main/java/org/spigotmc/WatchdogThread.java ++++ b/src/main/java/org/spigotmc/WatchdogThread.java +@@ -8,6 +8,7 @@ import java.lang.management.ThreadInfo; + import java.util.logging.Level; + import java.util.logging.Logger; + import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.level.ServerLevel; + import org.bukkit.Bukkit; + import org.bukkit.craftbukkit.CraftServer; + +@@ -124,8 +125,21 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre + logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Leaf!):"); // Paper // Gale - branding changes // Leaf - Rebrand + FeatureHooks.dumpAllChunkLoadInfo(MinecraftServer.getServer(), isLongTimeout); // Paper - log detailed tick information + WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE), logger); ++ // Leaf start + logger.log(Level.SEVERE, "------------------------------"); +- ++ logger.log(Level.SEVERE, "Parallel world ticking thread dump"); ++ for (Thread thread : org.apache.commons.lang3.ThreadUtils.getAllThreads()) { ++ if (MinecraftServer.getServer().serverThread == thread || thread instanceof WatchdogThread) { ++ continue; ++ } ++ if (thread instanceof ca.spottedleaf.moonrise.common.util.TickThread tickThread) { ++ if (tickThread instanceof ServerLevelTickThread tickThread1) { ++ WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(tickThread1.getId(), Integer.MAX_VALUE), logger); ++ } ++ } ++ } ++ logger.log(Level.SEVERE, "------------------------------"); ++ // Leaf end + // Paper start - Only print full dump on long timeouts + if (isLongTimeout) { + logger.log(Level.SEVERE, "Entire Thread Dump:"); From 1fb0c2e98cdd5b09b5b9fc21da79fdc29329006b Mon Sep 17 00:00:00 2001 From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Sun, 8 Jun 2025 02:38:20 +1400 Subject: [PATCH 37/57] SAVIOR OF SONG --- .../0190-Use-UUID-for-cure-reputation.patch | 56 +++---------------- 1 file changed, 9 insertions(+), 47 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch b/leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch index d66b6c8a..abb59c03 100644 --- a/leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch +++ b/leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch @@ -4,70 +4,32 @@ Date: Tue, 9 Nov 2077 00:00:00 +0800 Subject: [PATCH] Use UUID for cure reputation -diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index eb849c57992658005e0f514c6f7923f8ca43bebf..8043bc04fea74af4dac7fd3333353a08434b0b08 100644 ---- a/net/minecraft/server/level/ServerLevel.java -+++ b/net/minecraft/server/level/ServerLevel.java -@@ -2529,6 +2529,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - host.onReputationEventFrom(type, target); - } - -+ // Leaf start - Use UUID for cure reputation -+ public void onReputationEvent(ReputationEventType type, UUID target, ReputationEventHandler host) { -+ host.onReputationEventFrom(type, target); -+ } -+ // Leaf end - Use UUID for cure reputation -+ - public void saveDebugReport(Path path) throws IOException { - ChunkMap chunkMap = this.getChunkSource().chunkMap; - -diff --git a/net/minecraft/world/entity/ReputationEventHandler.java b/net/minecraft/world/entity/ReputationEventHandler.java -index 2d704ba14ce676a3e721e8ce8dd3fe76acea7db1..9b47635584cd1326c13c23201a7d2c1a8541b18f 100644 ---- a/net/minecraft/world/entity/ReputationEventHandler.java -+++ b/net/minecraft/world/entity/ReputationEventHandler.java -@@ -4,4 +4,5 @@ import net.minecraft.world.entity.ai.village.ReputationEventType; - - public interface ReputationEventHandler { - void onReputationEventFrom(ReputationEventType type, Entity target); -+ void onReputationEventFrom(ReputationEventType type, java.util.UUID target); // Leaf - Use UUID for cure reputation - } diff --git a/net/minecraft/world/entity/monster/ZombieVillager.java b/net/minecraft/world/entity/monster/ZombieVillager.java -index d4b6c93f9f0e109be300164c4fd9167aba2d951c..21b1ceb34973be6364c889f85c405717517ef462 100644 +index d4b6c93f9f0e109be300164c4fd9167aba2d951c..301228895f0347ec514cefc8a11d8ca7bc2f2225 100644 --- a/net/minecraft/world/entity/monster/ZombieVillager.java +++ b/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -312,7 +312,7 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { +@@ -310,9 +310,10 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + villager.refreshBrain(serverLevel); + if (this.conversionStarter != null) { Player playerByUuid = serverLevel.getGlobalPlayerByUUID(this.conversionStarter); // Paper - check global player list where appropriate ++ villager.onReputationEventFromUUID(ReputationEventType.ZOMBIE_VILLAGER_CURED, this.conversionStarter); // Leaf - Use UUID for cure reputation if (playerByUuid instanceof ServerPlayer) { CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((ServerPlayer)playerByUuid, this, villager); - serverLevel.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, playerByUuid, villager); -+ serverLevel.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, this.conversionStarter, villager); // Leaf - Use UUID for cure reputation ++ // serverLevel.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, playerByUuid, villager); // Leaf - move up } } diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java -index acf8059017f4e45c307a113abed36c59b231d9a6..b46a94c78bb487a102f0156ee9329f768794d28b 100644 +index acf8059017f4e45c307a113abed36c59b231d9a6..3d6ffc2f9bba9cd81adf34e12840a08fa1fa9245 100644 --- a/net/minecraft/world/entity/npc/Villager.java +++ b/net/minecraft/world/entity/npc/Villager.java -@@ -1091,6 +1091,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - - @Override - public void onReputationEventFrom(ReputationEventType type, Entity target) { -+ // Leaf start - Use UUID for reputation - Redirect to UUID method -+ if (true) { -+ this.onReputationEventFrom(type, target.getUUID()); -+ return; -+ } -+ // Leaf end - Use UUID for reputation - Redirect to UUID method - if (type == ReputationEventType.ZOMBIE_VILLAGER_CURED) { - this.gossips.add(target.getUUID(), GossipType.MAJOR_POSITIVE, 20); - this.gossips.add(target.getUUID(), GossipType.MINOR_POSITIVE, 25); -@@ -1103,6 +1109,22 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -1103,6 +1103,21 @@ public class Villager extends AbstractVillager implements ReputationEventHandler } } + // Leaf start - Use UUID for cure reputation -+ @Override -+ public void onReputationEventFrom(ReputationEventType type, java.util.UUID target) { ++ public void onReputationEventFromUUID(ReputationEventType type, java.util.UUID target) { + if (type == ReputationEventType.ZOMBIE_VILLAGER_CURED) { + this.gossips.add(target, GossipType.MAJOR_POSITIVE, 20); + this.gossips.add(target, GossipType.MINOR_POSITIVE, 25); From 30d128f30f60085f7cdd4bd0ada913215376d594 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 7 Jun 2025 17:56:33 +0200 Subject: [PATCH 38/57] faster maps for brain --- ...70-Replace-brain-maps-with-optimized-collection.patch | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0070-Replace-brain-maps-with-optimized-collection.patch b/leaf-server/minecraft-patches/features/0070-Replace-brain-maps-with-optimized-collection.patch index 7e942c78..653864fd 100644 --- a/leaf-server/minecraft-patches/features/0070-Replace-brain-maps-with-optimized-collection.patch +++ b/leaf-server/minecraft-patches/features/0070-Replace-brain-maps-with-optimized-collection.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Replace brain maps with optimized collection diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java -index 083eb9a7a0bc14d30db944f356d98ca552fa1784..c561b749fb9b76ba9b1e9689089b743248c65d50 100644 +index 083eb9a7a0bc14d30db944f356d98ca552fa1784..778e3b99a7f941a53b87cbec510db8deed5d77c8 100644 --- a/net/minecraft/world/entity/ai/Brain.java +++ b/net/minecraft/world/entity/ai/Brain.java @@ -45,14 +45,18 @@ public class Brain { @@ -14,11 +14,12 @@ index 083eb9a7a0bc14d30db944f356d98ca552fa1784..c561b749fb9b76ba9b1e9689089b7432 private static final int SCHEDULE_UPDATE_DELAY = 20; - private final Map, Optional>> memories = Maps.newHashMap(); - private final Map>, Sensor> sensors = Maps.newLinkedHashMap(); +- private final Map>>> availableBehaviorsByPriority = Maps.newTreeMap(); + // Leaf start - Replace brain maps with optimized collection -+ private final Map, Optional>> memories = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); -+ private final Map>, Sensor> sensors = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>(); ++ private final Map, Optional>> memories = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(); ++ private final Map>, Sensor> sensors = new it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap<>(); ++ private final Map>>> availableBehaviorsByPriority = new it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap<>(); + // Leaf end - Replace brain maps with optimized collection - private final Map>>> availableBehaviorsByPriority = Maps.newTreeMap(); private Schedule schedule = Schedule.EMPTY; - private final Map, MemoryStatus>>> activityRequirements = Maps.newHashMap(); - private final Map>> activityMemoriesToEraseWhenStopped = Maps.newHashMap(); From 08d67817b843d44648d2a5a99b1223a0ed124dc7 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 7 Jun 2025 19:43:44 +0200 Subject: [PATCH 39/57] cache potential behaviours --- ...1-Cache-potential-behaviors-in-Brain.patch | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0191-Cache-potential-behaviors-in-Brain.patch diff --git a/leaf-server/minecraft-patches/features/0191-Cache-potential-behaviors-in-Brain.patch b/leaf-server/minecraft-patches/features/0191-Cache-potential-behaviors-in-Brain.patch new file mode 100644 index 00000000..7b7cc23d --- /dev/null +++ b/leaf-server/minecraft-patches/features/0191-Cache-potential-behaviors-in-Brain.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sat, 7 Jun 2025 19:21:19 +0200 +Subject: [PATCH] Cache potential behaviors in Brain + + +diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java +index 457962b2113726c3cae7fed9c1926e8537834c3c..68eadc20d0bb80cac81dd3b6127c46ea484d39b0 100644 +--- a/net/minecraft/world/entity/ai/Brain.java ++++ b/net/minecraft/world/entity/ai/Brain.java +@@ -60,6 +60,7 @@ public class Brain { + private Activity defaultActivity = Activity.IDLE; + private long lastScheduleUpdate = -9999L; + ++ private ObjectArrayList> cachedPotentialBehaviors; + public static Brain.Provider provider( + Collection> memoryTypes, Collection>> sensorTypes + ) { +@@ -166,6 +167,7 @@ public class Brain { + for (Brain.MemoryValue memoryValue : memoryValues) { + memoryValue.setMemoryInternal(this); + } ++ this.invalidateBehaviorCache(); + } + + public DataResult serializeStart(DynamicOps ops) { +@@ -343,6 +345,7 @@ public class Brain { + this.activeActivities.clear(); + this.activeActivities.addAll(this.coreActivities); + this.activeActivities.add(activity); ++ this.invalidateBehaviorCache(); + } + } + +@@ -423,11 +426,13 @@ public class Brain { + .computeIfAbsent(activity, activity1 -> new it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet<>()) // Leaf - Replace brain activity maps with optimized collection + .add((BehaviorControl)pair.getSecond()); + } ++ this.invalidateBehaviorCache(); + } + + @VisibleForTesting + public void removeAllBehaviors() { + this.availableBehaviorsByPriority.clear(); ++ this.invalidateBehaviorCache(); + } + + public boolean isActive(Activity activity) { +@@ -481,30 +486,40 @@ public class Brain { + } + } + +- private void startEachNonRunningBehavior(ServerLevel level, E entity) { +- // Leaf start - Collect then startEachNonRunningBehavior in Brain +- final long gameTime = level.getGameTime(); +- List> behaviorsToStart = new ObjectArrayList<>(); +- +- for (Activity activeActivity : this.activeActivities) { +- for (Map>> priorityMap : this.availableBehaviorsByPriority.values()) { +- Set> behaviors = priorityMap.get(activeActivity); +- +- if (behaviors != null && !behaviors.isEmpty()) { +- for (BehaviorControl behaviorControl : behaviors) { +- if (behaviorControl.getStatus() == Behavior.Status.STOPPED) { +- behaviorsToStart.add(behaviorControl); +- } ++ private void invalidateBehaviorCache() { ++ this.cachedPotentialBehaviors = null; ++ } ++ ++ private void rebuildBehaviorCache() { ++ this.cachedPotentialBehaviors = new ObjectArrayList<>(30); ++ ++ for (Map>> map : this.availableBehaviorsByPriority.values()) { ++ for (Map.Entry>> entry : map.entrySet()) { ++ Activity activity = entry.getKey(); ++ if (this.activeActivities.contains(activity)) { ++ for (BehaviorControl task : entry.getValue()) { ++ this.cachedPotentialBehaviors.add(task); + } + } + } + } +- if (!behaviorsToStart.isEmpty()) { +- for (BehaviorControl behaviorControl : behaviorsToStart) { +- behaviorControl.tryStart(level, entity, gameTime); ++ } ++ ++ private ObjectArrayList> getPotentialBehaviors() { ++ if (this.cachedPotentialBehaviors == null) { ++ this.rebuildBehaviorCache(); ++ } ++ return this.cachedPotentialBehaviors; ++ } ++ ++ private void startEachNonRunningBehavior(ServerLevel level, E entity) { ++ long startTime = level.getGameTime(); ++ ++ for (BehaviorControl task : this.getPotentialBehaviors()) { ++ if (task.getStatus() == Behavior.Status.STOPPED) { ++ task.tryStart(level, entity, startTime); + } + } +- // Leaf end - Collect then startEachNonRunningBehavior in Brain + } + + private void tickEachRunningBehavior(ServerLevel level, E entity) { From da48e6e0fb876e9607d8fa2458a8805885afc636 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 7 Jun 2025 23:11:35 +0200 Subject: [PATCH 40/57] remove tick control on getRunningBehaviors --- .../0120-Alternative-Brain-Behaviour.patch | 72 ------------------- ...eligible-players-for-despawn-checks.patch} | 0 ...-Slightly-optimise-getNearestPlayer.patch} | 0 ...y-to-pre-populate-the-size-of-ticki.patch} | 0 ...pre-filtered-ticking-chunks-list-as.patch} | 0 ...writeLongArray-during-chunk-loading.patch} | 0 ...5-Improve-sorting-in-SortedArraySet.patch} | 0 ... 0126-Make-removeIf-slightly-faster.patch} | 0 ...atch => 0127-Optimize-LinearPalette.patch} | 0 ...128-Slightly-optimized-VarInt-write.patch} | 0 ...te-ClientboundLightUpdatePacketData.patch} | 0 ...send.patch => 0130-Async-chunk-send.patch} | 0 ...atch => 0131-Spawner-Configurations.patch} | 0 ...SparklyPaper-Parallel-world-ticking.patch} | 0 ...-SparklyPaper-Track-each-world-MSPT.patch} | 0 ...lled-Projectile-Events-still-consum.patch} | 0 ...ndInteract-and-NearestVisibleLiving.patch} | 0 ...emove-streams-on-InsideBrownianWalk.patch} | 0 ...=> 0137-Use-BFS-on-getSlopeDistance.patch} | 0 ...r-PR-Throttle-failed-spawn-attempts.patch} | 0 ...BlockEntity-ticking-isRemoved-check.patch} | 0 ...0-Raytrace-AntiXray-SDK-integration.patch} | 0 ...timize-addOrUpdateTransientModifier.patch} | 0 ... => 0142-Optimize-ContextMap.create.patch} | 0 ...Micro-optimizations-for-random-tick.patch} | 0 ...n-updateConnectedPlayersWithinRange.patch} | 0 ...45-Remove-streams-on-PlayerDetector.patch} | 0 ...se-direct-iteration-on-Sensing.tick.patch} | 0 ...7-Optimise-non-flush-packet-sending.patch} | 0 ...unk-retrieving-in-entity-fluid-push.patch} | 0 ...-Null-handling-on-MultifaceSpreader.patch} | 0 ....patch => 0150-More-virtual-threads.patch} | 0 ....patch => 0151-Async-target-finding.patch} | 0 ...imize-ThreadedTicketLevelPropagator.patch} | 0 ...EffectUtil-getDigSpeedAmplification.patch} | 0 ...patch => 0154-Optimise-chunkUnloads.patch} | 0 ...55-Optimize-BlockEntityType-isValid.patch} | 0 ...t-on-player-join-to-avoid-chunk-loa.patch} | 0 ...rPR-Fix-save-load-NaN-Entity-Motion.patch} | 0 ...erPR-Fix-unnecessary-map-data-saves.patch} | 0 ...heck-inside-blocks-and-traverse-blo.patch} | 0 ...yList-implementation-to-BasicEntity.patch} | 0 ...ol-Core.patch => 0161-Protocol-Core.patch} | 0 ... => 0162-Reduce-PlayerChunk-Updates.patch} | 0 ... 0163-Async-switch-connection-state.patch} | 0 ...timise-BlockEntities-tickersInLevel.patch} | 0 ...e-cactus-can-even-survive-being-pla.patch} | 0 ...0166-Flush-location-while-knockback.patch} | 0 ...tch => 0167-Only-tick-items-at-hand.patch} | 0 ...art-sort-items-in-NearestItemSensor.patch} | 0 ...169-Optimise-player-movement-checks.patch} | 0 ...=> 0170-Remove-streams-in-MobSensor.patch} | 0 ...71-Remove-streams-in-TemptingSensor.patch} | 0 ...se-HashedList-on-WeightedRandomList.patch} | 0 ...-death-item-drop-knockback-settings.patch} | 0 ...-Optimize-getScaledTrackingDistance.patch} | 0 ...ptimize-SynchedEntityData-packDirty.patch} | 0 ...patch => 0176-Optimize-isEyeInFluid.patch} | 0 ...patch => 0177-Cache-block-path-type.patch} | 0 ...ch => 0178-optimize-getEntityStatus.patch} | 0 ...on-optimized-PoweredRailBlock-logic.patch} | 0 ...0-optimise-ChunkGenerator-getMobsAt.patch} | 0 ...ome.patch => 0181-optimise-getBiome.patch} | 0 ...patch => 0182-optimize-mob-spawning.patch} | 0 ...atch => 0183-optimize-structure-map.patch} | 0 ...patch => 0184-throttle-mob-spawning.patch} | 0 ... 0185-preload-mob-spawning-position.patch} | 0 ... => 0186-Add-BlockExplosionHitEvent.patch} | 0 ...last-Protection-explosion-knockback.patch} | 0 ...tion.patch => 0188-Paw-optimization.patch} | 0 ...> 0189-Use-UUID-for-cure-reputation.patch} | 0 ...-Cache-potential-behaviors-in-Brain.patch} | 8 +-- .../opt/BrainRunningBehaviorCacheUpdate.java | 21 ------ 73 files changed, 4 insertions(+), 97 deletions(-) delete mode 100644 leaf-server/minecraft-patches/features/0120-Alternative-Brain-Behaviour.patch rename leaf-server/minecraft-patches/features/{0121-Cache-eligible-players-for-despawn-checks.patch => 0120-Cache-eligible-players-for-despawn-checks.patch} (100%) rename leaf-server/minecraft-patches/features/{0122-Slightly-optimise-getNearestPlayer.patch => 0121-Slightly-optimise-getNearestPlayer.patch} (100%) rename leaf-server/minecraft-patches/features/{0123-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch => 0122-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch} (100%) rename leaf-server/minecraft-patches/features/{0124-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch => 0123-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch} (100%) rename leaf-server/minecraft-patches/features/{0125-Bulk-writes-to-writeLongArray-during-chunk-loading.patch => 0124-Bulk-writes-to-writeLongArray-during-chunk-loading.patch} (100%) rename leaf-server/minecraft-patches/features/{0126-Improve-sorting-in-SortedArraySet.patch => 0125-Improve-sorting-in-SortedArraySet.patch} (100%) rename leaf-server/minecraft-patches/features/{0127-Make-removeIf-slightly-faster.patch => 0126-Make-removeIf-slightly-faster.patch} (100%) rename leaf-server/minecraft-patches/features/{0128-Optimize-LinearPalette.patch => 0127-Optimize-LinearPalette.patch} (100%) rename leaf-server/minecraft-patches/features/{0129-Slightly-optimized-VarInt-write.patch => 0128-Slightly-optimized-VarInt-write.patch} (100%) rename leaf-server/minecraft-patches/features/{0130-Rewrite-ClientboundLightUpdatePacketData.patch => 0129-Rewrite-ClientboundLightUpdatePacketData.patch} (100%) rename leaf-server/minecraft-patches/features/{0131-Async-chunk-send.patch => 0130-Async-chunk-send.patch} (100%) rename leaf-server/minecraft-patches/features/{0132-Spawner-Configurations.patch => 0131-Spawner-Configurations.patch} (100%) rename leaf-server/minecraft-patches/features/{0133-SparklyPaper-Parallel-world-ticking.patch => 0132-SparklyPaper-Parallel-world-ticking.patch} (100%) rename leaf-server/minecraft-patches/features/{0134-SparklyPaper-Track-each-world-MSPT.patch => 0133-SparklyPaper-Track-each-world-MSPT.patch} (100%) rename leaf-server/minecraft-patches/features/{0135-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch => 0134-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch} (100%) rename leaf-server/minecraft-patches/features/{0136-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch => 0135-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch} (100%) rename leaf-server/minecraft-patches/features/{0137-Remove-streams-on-InsideBrownianWalk.patch => 0136-Remove-streams-on-InsideBrownianWalk.patch} (100%) rename leaf-server/minecraft-patches/features/{0138-Use-BFS-on-getSlopeDistance.patch => 0137-Use-BFS-on-getSlopeDistance.patch} (100%) rename leaf-server/minecraft-patches/features/{0139-Paper-PR-Throttle-failed-spawn-attempts.patch => 0138-Paper-PR-Throttle-failed-spawn-attempts.patch} (100%) rename leaf-server/minecraft-patches/features/{0140-Improve-BlockEntity-ticking-isRemoved-check.patch => 0139-Improve-BlockEntity-ticking-isRemoved-check.patch} (100%) rename leaf-server/minecraft-patches/features/{0141-Raytrace-AntiXray-SDK-integration.patch => 0140-Raytrace-AntiXray-SDK-integration.patch} (100%) rename leaf-server/minecraft-patches/features/{0142-Optimize-addOrUpdateTransientModifier.patch => 0141-Optimize-addOrUpdateTransientModifier.patch} (100%) rename leaf-server/minecraft-patches/features/{0143-Optimize-ContextMap.create.patch => 0142-Optimize-ContextMap.create.patch} (100%) rename leaf-server/minecraft-patches/features/{0144-Micro-optimizations-for-random-tick.patch => 0143-Micro-optimizations-for-random-tick.patch} (100%) rename leaf-server/minecraft-patches/features/{0145-Remove-streams-on-updateConnectedPlayersWithinRange.patch => 0144-Remove-streams-on-updateConnectedPlayersWithinRange.patch} (100%) rename leaf-server/minecraft-patches/features/{0146-Remove-streams-on-PlayerDetector.patch => 0145-Remove-streams-on-PlayerDetector.patch} (100%) rename leaf-server/minecraft-patches/features/{0147-Use-direct-iteration-on-Sensing.tick.patch => 0146-Use-direct-iteration-on-Sensing.tick.patch} (100%) rename leaf-server/minecraft-patches/features/{0148-Optimise-non-flush-packet-sending.patch => 0147-Optimise-non-flush-packet-sending.patch} (100%) rename leaf-server/minecraft-patches/features/{0149-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch => 0148-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch} (100%) rename leaf-server/minecraft-patches/features/{0150-Null-handling-on-MultifaceSpreader.patch => 0149-Null-handling-on-MultifaceSpreader.patch} (100%) rename leaf-server/minecraft-patches/features/{0151-More-virtual-threads.patch => 0150-More-virtual-threads.patch} (100%) rename leaf-server/minecraft-patches/features/{0152-Async-target-finding.patch => 0151-Async-target-finding.patch} (100%) rename leaf-server/minecraft-patches/features/{0153-Optimize-ThreadedTicketLevelPropagator.patch => 0152-Optimize-ThreadedTicketLevelPropagator.patch} (100%) rename leaf-server/minecraft-patches/features/{0154-Optimise-MobEffectUtil-getDigSpeedAmplification.patch => 0153-Optimise-MobEffectUtil-getDigSpeedAmplification.patch} (100%) rename leaf-server/minecraft-patches/features/{0155-Optimise-chunkUnloads.patch => 0154-Optimise-chunkUnloads.patch} (100%) rename leaf-server/minecraft-patches/features/{0156-Optimize-BlockEntityType-isValid.patch => 0155-Optimize-BlockEntityType-isValid.patch} (100%) rename leaf-server/minecraft-patches/features/{0157-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch => 0156-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch} (100%) rename leaf-server/minecraft-patches/features/{0158-PaperPR-Fix-save-load-NaN-Entity-Motion.patch => 0157-PaperPR-Fix-save-load-NaN-Entity-Motion.patch} (100%) rename leaf-server/minecraft-patches/features/{0159-PaperPR-Fix-unnecessary-map-data-saves.patch => 0158-PaperPR-Fix-unnecessary-map-data-saves.patch} (100%) rename leaf-server/minecraft-patches/features/{0160-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch => 0159-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch} (100%) rename leaf-server/minecraft-patches/features/{0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch => 0160-Sakura-copy-EntityList-implementation-to-BasicEntity.patch} (100%) rename leaf-server/minecraft-patches/features/{0162-Protocol-Core.patch => 0161-Protocol-Core.patch} (100%) rename leaf-server/minecraft-patches/features/{0163-Reduce-PlayerChunk-Updates.patch => 0162-Reduce-PlayerChunk-Updates.patch} (100%) rename leaf-server/minecraft-patches/features/{0164-Async-switch-connection-state.patch => 0163-Async-switch-connection-state.patch} (100%) rename leaf-server/minecraft-patches/features/{0165-Optimise-BlockEntities-tickersInLevel.patch => 0164-Optimise-BlockEntities-tickersInLevel.patch} (100%) rename leaf-server/minecraft-patches/features/{0166-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch => 0165-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch} (100%) rename leaf-server/minecraft-patches/features/{0167-Flush-location-while-knockback.patch => 0166-Flush-location-while-knockback.patch} (100%) rename leaf-server/minecraft-patches/features/{0168-Only-tick-items-at-hand.patch => 0167-Only-tick-items-at-hand.patch} (100%) rename leaf-server/minecraft-patches/features/{0169-Smart-sort-items-in-NearestItemSensor.patch => 0168-Smart-sort-items-in-NearestItemSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0170-Optimise-player-movement-checks.patch => 0169-Optimise-player-movement-checks.patch} (100%) rename leaf-server/minecraft-patches/features/{0171-Remove-streams-in-MobSensor.patch => 0170-Remove-streams-in-MobSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0172-Remove-streams-in-TemptingSensor.patch => 0171-Remove-streams-in-TemptingSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0173-Use-HashedList-on-WeightedRandomList.patch => 0172-Use-HashedList-on-WeightedRandomList.patch} (100%) rename leaf-server/minecraft-patches/features/{0174-Add-configurable-death-item-drop-knockback-settings.patch => 0173-Add-configurable-death-item-drop-knockback-settings.patch} (100%) rename leaf-server/minecraft-patches/features/{0175-Optimize-getScaledTrackingDistance.patch => 0174-Optimize-getScaledTrackingDistance.patch} (100%) rename leaf-server/minecraft-patches/features/{0176-Optimize-SynchedEntityData-packDirty.patch => 0175-Optimize-SynchedEntityData-packDirty.patch} (100%) rename leaf-server/minecraft-patches/features/{0177-Optimize-isEyeInFluid.patch => 0176-Optimize-isEyeInFluid.patch} (100%) rename leaf-server/minecraft-patches/features/{0178-Cache-block-path-type.patch => 0177-Cache-block-path-type.patch} (100%) rename leaf-server/minecraft-patches/features/{0179-optimize-getEntityStatus.patch => 0178-optimize-getEntityStatus.patch} (100%) rename leaf-server/minecraft-patches/features/{0180-Rail-Optimization-optimized-PoweredRailBlock-logic.patch => 0179-Rail-Optimization-optimized-PoweredRailBlock-logic.patch} (100%) rename leaf-server/minecraft-patches/features/{0181-optimise-ChunkGenerator-getMobsAt.patch => 0180-optimise-ChunkGenerator-getMobsAt.patch} (100%) rename leaf-server/minecraft-patches/features/{0182-optimise-getBiome.patch => 0181-optimise-getBiome.patch} (100%) rename leaf-server/minecraft-patches/features/{0183-optimize-mob-spawning.patch => 0182-optimize-mob-spawning.patch} (100%) rename leaf-server/minecraft-patches/features/{0184-optimize-structure-map.patch => 0183-optimize-structure-map.patch} (100%) rename leaf-server/minecraft-patches/features/{0185-throttle-mob-spawning.patch => 0184-throttle-mob-spawning.patch} (100%) rename leaf-server/minecraft-patches/features/{0186-preload-mob-spawning-position.patch => 0185-preload-mob-spawning-position.patch} (100%) rename leaf-server/minecraft-patches/features/{0187-Add-BlockExplosionHitEvent.patch => 0186-Add-BlockExplosionHitEvent.patch} (100%) rename leaf-server/minecraft-patches/features/{0188-Old-Blast-Protection-explosion-knockback.patch => 0187-Old-Blast-Protection-explosion-knockback.patch} (100%) rename leaf-server/minecraft-patches/features/{0189-Paw-optimization.patch => 0188-Paw-optimization.patch} (100%) rename leaf-server/minecraft-patches/features/{0190-Use-UUID-for-cure-reputation.patch => 0189-Use-UUID-for-cure-reputation.patch} (100%) rename leaf-server/minecraft-patches/features/{0191-Cache-potential-behaviors-in-Brain.patch => 0190-Cache-potential-behaviors-in-Brain.patch} (94%) delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/BrainRunningBehaviorCacheUpdate.java diff --git a/leaf-server/minecraft-patches/features/0120-Alternative-Brain-Behaviour.patch b/leaf-server/minecraft-patches/features/0120-Alternative-Brain-Behaviour.patch deleted file mode 100644 index b6b27059..00000000 --- a/leaf-server/minecraft-patches/features/0120-Alternative-Brain-Behaviour.patch +++ /dev/null @@ -1,72 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Fri, 14 Feb 2025 14:58:59 +0100 -Subject: [PATCH] Alternative Brain Behaviour - -In the test, this can give ~54.87% improvement (~25712ms -> ~11604ms), -under 1024 villagers situation. - -diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java -index e27284f9897923f67985e3d60c3438bd00cc4a51..0ff7564e0e848bd38e82f9089bfd7249fa649dc5 100644 ---- a/net/minecraft/world/entity/ai/Brain.java -+++ b/net/minecraft/world/entity/ai/Brain.java -@@ -268,23 +268,52 @@ public class Brain { - return this.activeActivities; - } - -+ // Leaf start - Alternative Brain Behaviour -+ private ObjectArrayList> runningBehaviorsCache; -+ private long lastRunningBehaviorCheck = -1; -+ // Leaf end - Alternative Brain Behaviour -+ - @Deprecated - @VisibleForDebug - public List> getRunningBehaviors() { -- List> list = new ObjectArrayList<>(); -+ // Leaf start - Alternative Brain Behaviour -+ long currentTick = getCurrentTick(); -+ -+ // Use cached result if within update interval -+ if (runningBehaviorsCache != null && (currentTick - lastRunningBehaviorCheck) < org.dreeam.leaf.config.modules.opt.BrainRunningBehaviorCacheUpdate.interval) { -+ return runningBehaviorsCache; -+ } -+ -+ // Initialize or reuse cache list -+ if (runningBehaviorsCache == null) { -+ runningBehaviorsCache = new ObjectArrayList<>(32); -+ } else { -+ runningBehaviorsCache.clear(); -+ } -+ -+ for (Map>> activityMap : availableBehaviorsByPriority.values()) { -+ for (Set> behaviors : activityMap.values()) { -+ if (behaviors.isEmpty()) continue; - -- for (Map>> map : this.availableBehaviorsByPriority.values()) { -- for (Set> set : map.values()) { -- for (BehaviorControl behaviorControl : set) { -- if (behaviorControl.getStatus() == Behavior.Status.RUNNING) { -- list.add(behaviorControl); -+ for (BehaviorControl behavior : behaviors) { -+ if (behavior.getStatus() == Behavior.Status.RUNNING) { -+ runningBehaviorsCache.add(behavior); - } - } - } - } - -- return list; -+ lastRunningBehaviorCheck = currentTick; -+ -+ return runningBehaviorsCache; -+ } -+ -+ // Helper method to get current tick -+ private long getCurrentTick() { -+ // This should be implemented to return the current game tick -+ return System.nanoTime() / 50_000_000; // Approximate tick time of 50ms - } -+ // Leaf end - Alternative Brain Behaviour - - public void useDefaultActivity() { - this.setActiveActivity(this.defaultActivity); diff --git a/leaf-server/minecraft-patches/features/0121-Cache-eligible-players-for-despawn-checks.patch b/leaf-server/minecraft-patches/features/0120-Cache-eligible-players-for-despawn-checks.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0121-Cache-eligible-players-for-despawn-checks.patch rename to leaf-server/minecraft-patches/features/0120-Cache-eligible-players-for-despawn-checks.patch diff --git a/leaf-server/minecraft-patches/features/0122-Slightly-optimise-getNearestPlayer.patch b/leaf-server/minecraft-patches/features/0121-Slightly-optimise-getNearestPlayer.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0122-Slightly-optimise-getNearestPlayer.patch rename to leaf-server/minecraft-patches/features/0121-Slightly-optimise-getNearestPlayer.patch diff --git a/leaf-server/minecraft-patches/features/0123-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch b/leaf-server/minecraft-patches/features/0122-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0123-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch rename to leaf-server/minecraft-patches/features/0122-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch diff --git a/leaf-server/minecraft-patches/features/0124-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch b/leaf-server/minecraft-patches/features/0123-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0124-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch rename to leaf-server/minecraft-patches/features/0123-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch diff --git a/leaf-server/minecraft-patches/features/0125-Bulk-writes-to-writeLongArray-during-chunk-loading.patch b/leaf-server/minecraft-patches/features/0124-Bulk-writes-to-writeLongArray-during-chunk-loading.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0125-Bulk-writes-to-writeLongArray-during-chunk-loading.patch rename to leaf-server/minecraft-patches/features/0124-Bulk-writes-to-writeLongArray-during-chunk-loading.patch diff --git a/leaf-server/minecraft-patches/features/0126-Improve-sorting-in-SortedArraySet.patch b/leaf-server/minecraft-patches/features/0125-Improve-sorting-in-SortedArraySet.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0126-Improve-sorting-in-SortedArraySet.patch rename to leaf-server/minecraft-patches/features/0125-Improve-sorting-in-SortedArraySet.patch diff --git a/leaf-server/minecraft-patches/features/0127-Make-removeIf-slightly-faster.patch b/leaf-server/minecraft-patches/features/0126-Make-removeIf-slightly-faster.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0127-Make-removeIf-slightly-faster.patch rename to leaf-server/minecraft-patches/features/0126-Make-removeIf-slightly-faster.patch diff --git a/leaf-server/minecraft-patches/features/0128-Optimize-LinearPalette.patch b/leaf-server/minecraft-patches/features/0127-Optimize-LinearPalette.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0128-Optimize-LinearPalette.patch rename to leaf-server/minecraft-patches/features/0127-Optimize-LinearPalette.patch diff --git a/leaf-server/minecraft-patches/features/0129-Slightly-optimized-VarInt-write.patch b/leaf-server/minecraft-patches/features/0128-Slightly-optimized-VarInt-write.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0129-Slightly-optimized-VarInt-write.patch rename to leaf-server/minecraft-patches/features/0128-Slightly-optimized-VarInt-write.patch diff --git a/leaf-server/minecraft-patches/features/0130-Rewrite-ClientboundLightUpdatePacketData.patch b/leaf-server/minecraft-patches/features/0129-Rewrite-ClientboundLightUpdatePacketData.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0130-Rewrite-ClientboundLightUpdatePacketData.patch rename to leaf-server/minecraft-patches/features/0129-Rewrite-ClientboundLightUpdatePacketData.patch diff --git a/leaf-server/minecraft-patches/features/0131-Async-chunk-send.patch b/leaf-server/minecraft-patches/features/0130-Async-chunk-send.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0131-Async-chunk-send.patch rename to leaf-server/minecraft-patches/features/0130-Async-chunk-send.patch diff --git a/leaf-server/minecraft-patches/features/0132-Spawner-Configurations.patch b/leaf-server/minecraft-patches/features/0131-Spawner-Configurations.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0132-Spawner-Configurations.patch rename to leaf-server/minecraft-patches/features/0131-Spawner-Configurations.patch diff --git a/leaf-server/minecraft-patches/features/0133-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0133-SparklyPaper-Parallel-world-ticking.patch rename to leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Track-each-world-MSPT.patch b/leaf-server/minecraft-patches/features/0133-SparklyPaper-Track-each-world-MSPT.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0134-SparklyPaper-Track-each-world-MSPT.patch rename to leaf-server/minecraft-patches/features/0133-SparklyPaper-Track-each-world-MSPT.patch diff --git a/leaf-server/minecraft-patches/features/0135-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch b/leaf-server/minecraft-patches/features/0134-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0135-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch rename to leaf-server/minecraft-patches/features/0134-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch diff --git a/leaf-server/minecraft-patches/features/0136-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch b/leaf-server/minecraft-patches/features/0135-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0136-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch rename to leaf-server/minecraft-patches/features/0135-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch diff --git a/leaf-server/minecraft-patches/features/0137-Remove-streams-on-InsideBrownianWalk.patch b/leaf-server/minecraft-patches/features/0136-Remove-streams-on-InsideBrownianWalk.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0137-Remove-streams-on-InsideBrownianWalk.patch rename to leaf-server/minecraft-patches/features/0136-Remove-streams-on-InsideBrownianWalk.patch diff --git a/leaf-server/minecraft-patches/features/0138-Use-BFS-on-getSlopeDistance.patch b/leaf-server/minecraft-patches/features/0137-Use-BFS-on-getSlopeDistance.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0138-Use-BFS-on-getSlopeDistance.patch rename to leaf-server/minecraft-patches/features/0137-Use-BFS-on-getSlopeDistance.patch diff --git a/leaf-server/minecraft-patches/features/0139-Paper-PR-Throttle-failed-spawn-attempts.patch b/leaf-server/minecraft-patches/features/0138-Paper-PR-Throttle-failed-spawn-attempts.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0139-Paper-PR-Throttle-failed-spawn-attempts.patch rename to leaf-server/minecraft-patches/features/0138-Paper-PR-Throttle-failed-spawn-attempts.patch diff --git a/leaf-server/minecraft-patches/features/0140-Improve-BlockEntity-ticking-isRemoved-check.patch b/leaf-server/minecraft-patches/features/0139-Improve-BlockEntity-ticking-isRemoved-check.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0140-Improve-BlockEntity-ticking-isRemoved-check.patch rename to leaf-server/minecraft-patches/features/0139-Improve-BlockEntity-ticking-isRemoved-check.patch diff --git a/leaf-server/minecraft-patches/features/0141-Raytrace-AntiXray-SDK-integration.patch b/leaf-server/minecraft-patches/features/0140-Raytrace-AntiXray-SDK-integration.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0141-Raytrace-AntiXray-SDK-integration.patch rename to leaf-server/minecraft-patches/features/0140-Raytrace-AntiXray-SDK-integration.patch diff --git a/leaf-server/minecraft-patches/features/0142-Optimize-addOrUpdateTransientModifier.patch b/leaf-server/minecraft-patches/features/0141-Optimize-addOrUpdateTransientModifier.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0142-Optimize-addOrUpdateTransientModifier.patch rename to leaf-server/minecraft-patches/features/0141-Optimize-addOrUpdateTransientModifier.patch diff --git a/leaf-server/minecraft-patches/features/0143-Optimize-ContextMap.create.patch b/leaf-server/minecraft-patches/features/0142-Optimize-ContextMap.create.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0143-Optimize-ContextMap.create.patch rename to leaf-server/minecraft-patches/features/0142-Optimize-ContextMap.create.patch diff --git a/leaf-server/minecraft-patches/features/0144-Micro-optimizations-for-random-tick.patch b/leaf-server/minecraft-patches/features/0143-Micro-optimizations-for-random-tick.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0144-Micro-optimizations-for-random-tick.patch rename to leaf-server/minecraft-patches/features/0143-Micro-optimizations-for-random-tick.patch diff --git a/leaf-server/minecraft-patches/features/0145-Remove-streams-on-updateConnectedPlayersWithinRange.patch b/leaf-server/minecraft-patches/features/0144-Remove-streams-on-updateConnectedPlayersWithinRange.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0145-Remove-streams-on-updateConnectedPlayersWithinRange.patch rename to leaf-server/minecraft-patches/features/0144-Remove-streams-on-updateConnectedPlayersWithinRange.patch diff --git a/leaf-server/minecraft-patches/features/0146-Remove-streams-on-PlayerDetector.patch b/leaf-server/minecraft-patches/features/0145-Remove-streams-on-PlayerDetector.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0146-Remove-streams-on-PlayerDetector.patch rename to leaf-server/minecraft-patches/features/0145-Remove-streams-on-PlayerDetector.patch diff --git a/leaf-server/minecraft-patches/features/0147-Use-direct-iteration-on-Sensing.tick.patch b/leaf-server/minecraft-patches/features/0146-Use-direct-iteration-on-Sensing.tick.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0147-Use-direct-iteration-on-Sensing.tick.patch rename to leaf-server/minecraft-patches/features/0146-Use-direct-iteration-on-Sensing.tick.patch diff --git a/leaf-server/minecraft-patches/features/0148-Optimise-non-flush-packet-sending.patch b/leaf-server/minecraft-patches/features/0147-Optimise-non-flush-packet-sending.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0148-Optimise-non-flush-packet-sending.patch rename to leaf-server/minecraft-patches/features/0147-Optimise-non-flush-packet-sending.patch diff --git a/leaf-server/minecraft-patches/features/0149-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch b/leaf-server/minecraft-patches/features/0148-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0149-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch rename to leaf-server/minecraft-patches/features/0148-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch diff --git a/leaf-server/minecraft-patches/features/0150-Null-handling-on-MultifaceSpreader.patch b/leaf-server/minecraft-patches/features/0149-Null-handling-on-MultifaceSpreader.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0150-Null-handling-on-MultifaceSpreader.patch rename to leaf-server/minecraft-patches/features/0149-Null-handling-on-MultifaceSpreader.patch diff --git a/leaf-server/minecraft-patches/features/0151-More-virtual-threads.patch b/leaf-server/minecraft-patches/features/0150-More-virtual-threads.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0151-More-virtual-threads.patch rename to leaf-server/minecraft-patches/features/0150-More-virtual-threads.patch diff --git a/leaf-server/minecraft-patches/features/0152-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0151-Async-target-finding.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0152-Async-target-finding.patch rename to leaf-server/minecraft-patches/features/0151-Async-target-finding.patch diff --git a/leaf-server/minecraft-patches/features/0153-Optimize-ThreadedTicketLevelPropagator.patch b/leaf-server/minecraft-patches/features/0152-Optimize-ThreadedTicketLevelPropagator.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0153-Optimize-ThreadedTicketLevelPropagator.patch rename to leaf-server/minecraft-patches/features/0152-Optimize-ThreadedTicketLevelPropagator.patch diff --git a/leaf-server/minecraft-patches/features/0154-Optimise-MobEffectUtil-getDigSpeedAmplification.patch b/leaf-server/minecraft-patches/features/0153-Optimise-MobEffectUtil-getDigSpeedAmplification.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0154-Optimise-MobEffectUtil-getDigSpeedAmplification.patch rename to leaf-server/minecraft-patches/features/0153-Optimise-MobEffectUtil-getDigSpeedAmplification.patch diff --git a/leaf-server/minecraft-patches/features/0155-Optimise-chunkUnloads.patch b/leaf-server/minecraft-patches/features/0154-Optimise-chunkUnloads.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0155-Optimise-chunkUnloads.patch rename to leaf-server/minecraft-patches/features/0154-Optimise-chunkUnloads.patch diff --git a/leaf-server/minecraft-patches/features/0156-Optimize-BlockEntityType-isValid.patch b/leaf-server/minecraft-patches/features/0155-Optimize-BlockEntityType-isValid.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0156-Optimize-BlockEntityType-isValid.patch rename to leaf-server/minecraft-patches/features/0155-Optimize-BlockEntityType-isValid.patch diff --git a/leaf-server/minecraft-patches/features/0157-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch b/leaf-server/minecraft-patches/features/0156-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0157-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch rename to leaf-server/minecraft-patches/features/0156-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch diff --git a/leaf-server/minecraft-patches/features/0158-PaperPR-Fix-save-load-NaN-Entity-Motion.patch b/leaf-server/minecraft-patches/features/0157-PaperPR-Fix-save-load-NaN-Entity-Motion.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0158-PaperPR-Fix-save-load-NaN-Entity-Motion.patch rename to leaf-server/minecraft-patches/features/0157-PaperPR-Fix-save-load-NaN-Entity-Motion.patch diff --git a/leaf-server/minecraft-patches/features/0159-PaperPR-Fix-unnecessary-map-data-saves.patch b/leaf-server/minecraft-patches/features/0158-PaperPR-Fix-unnecessary-map-data-saves.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0159-PaperPR-Fix-unnecessary-map-data-saves.patch rename to leaf-server/minecraft-patches/features/0158-PaperPR-Fix-unnecessary-map-data-saves.patch diff --git a/leaf-server/minecraft-patches/features/0160-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch b/leaf-server/minecraft-patches/features/0159-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0160-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch rename to leaf-server/minecraft-patches/features/0159-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch diff --git a/leaf-server/minecraft-patches/features/0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0160-Sakura-copy-EntityList-implementation-to-BasicEntity.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch rename to leaf-server/minecraft-patches/features/0160-Sakura-copy-EntityList-implementation-to-BasicEntity.patch diff --git a/leaf-server/minecraft-patches/features/0162-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0161-Protocol-Core.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0162-Protocol-Core.patch rename to leaf-server/minecraft-patches/features/0161-Protocol-Core.patch diff --git a/leaf-server/minecraft-patches/features/0163-Reduce-PlayerChunk-Updates.patch b/leaf-server/minecraft-patches/features/0162-Reduce-PlayerChunk-Updates.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0163-Reduce-PlayerChunk-Updates.patch rename to leaf-server/minecraft-patches/features/0162-Reduce-PlayerChunk-Updates.patch diff --git a/leaf-server/minecraft-patches/features/0164-Async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0164-Async-switch-connection-state.patch rename to leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch diff --git a/leaf-server/minecraft-patches/features/0165-Optimise-BlockEntities-tickersInLevel.patch b/leaf-server/minecraft-patches/features/0164-Optimise-BlockEntities-tickersInLevel.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0165-Optimise-BlockEntities-tickersInLevel.patch rename to leaf-server/minecraft-patches/features/0164-Optimise-BlockEntities-tickersInLevel.patch diff --git a/leaf-server/minecraft-patches/features/0166-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch b/leaf-server/minecraft-patches/features/0165-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0166-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch rename to leaf-server/minecraft-patches/features/0165-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch diff --git a/leaf-server/minecraft-patches/features/0167-Flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0166-Flush-location-while-knockback.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0167-Flush-location-while-knockback.patch rename to leaf-server/minecraft-patches/features/0166-Flush-location-while-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0168-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0167-Only-tick-items-at-hand.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0168-Only-tick-items-at-hand.patch rename to leaf-server/minecraft-patches/features/0167-Only-tick-items-at-hand.patch diff --git a/leaf-server/minecraft-patches/features/0169-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0168-Smart-sort-items-in-NearestItemSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0169-Smart-sort-items-in-NearestItemSensor.patch rename to leaf-server/minecraft-patches/features/0168-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0170-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0169-Optimise-player-movement-checks.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0170-Optimise-player-movement-checks.patch rename to leaf-server/minecraft-patches/features/0169-Optimise-player-movement-checks.patch diff --git a/leaf-server/minecraft-patches/features/0171-Remove-streams-in-MobSensor.patch b/leaf-server/minecraft-patches/features/0170-Remove-streams-in-MobSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0171-Remove-streams-in-MobSensor.patch rename to leaf-server/minecraft-patches/features/0170-Remove-streams-in-MobSensor.patch diff --git a/leaf-server/minecraft-patches/features/0172-Remove-streams-in-TemptingSensor.patch b/leaf-server/minecraft-patches/features/0171-Remove-streams-in-TemptingSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0172-Remove-streams-in-TemptingSensor.patch rename to leaf-server/minecraft-patches/features/0171-Remove-streams-in-TemptingSensor.patch diff --git a/leaf-server/minecraft-patches/features/0173-Use-HashedList-on-WeightedRandomList.patch b/leaf-server/minecraft-patches/features/0172-Use-HashedList-on-WeightedRandomList.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0173-Use-HashedList-on-WeightedRandomList.patch rename to leaf-server/minecraft-patches/features/0172-Use-HashedList-on-WeightedRandomList.patch diff --git a/leaf-server/minecraft-patches/features/0174-Add-configurable-death-item-drop-knockback-settings.patch b/leaf-server/minecraft-patches/features/0173-Add-configurable-death-item-drop-knockback-settings.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0174-Add-configurable-death-item-drop-knockback-settings.patch rename to leaf-server/minecraft-patches/features/0173-Add-configurable-death-item-drop-knockback-settings.patch diff --git a/leaf-server/minecraft-patches/features/0175-Optimize-getScaledTrackingDistance.patch b/leaf-server/minecraft-patches/features/0174-Optimize-getScaledTrackingDistance.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0175-Optimize-getScaledTrackingDistance.patch rename to leaf-server/minecraft-patches/features/0174-Optimize-getScaledTrackingDistance.patch diff --git a/leaf-server/minecraft-patches/features/0176-Optimize-SynchedEntityData-packDirty.patch b/leaf-server/minecraft-patches/features/0175-Optimize-SynchedEntityData-packDirty.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0176-Optimize-SynchedEntityData-packDirty.patch rename to leaf-server/minecraft-patches/features/0175-Optimize-SynchedEntityData-packDirty.patch diff --git a/leaf-server/minecraft-patches/features/0177-Optimize-isEyeInFluid.patch b/leaf-server/minecraft-patches/features/0176-Optimize-isEyeInFluid.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0177-Optimize-isEyeInFluid.patch rename to leaf-server/minecraft-patches/features/0176-Optimize-isEyeInFluid.patch diff --git a/leaf-server/minecraft-patches/features/0178-Cache-block-path-type.patch b/leaf-server/minecraft-patches/features/0177-Cache-block-path-type.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0178-Cache-block-path-type.patch rename to leaf-server/minecraft-patches/features/0177-Cache-block-path-type.patch diff --git a/leaf-server/minecraft-patches/features/0179-optimize-getEntityStatus.patch b/leaf-server/minecraft-patches/features/0178-optimize-getEntityStatus.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0179-optimize-getEntityStatus.patch rename to leaf-server/minecraft-patches/features/0178-optimize-getEntityStatus.patch diff --git a/leaf-server/minecraft-patches/features/0180-Rail-Optimization-optimized-PoweredRailBlock-logic.patch b/leaf-server/minecraft-patches/features/0179-Rail-Optimization-optimized-PoweredRailBlock-logic.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0180-Rail-Optimization-optimized-PoweredRailBlock-logic.patch rename to leaf-server/minecraft-patches/features/0179-Rail-Optimization-optimized-PoweredRailBlock-logic.patch diff --git a/leaf-server/minecraft-patches/features/0181-optimise-ChunkGenerator-getMobsAt.patch b/leaf-server/minecraft-patches/features/0180-optimise-ChunkGenerator-getMobsAt.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0181-optimise-ChunkGenerator-getMobsAt.patch rename to leaf-server/minecraft-patches/features/0180-optimise-ChunkGenerator-getMobsAt.patch diff --git a/leaf-server/minecraft-patches/features/0182-optimise-getBiome.patch b/leaf-server/minecraft-patches/features/0181-optimise-getBiome.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0182-optimise-getBiome.patch rename to leaf-server/minecraft-patches/features/0181-optimise-getBiome.patch diff --git a/leaf-server/minecraft-patches/features/0183-optimize-mob-spawning.patch b/leaf-server/minecraft-patches/features/0182-optimize-mob-spawning.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0183-optimize-mob-spawning.patch rename to leaf-server/minecraft-patches/features/0182-optimize-mob-spawning.patch diff --git a/leaf-server/minecraft-patches/features/0184-optimize-structure-map.patch b/leaf-server/minecraft-patches/features/0183-optimize-structure-map.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0184-optimize-structure-map.patch rename to leaf-server/minecraft-patches/features/0183-optimize-structure-map.patch diff --git a/leaf-server/minecraft-patches/features/0185-throttle-mob-spawning.patch b/leaf-server/minecraft-patches/features/0184-throttle-mob-spawning.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0185-throttle-mob-spawning.patch rename to leaf-server/minecraft-patches/features/0184-throttle-mob-spawning.patch diff --git a/leaf-server/minecraft-patches/features/0186-preload-mob-spawning-position.patch b/leaf-server/minecraft-patches/features/0185-preload-mob-spawning-position.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0186-preload-mob-spawning-position.patch rename to leaf-server/minecraft-patches/features/0185-preload-mob-spawning-position.patch diff --git a/leaf-server/minecraft-patches/features/0187-Add-BlockExplosionHitEvent.patch b/leaf-server/minecraft-patches/features/0186-Add-BlockExplosionHitEvent.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0187-Add-BlockExplosionHitEvent.patch rename to leaf-server/minecraft-patches/features/0186-Add-BlockExplosionHitEvent.patch diff --git a/leaf-server/minecraft-patches/features/0188-Old-Blast-Protection-explosion-knockback.patch b/leaf-server/minecraft-patches/features/0187-Old-Blast-Protection-explosion-knockback.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0188-Old-Blast-Protection-explosion-knockback.patch rename to leaf-server/minecraft-patches/features/0187-Old-Blast-Protection-explosion-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0189-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0188-Paw-optimization.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0189-Paw-optimization.patch rename to leaf-server/minecraft-patches/features/0188-Paw-optimization.patch diff --git a/leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch b/leaf-server/minecraft-patches/features/0189-Use-UUID-for-cure-reputation.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0190-Use-UUID-for-cure-reputation.patch rename to leaf-server/minecraft-patches/features/0189-Use-UUID-for-cure-reputation.patch diff --git a/leaf-server/minecraft-patches/features/0191-Cache-potential-behaviors-in-Brain.patch b/leaf-server/minecraft-patches/features/0190-Cache-potential-behaviors-in-Brain.patch similarity index 94% rename from leaf-server/minecraft-patches/features/0191-Cache-potential-behaviors-in-Brain.patch rename to leaf-server/minecraft-patches/features/0190-Cache-potential-behaviors-in-Brain.patch index 7b7cc23d..5b0006d5 100644 --- a/leaf-server/minecraft-patches/features/0191-Cache-potential-behaviors-in-Brain.patch +++ b/leaf-server/minecraft-patches/features/0190-Cache-potential-behaviors-in-Brain.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Cache potential behaviors in Brain diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java -index 457962b2113726c3cae7fed9c1926e8537834c3c..68eadc20d0bb80cac81dd3b6127c46ea484d39b0 100644 +index 97dad57ba873c0f6404a490e358739dbaf11bc55..34b66ee67927bc0796d6c5f069393618abca9d74 100644 --- a/net/minecraft/world/entity/ai/Brain.java +++ b/net/minecraft/world/entity/ai/Brain.java @@ -60,6 +60,7 @@ public class Brain { @@ -24,7 +24,7 @@ index 457962b2113726c3cae7fed9c1926e8537834c3c..68eadc20d0bb80cac81dd3b6127c46ea } public DataResult serializeStart(DynamicOps ops) { -@@ -343,6 +345,7 @@ public class Brain { +@@ -314,6 +316,7 @@ public class Brain { this.activeActivities.clear(); this.activeActivities.addAll(this.coreActivities); this.activeActivities.add(activity); @@ -32,7 +32,7 @@ index 457962b2113726c3cae7fed9c1926e8537834c3c..68eadc20d0bb80cac81dd3b6127c46ea } } -@@ -423,11 +426,13 @@ public class Brain { +@@ -394,11 +397,13 @@ public class Brain { .computeIfAbsent(activity, activity1 -> new it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet<>()) // Leaf - Replace brain activity maps with optimized collection .add((BehaviorControl)pair.getSecond()); } @@ -46,7 +46,7 @@ index 457962b2113726c3cae7fed9c1926e8537834c3c..68eadc20d0bb80cac81dd3b6127c46ea } public boolean isActive(Activity activity) { -@@ -481,30 +486,40 @@ public class Brain { +@@ -452,30 +457,40 @@ public class Brain { } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/BrainRunningBehaviorCacheUpdate.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/BrainRunningBehaviorCacheUpdate.java deleted file mode 100644 index f1ae5bf9..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/BrainRunningBehaviorCacheUpdate.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.dreeam.leaf.config.modules.opt; - -import org.dreeam.leaf.config.ConfigModules; -import org.dreeam.leaf.config.EnumConfigCategory; - -public class BrainRunningBehaviorCacheUpdate extends ConfigModules { - - public String getBasePath() { - return EnumConfigCategory.PERF.getBaseKeyName(); - } - - public static int interval = 5; - - @Override - public void onLoaded() { - interval = config.getInt(getBasePath() + ".entity-running-behavior-cache-update-interval", interval, - config.pickStringRegionBased( - "How often entity update current brain running behavior list.", - "生物更新现有 Brain Behavior 列表缓存的间隔.")); - } -} From acf2c14f80e9047cfe07fea23611b2ede2223d39 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 8 Jun 2025 00:15:07 +0200 Subject: [PATCH 41/57] Use ActivationList on runningBehaviours --- ...e-ActivationList-on-runningBehaviors.patch | 100 ++++++++ .../dreeam/leaf/util/list/ActivationList.java | 215 ++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0191-Use-ActivationList-on-runningBehaviors.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/util/list/ActivationList.java diff --git a/leaf-server/minecraft-patches/features/0191-Use-ActivationList-on-runningBehaviors.patch b/leaf-server/minecraft-patches/features/0191-Use-ActivationList-on-runningBehaviors.patch new file mode 100644 index 00000000..667f2a4c --- /dev/null +++ b/leaf-server/minecraft-patches/features/0191-Use-ActivationList-on-runningBehaviors.patch @@ -0,0 +1,100 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sat, 7 Jun 2025 23:22:56 +0200 +Subject: [PATCH] Use ActivationList on runningBehaviors + + +diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java +index 34b66ee67927bc0796d6c5f069393618abca9d74..f7dd07feea8884c686e78becb1f9cbd0d2769915 100644 +--- a/net/minecraft/world/entity/ai/Brain.java ++++ b/net/minecraft/world/entity/ai/Brain.java +@@ -61,6 +61,7 @@ public class Brain { + private long lastScheduleUpdate = -9999L; + + private ObjectArrayList> cachedPotentialBehaviors; ++ private org.dreeam.leaf.util.list.ActivationList> runningBehaviors; + public static Brain.Provider provider( + Collection> memoryTypes, Collection>> sensorTypes + ) { +@@ -273,19 +274,7 @@ public class Brain { + @Deprecated + @VisibleForDebug + public List> getRunningBehaviors() { +- List> list = new ObjectArrayList<>(); +- +- for (Map>> map : this.availableBehaviorsByPriority.values()) { +- for (Set> set : map.values()) { +- for (BehaviorControl behaviorControl : set) { +- if (behaviorControl.getStatus() == Behavior.Status.RUNNING) { +- list.add(behaviorControl); +- } +- } +- } +- } +- +- return list; ++ return this.getRunningBehaviorsList(); + } + + public void useDefaultActivity() { +@@ -453,12 +442,14 @@ public class Brain { + long gameTime = owner.level().getGameTime(); + + for (BehaviorControl behaviorControl : this.getRunningBehaviors()) { ++ this.getRunningBehaviorsList().setVisibility(behaviorControl, false); + behaviorControl.doStop(level, owner, gameTime); + } + } + + private void invalidateBehaviorCache() { + this.cachedPotentialBehaviors = null; ++ this.runningBehaviors = null; + } + + private void rebuildBehaviorCache() { +@@ -476,6 +467,25 @@ public class Brain { + } + } + ++ private void initializeRunningBehaviors() { ++ this.runningBehaviors = new org.dreeam.leaf.util.list.ActivationList<>(false); ++ ++ for (Map>> map : this.availableBehaviorsByPriority.values()) { ++ for (Set> set : map.values()) { ++ for (BehaviorControl task : set) { ++ this.runningBehaviors.addOrUpdate(task, task.getStatus() == Behavior.Status.RUNNING); ++ } ++ } ++ } ++ } ++ ++ private org.dreeam.leaf.util.list.ActivationList> getRunningBehaviorsList() { ++ if (this.runningBehaviors == null) { ++ this.initializeRunningBehaviors(); ++ } ++ return this.runningBehaviors; ++ } ++ + private ObjectArrayList> getPotentialBehaviors() { + if (this.cachedPotentialBehaviors == null) { + this.rebuildBehaviorCache(); +@@ -489,6 +499,9 @@ public class Brain { + for (BehaviorControl task : this.getPotentialBehaviors()) { + if (task.getStatus() == Behavior.Status.STOPPED) { + task.tryStart(level, entity, startTime); ++ if (task.getStatus() == Behavior.Status.RUNNING) { ++ this.getRunningBehaviorsList().setVisibility(task, true); ++ } + } + } + } +@@ -498,6 +511,9 @@ public class Brain { + + for (BehaviorControl behaviorControl : this.getRunningBehaviors()) { + behaviorControl.tickOrStop(level, entity, gameTime); ++ if (behaviorControl.getStatus() != Behavior.Status.RUNNING) { ++ this.getRunningBehaviorsList().setVisibility(behaviorControl, false); ++ } + } + } + diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/list/ActivationList.java b/leaf-server/src/main/java/org/dreeam/leaf/util/list/ActivationList.java new file mode 100644 index 00000000..21fad340 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/list/ActivationList.java @@ -0,0 +1,215 @@ +package org.dreeam.leaf.util.list; + +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.util.AbstractList; +import java.util.BitSet; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.function.Consumer; + +/** + * A specialized list that allows for efficient hiding and showing of elements + * without physically removing them from the backing store. + *

+ * Iteration only processes "visible" elements, and visibility can be toggled in O(1) time. + * This is useful for managing lists of tasks or objects where a large set exists, + * but only a small subset is active at any given time. + * + * @param The type of elements in this list. + */ +public class ActivationList extends AbstractList { + + private final ObjectArrayList elements; + private final BitSet visibilityMask; + private final Object2IntOpenHashMap elementToIndexMap; + private final boolean isVisibleByDefault; + private int removedSlotCount; + + /** + * Constructs a new, empty MaskedList. + * + * @param isVisibleByDefault The default visibility for elements added to this list. + */ + public ActivationList(boolean isVisibleByDefault) { + this.elements = new ObjectArrayList<>(); + this.visibilityMask = new BitSet(); + this.elementToIndexMap = new Object2IntOpenHashMap<>(); + this.elementToIndexMap.defaultReturnValue(-1); + this.isVisibleByDefault = isVisibleByDefault; + } + + /** + * Constructs a new, empty MaskedList with default visibility set to true. + */ + public ActivationList() { + this(true); + } + + /** + * Adds an element to the list or, if it already exists, updates its visibility. + * + * @param element The element to add or update. + * @param visible The desired visibility of the element. + */ + public void addOrUpdate(E element, boolean visible) { + int index = this.elementToIndexMap.getInt(element); + if (index == -1) { + index = this.elements.size(); + this.elements.add(element); + this.elementToIndexMap.put(element, index); + } + this.visibilityMask.set(index, visible); + } + + /** + * Sets the visibility of an existing element. + * + * @param element The element whose visibility to change. + * @param visible True to make the element visible, false to hide it. + */ + public void setVisibility(E element, boolean visible) { + int index = this.elementToIndexMap.getInt(element); + if (index != -1) { + this.visibilityMask.set(index, visible); + } + } + + @Override + public boolean add(E element) { + if (this.elementToIndexMap.containsKey(element)) { + throw new IllegalArgumentException("MaskedList cannot contain duplicate elements: " + element); + } + this.addOrUpdate(element, this.isVisibleByDefault); + return true; + } + + @Override + public boolean remove(Object o) { + int index = this.elementToIndexMap.removeInt(o); + if (index == -1) { + return false; + } + + this.visibilityMask.clear(index); + this.elements.set(index, null); + this.removedSlotCount++; + + if (this.removedSlotCount > 0 && this.removedSlotCount * 2 >= this.elements.size()) { + compact(); + } + return true; + } + + /** + * Rebuilds the internal list (wow) + */ + private void compact() { + int writeIndex = 0; + for (int readIndex = 0; readIndex < this.elements.size(); readIndex++) { + E element = this.elements.get(readIndex); + + if (element != null) { + if (readIndex != writeIndex) { + this.elements.set(writeIndex, element); + this.elementToIndexMap.put(element, writeIndex); + this.visibilityMask.set(writeIndex, this.visibilityMask.get(readIndex)); + } + writeIndex++; + } + } + + int oldSize = this.elements.size(); + if (writeIndex < oldSize) { + this.elements.removeElements(writeIndex, oldSize); + this.visibilityMask.clear(writeIndex, oldSize); + } + + this.removedSlotCount = 0; + } + + @Override + public int size() { + return this.visibilityMask.cardinality(); + } + + @Override + public E get(int index) { + if (index < 0 || index >= this.size()) { + throw new IndexOutOfBoundsException("Index: " + index + ", Visible Size: " + this.size()); + } + + int setBitIndex = -1; + for (int i = 0; i <= index; i++) { + setBitIndex = this.visibilityMask.nextSetBit(setBitIndex + 1); + } + return this.elements.get(setBitIndex); + } + + @Override + public Iterator iterator() { + return new MaskedIterator(); + } + + private class MaskedIterator implements Iterator { + private int nextVisibleIndex; + + MaskedIterator() { + this.nextVisibleIndex = ActivationList.this.visibilityMask.nextSetBit(0); + } + + @Override + public boolean hasNext() { + return this.nextVisibleIndex != -1; + } + + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + E element = ActivationList.this.elements.get(this.nextVisibleIndex); + this.nextVisibleIndex = ActivationList.this.visibilityMask.nextSetBit(this.nextVisibleIndex + 1); + return element; + } + } + + @Override + public Spliterator spliterator() { + return new MaskedSpliterator(); + } + + private class MaskedSpliterator implements Spliterator { + private int currentIndex; + + MaskedSpliterator() { + this.currentIndex = ActivationList.this.visibilityMask.nextSetBit(0); + } + + @Override + public boolean tryAdvance(Consumer action) { + if (this.currentIndex != -1) { + action.accept(ActivationList.this.elements.get(this.currentIndex)); + this.currentIndex = ActivationList.this.visibilityMask.nextSetBit(this.currentIndex + 1); + return true; + } + return false; + } + + @Override + public Spliterator trySplit() { + return null; // This spliterator does not support splitting. + } + + @Override + public long estimateSize() { + return ActivationList.this.size(); + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SIZED; + } + } +} From 107ae7954f024b1fb250373a1929766561c5a8ed Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 8 Jun 2025 12:18:01 +0900 Subject: [PATCH 42/57] async saving player stats and advancements (#334) * async saving player stats and advancements * remove thread check * fix interrupt * longer wait IO tasks time * safe replace * delay join while saving player * mark as experimental --------- Co-authored-by: Taiyou06 --- .../server/0100-Smooth-teleport-config.patch | 4 +- .../0086-Nitori-Async-playerdata-saving.patch | 337 +++++++++++++----- ...p-dirty-stats-copy-when-requesting-p.patch | 6 +- ...-SparklyPaper-Parallel-world-ticking.patch | 18 +- .../0163-Async-switch-connection-state.patch | 4 +- .../0033-Async-playerdata-saving.patch | 6 +- .../leaf/async/AsyncPlayerDataSaving.java | 37 -- .../dreeam/leaf/async/ShutdownExecutors.java | 1 + .../async/storage/AsyncPlayerDataSaving.java | 307 ++++++++++++++++ .../modules/async/AsyncPlayerDataSave.java | 11 + .../leavesmc/leaves/bot/BotStatsCounter.java | 2 +- 11 files changed, 583 insertions(+), 150 deletions(-) delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/storage/AsyncPlayerDataSaving.java diff --git a/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch b/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch index 30429f8d..b5c7d20c 100644 --- a/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch +++ b/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch @@ -32,10 +32,10 @@ index 4f01b53bf801f99253efd27df6216912705d18af..82a1715fea41e6a41c4ff441ea89f424 level.addDuringTeleport(this); this.triggerDimensionChangeTriggers(serverLevel); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 75fb49f1596f475278d12c8c7aea9ad4952b6056..b17c8a2f5294ac28cc05fb05c84a041b2c6c8721 100644 +index 52a0fa425a30caa2e592c0fdda44800da169c2a0..3f5c5b6234eb400838973c37e5a48bb121d1ff16 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java -@@ -955,11 +955,11 @@ public abstract class PlayerList { +@@ -997,11 +997,11 @@ public abstract class PlayerList { byte b = (byte)(keepInventory ? 1 : 0); ServerLevel serverLevel = serverPlayer.serverLevel(); LevelData levelData = serverLevel.getLevelData(); diff --git a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch index 337ffac9..5fdb3805 100644 --- a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch +++ b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch @@ -6,15 +6,213 @@ Subject: [PATCH] Nitori: Async playerdata saving Original license: GPL v3 Original project: https://github.com/Gensokyo-Reimagined/Nitori +diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java +index 00a82873d226f113278632a53c0faca420dd67d4..2c4423eb2d465c2782a8dab851619ce539f69ae8 100644 +--- a/net/minecraft/network/Connection.java ++++ b/net/minecraft/network/Connection.java +@@ -586,7 +586,7 @@ public class Connection extends SimpleChannelInboundHandler> { + // Paper end - Optimize network + + private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world +- private static int joinAttemptsThisTick; // Paper - Buffer joins to world ++ public static int joinAttemptsThisTick; // Paper - Buffer joins to world // Leaf - Async player IO + private static int currTick; // Paper - Buffer joins to world + private static int tickSecond; // Purpur - Max joins per second + public void tick() { +diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java +index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..d5196b181a0a633cb04ce18b0471cda2dcaa8816 100644 +--- a/net/minecraft/server/PlayerAdvancements.java ++++ b/net/minecraft/server/PlayerAdvancements.java +@@ -111,6 +111,7 @@ public class PlayerAdvancements { + } + + private void load(ServerAdvancementManager manager) { ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.blockAdvancements(player.getUUID(), player.getScoreboardName()); // Leaf - Async player IO + if (Files.isRegularFile(this.playerSavePath)) { + try (JsonReader jsonReader = new JsonReader(Files.newBufferedReader(this.playerSavePath, StandardCharsets.UTF_8))) { + jsonReader.setLenient(false); +@@ -133,12 +134,18 @@ public class PlayerAdvancements { + JsonElement jsonElement = this.codec.encodeStart(JsonOps.INSTANCE, this.asData()).getOrThrow(); + + try { +- FileUtil.createDirectoriesSafe(this.playerSavePath.getParent()); +- +- try (Writer bufferedWriter = Files.newBufferedWriter(this.playerSavePath, StandardCharsets.UTF_8)) { +- GSON.toJson(jsonElement, GSON.newJsonWriter(bufferedWriter)); +- } +- } catch (JsonIOException | IOException var7) { ++ // Leaf start - Async player IO ++ String content = GSON.toJson(jsonElement); ++ final Path path = this.playerSavePath; ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.submitAdvancements( ++ this.player.getUUID(), ++ this.player.getScoreboardName(), ++ () -> { ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplace(path, content); ++ return null; ++ }); ++ } catch (JsonIOException /*| IOException*/ var7) { ++ // Leaf end - Async player IO + LOGGER.error("Couldn't save player advancements to {}", this.playerSavePath, var7); + } + } +diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +index 114b25f933c6a1b011523581a5a02a5a2c1e827e..3da6dad3dd0f4c5750609b382f47a6cd14f18e7a 100644 +--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +@@ -79,6 +79,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, + .expireAfterWrite(org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResultTimeout, java.util.concurrent.TimeUnit.MINUTES) + .build(); + // Leaf end - Cache player profileResult ++ @Nullable public java.util.UUID[] duplicateDisconnect = null; // Leaf - Async player IO + + public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) { + this.server = server; +diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java +index 75fb49f1596f475278d12c8c7aea9ad4952b6056..52a0fa425a30caa2e592c0fdda44800da169c2a0 100644 +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -782,6 +782,31 @@ public abstract class PlayerList { + // UserBanListEntry userBanListEntry = this.bans.get(gameProfile); + // Moved from processLogin + UUID uuid = gameProfile.getId(); ++ ++ // Leaf start - Async player IO ++ if (org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.enabled) { ++ if (org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.isSaving(uuid)) { ++ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1; ++ return null; ++ } ++ if (loginlistener.duplicateDisconnect != null ++ && loginlistener.duplicateDisconnect.length != 0) { ++ // check last one ++ var last = loginlistener.duplicateDisconnect[loginlistener.duplicateDisconnect.length - 1]; ++ if (org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.isSaving(last)) { ++ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1; ++ return null; ++ } ++ for (UUID uuid1 : loginlistener.duplicateDisconnect) { ++ if (org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.isSaving(uuid1)) { ++ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1; ++ return null; ++ } ++ } ++ loginlistener.duplicateDisconnect = null; ++ } ++ } ++ // Leaf end - Async player IO + List list = Lists.newArrayList(); + + ServerPlayer entityplayer; +@@ -793,6 +818,23 @@ public abstract class PlayerList { + } + } + ++ // Leaf start - Async player IO ++ if (org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.enabled && !list.isEmpty()) { ++ loginlistener.duplicateDisconnect = new UUID[list.size()]; ++ java.util.Iterator iterator = list.iterator(); ++ ++ int index = 0; ++ while (iterator.hasNext()) { ++ entityplayer = iterator.next(); ++ // this.save(entityplayer); // CraftBukkit - Force the player's inventory to be saved ++ entityplayer.connection.disconnect(Component.translatable("multiplayer.disconnect.duplicate_login"), org.bukkit.event.player.PlayerKickEvent.Cause.DUPLICATE_LOGIN); // Paper - kick event cause ++ loginlistener.duplicateDisconnect[index] = entityplayer.getUUID(); ++ index++; ++ } ++ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1; ++ return null; ++ } ++ // Leaf end - Async player IO + java.util.Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { +@@ -1582,7 +1624,7 @@ public abstract class PlayerList { + */ + // Leaf end - Remove useless creating stats json bases on player name logic + +- serverStatsCounter = new ServerStatsCounter(this.server, file1); ++ serverStatsCounter = new ServerStatsCounter(this.server, file1, displayName, uuid); + // this.stats.put(uuid, serverStatsCounter); // CraftBukkit + } + +diff --git a/net/minecraft/stats/ServerStatsCounter.java b/net/minecraft/stats/ServerStatsCounter.java +index b26dbe807e5cb0a42f6c06b933397902310e5616..35ad7f249cfb6f5c779136d96f3698ea4de1eb7c 100644 +--- a/net/minecraft/stats/ServerStatsCounter.java ++++ b/net/minecraft/stats/ServerStatsCounter.java +@@ -39,12 +39,23 @@ public class ServerStatsCounter extends StatsCounter { + private final File file; + private final Set> dirty = Sets.newHashSet(); + ++ // Leaf start - Async player IO ++ private final String name; ++ private final java.util.UUID uuid; ++ @Deprecated(forRemoval = true) + public ServerStatsCounter(MinecraftServer server, File file) { ++ throw new UnsupportedOperationException(); ++ } ++ public ServerStatsCounter(MinecraftServer server, File file, String name, java.util.UUID uuid) { ++ this.name = name; ++ this.uuid = uuid; ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.blockStats(uuid, name); ++ // Leaf end - Async player IO + this.server = server; + this.file = file; + if (file.isFile()) { + try { +- this.parseLocal(server.getFixerUpper(), FileUtils.readFileToString(file)); ++ this.parseLocal(server.getFixerUpper(), FileUtils.readFileToString(file, java.nio.charset.StandardCharsets.UTF_8)); // Leaf - UTF-8 + } catch (IOException var4) { + LOGGER.error("Couldn't read statistics file {}", file, var4); + } catch (JsonParseException var5) { +@@ -66,11 +77,37 @@ public class ServerStatsCounter extends StatsCounter { + + public void save() { + if (org.spigotmc.SpigotConfig.disableStatSaving) return; // Spigot ++ // Leaf start - Async player IO ++ Map, JsonObject> map = Maps.newHashMap(); ++ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry> entry : this.stats.object2IntEntrySet()) { ++ Stat stat = entry.getKey(); ++ map.computeIfAbsent(stat.getType(), type -> new JsonObject()).addProperty(getKey(stat).toString(), entry.getIntValue()); ++ } ++ final File file = this.file; ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.submitStats( ++ uuid, ++ name, ++ () -> { ++ JsonObject jsonObject = new JsonObject(); ++ ++ for (Entry, JsonObject> entry1 : map.entrySet()) { ++ jsonObject.add(BuiltInRegistries.STAT_TYPE.getKey(entry1.getKey()).toString(), entry1.getValue()); ++ } ++ ++ JsonObject jsonObject1 = new JsonObject(); ++ jsonObject1.add("stats", jsonObject); ++ jsonObject1.addProperty("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplace(file.toPath(), jsonObject1.toString()); ++ return null; ++ }); ++ /* + try { + FileUtils.writeStringToFile(this.file, this.toJson()); + } catch (IOException var2) { + LOGGER.error("Couldn't save stats", (Throwable)var2); + } ++ */ ++ // Leaf end - Async player IO + } + + @Override diff --git a/net/minecraft/world/level/storage/LevelStorageSource.java b/net/minecraft/world/level/storage/LevelStorageSource.java -index de43e54698125ce9f319d4889dd49f7029fe95e0..742bd4b60321adc9e63c3de910ea95f4990b618d 100644 +index de43e54698125ce9f319d4889dd49f7029fe95e0..360e54b87db68fad60cdec63af466765baae0a07 100644 --- a/net/minecraft/world/level/storage/LevelStorageSource.java +++ b/net/minecraft/world/level/storage/LevelStorageSource.java -@@ -520,15 +520,26 @@ public class LevelStorageSource { +@@ -520,15 +520,24 @@ public class LevelStorageSource { private void saveLevelData(CompoundTag tag) { Path path = this.levelDirectory.path(); -+ // Leaf start - Async playerdata saving ++ // Leaf start - Async player IO + // Save level.dat asynchronously + var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536); try { @@ -28,38 +226,36 @@ index de43e54698125ce9f319d4889dd49f7029fe95e0..742bd4b60321adc9e63c3de910ea95f4 - LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6); + LevelStorageSource.LOGGER.error("Failed to encode level {}", path, var6); } -+ org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> { ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.saveLevelData(path, () -> { + try { -+ Path path1 = Files.createTempFile(path, "level", ".dat"); -+ org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false); -+ Path path2 = this.levelDirectory.oldDataFile(); -+ Path path3 = this.levelDirectory.dataFile(); -+ Util.safeReplaceFile(path3, path1, path2); ++ Path old = this.levelDirectory.oldDataFile(); ++ Path current = this.levelDirectory.dataFile(); ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplaceBackup(current, old, nbtBytes.array, 0, nbtBytes.length); + } catch (Exception var6) { -+ LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6); ++ LevelStorageSource.LOGGER.error("Failed to save level.dat {}", path, var6); + } + }); -+ // Leaf end - Async playerdata saving ++ // Leaf end - Async player IO } public Optional getIconFile() { +@@ -645,6 +654,7 @@ public class LevelStorageSource { + + @Override + public void close() throws IOException { ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.saveLevelData(this.levelDirectory.path(), null); // Leaf - Async player IO + this.lock.close(); + } + diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java -index c44110b123ba5912af18faf0065e9ded780da9b7..fd8b4832c8b4a52bd8f9b3ea59111af85127b573 100644 +index c44110b123ba5912af18faf0065e9ded780da9b7..2eae5ccb37b942b94964c28391b96989ae85b072 100644 --- a/net/minecraft/world/level/storage/PlayerDataStorage.java +++ b/net/minecraft/world/level/storage/PlayerDataStorage.java -@@ -25,6 +25,7 @@ public class PlayerDataStorage { - private final File playerDir; - protected final DataFixer fixerUpper; - private static final DateTimeFormatter FORMATTER = FileNameDateFormatter.create(); -+ private final java.util.Map> savingLocks = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // Leaf - Async playerdata saving - - public PlayerDataStorage(LevelStorageSource.LevelStorageAccess levelStorageAccess, DataFixer fixerUpper) { - this.fixerUpper = fixerUpper; -@@ -34,19 +35,82 @@ public class PlayerDataStorage { +@@ -34,19 +34,37 @@ public class PlayerDataStorage { public void save(Player player) { if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return; // Spigot -+ // Leaf start - Async playerdata saving ++ // Leaf start - Async player IO + CompoundTag compoundTag; try { - CompoundTag compoundTag = player.saveWithoutId(new CompoundTag()); @@ -77,105 +273,60 @@ index c44110b123ba5912af18faf0065e9ded780da9b7..fd8b4832c8b4a52bd8f9b3ea59111af8 + return; } + save(player.getScoreboardName(), player.getUUID(), player.getStringUUID(), compoundTag); -+ // Leaf end - Async playerdata saving ++ // Leaf end - Async player IO } -+ // Leaf start - Async playerdata saving -+ public void save(String playerName, java.util.UUID uniqueId, String stringId, CompoundTag compoundTag) { -+ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536); -+ try { -+ NbtIo.writeCompressed(compoundTag, nbtBytes); -+ } catch (Exception exception) { -+ LOGGER.warn("Failed to encode player data for {}", stringId, exception); -+ } -+ lockFor(uniqueId, playerName); -+ synchronized (PlayerDataStorage.this) { -+ org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> { -+ try { -+ Path path = this.playerDir.toPath(); -+ Path path1 = Files.createTempFile(path, stringId + "-", ".dat"); -+ org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false); -+ Path path2 = path.resolve(stringId + ".dat"); -+ Path path3 = path.resolve(stringId + ".dat_old"); -+ Util.safeReplaceFile(path2, path1, path3); -+ } catch (Exception var7) { -+ LOGGER.warn("Failed to save player data for {}", playerName, var7); -+ } finally { -+ synchronized (PlayerDataStorage.this) { -+ savingLocks.remove(uniqueId); -+ } -+ } -+ }).ifPresent(future -> savingLocks.put(uniqueId, future)); -+ } -+ } ++ // Leaf start - Async player IO ++ public void save(String playerName, java.util.UUID uuid, String stringId, CompoundTag compoundTag) { ++ final File playerDir = this.playerDir; ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.submitEntity( ++ uuid, ++ playerName, ++ () -> { ++ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536); ++ NbtIo.writeCompressed(compoundTag, nbtBytes); ++ Path path = playerDir.toPath(); + -+ private void lockFor(java.util.UUID uniqueId, String playerName) { -+ java.util.concurrent.Future fut; -+ synchronized (this) { -+ fut = savingLocks.get(uniqueId); -+ } -+ if (fut == null) { -+ return; -+ } -+ while (true) { -+ try { -+ fut.get(10_000L, java.util.concurrent.TimeUnit.MILLISECONDS); -+ break; -+ } catch (InterruptedException ignored) { -+ } catch (java.util.concurrent.ExecutionException -+ | java.util.concurrent.TimeoutException exception) { -+ LOGGER.warn("Failed to save player data for {}", playerName, exception); -+ -+ String threadDump = ""; -+ var threadMXBean = java.lang.management.ManagementFactory.getThreadMXBean(); -+ for (var threadInfo : threadMXBean.dumpAllThreads(true, true)) { -+ if (threadInfo.getThreadName().equals("Leaf IO Thread")) { -+ threadDump = threadInfo.toString(); -+ break; -+ } -+ } -+ LOGGER.warn(threadDump); -+ fut.cancel(true); -+ break; -+ } finally { -+ savingLocks.remove(uniqueId); -+ } -+ } ++ Path current = path.resolve(stringId + ".dat"); ++ Path old = path.resolve(stringId + ".dat_old"); ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplaceBackup(current, old, nbtBytes.array, 0, nbtBytes.length); ++ return null; ++ }); + } -+ // Leaf end - Async playerdata saving ++ // Leaf end - Async player IO + private void backup(String name, String stringUuid, String suffix) { // CraftBukkit Path path = this.playerDir.toPath(); Path path1 = path.resolve(stringUuid + suffix); // CraftBukkit -@@ -60,7 +124,13 @@ public class PlayerDataStorage { +@@ -60,7 +78,13 @@ public class PlayerDataStorage { } } - private Optional load(String name, String stringUuid, String suffix) { // CraftBukkit -+ // Leaf start - Async playerdata saving ++ // Leaf start - Async player IO + private Optional load(String name, String stringUuid, String suffix) { + return load(name, stringUuid, suffix, java.util.UUID.fromString(stringUuid)); + } + private Optional load(String name, String stringUuid, String suffix, java.util.UUID playerUuid) { // CraftBukkit -+ lockFor(playerUuid, name); -+ // Leaf end - Async playerdata saving ++ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.blockEntity(playerUuid, name); ++ // Leaf end - Async player IO File file = new File(this.playerDir, stringUuid + suffix); // CraftBukkit // Spigot start boolean usingWrongFile = false; -@@ -91,7 +161,7 @@ public class PlayerDataStorage { +@@ -91,7 +115,7 @@ public class PlayerDataStorage { public Optional load(Player player) { // CraftBukkit start - return this.load(player.getName().getString(), player.getStringUUID()).map((tag) -> { -+ return this.load(player.getName().getString(), player.getStringUUID(), player.getUUID()).map((tag) -> { // Leaf - Async playerdata saving ++ return this.load(player.getName().getString(), player.getStringUUID(), player.getUUID()).map((tag) -> { // Leaf - Async player IO if (player instanceof ServerPlayer serverPlayer) { CraftPlayer craftPlayer = serverPlayer.getBukkitEntity(); // Only update first played if it is older than the one we have -@@ -106,20 +176,25 @@ public class PlayerDataStorage { +@@ -106,20 +130,25 @@ public class PlayerDataStorage { }); } -+ // Leaf start - Async playerdata saving ++ // Leaf start - Async player IO public Optional load(String name, String uuid) { + return this.load(name, uuid, java.util.UUID.fromString(uuid)); + } @@ -195,7 +346,7 @@ index c44110b123ba5912af18faf0065e9ded780da9b7..fd8b4832c8b4a52bd8f9b3ea59111af8 return compoundTag; }); } -+ // Leaf end - Async playerdata saving ++ // Leaf end - Async player IO // CraftBukkit start public File getPlayerDir() { diff --git a/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch b/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch index ea9f51d9..9b461562 100644 --- a/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch +++ b/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch @@ -6,10 +6,10 @@ Subject: [PATCH] SparklyPaper: Skip dirty stats copy when requesting player diff --git a/net/minecraft/stats/ServerStatsCounter.java b/net/minecraft/stats/ServerStatsCounter.java -index b26dbe807e5cb0a42f6c06b933397902310e5616..ce89060bd01b253af7577fd0e6c03fc95f046b91 100644 +index 35ad7f249cfb6f5c779136d96f3698ea4de1eb7c..523dc12a8866a199eac1b2f418bf206f068ba80c 100644 --- a/net/minecraft/stats/ServerStatsCounter.java +++ b/net/minecraft/stats/ServerStatsCounter.java -@@ -81,11 +81,15 @@ public class ServerStatsCounter extends StatsCounter { +@@ -118,11 +118,15 @@ public class ServerStatsCounter extends StatsCounter { this.dirty.add(stat); } @@ -25,7 +25,7 @@ index b26dbe807e5cb0a42f6c06b933397902310e5616..ce89060bd01b253af7577fd0e6c03fc9 public void parseLocal(DataFixer fixerUpper, String json) { try { -@@ -194,10 +198,12 @@ public class ServerStatsCounter extends StatsCounter { +@@ -231,10 +235,12 @@ public class ServerStatsCounter extends StatsCounter { public void sendStats(ServerPlayer player) { Object2IntMap> map = new Object2IntOpenHashMap<>(); diff --git a/leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch index 9f02149e..71743a1a 100644 --- a/leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch @@ -458,7 +458,7 @@ index 63ff20f467c7508486a8f274442269b90faea108..15de8904a43c0ee1e6d55d511ebd84df } // CraftBukkit end diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java -index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..abe7ffd48766c48fab091947f34db436b3c883d0 100644 +index d5196b181a0a633cb04ce18b0471cda2dcaa8816..46433f0e6b37d31ec5468b3f4a5b2524d3cb29ed 100644 --- a/net/minecraft/server/PlayerAdvancements.java +++ b/net/minecraft/server/PlayerAdvancements.java @@ -53,8 +53,11 @@ public class PlayerAdvancements { @@ -483,7 +483,7 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..abe7ffd48766c48fab091947f34db436 this.isFirstPacket = true; this.lastSelectedTab = null; this.tree = manager.tree(); -@@ -151,7 +155,7 @@ public class PlayerAdvancements { +@@ -158,7 +162,7 @@ public class PlayerAdvancements { if (org.galemc.gale.configuration.GaleGlobalConfiguration.get().logToConsole.ignoredAdvancements) LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", path, this.playerSavePath); // Gale - Purpur - do not log ignored advancements } else { this.startProgress(advancementHolder, progress); @@ -492,7 +492,7 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..abe7ffd48766c48fab091947f34db436 this.markForVisibilityUpdate(advancementHolder); } }); -@@ -183,10 +187,12 @@ public class PlayerAdvancements { +@@ -190,10 +194,12 @@ public class PlayerAdvancements { return false; } // Paper end - Add PlayerAdvancementCriterionGrantEvent @@ -509,7 +509,7 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..abe7ffd48766c48fab091947f34db436 // Paper start - Add Adventure message to PlayerAdvancementDoneEvent final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> { return java.util.Optional.ofNullable( -@@ -220,12 +226,14 @@ public class PlayerAdvancements { +@@ -227,12 +233,14 @@ public class PlayerAdvancements { AdvancementProgress orStartProgress = this.getOrStartProgress(advancement); boolean isDone = orStartProgress.isDone(); if (orStartProgress.revokeProgress(criterionKey)) { @@ -527,7 +527,7 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..abe7ffd48766c48fab091947f34db436 this.markForVisibilityUpdate(advancement); } -@@ -271,7 +279,10 @@ public class PlayerAdvancements { +@@ -278,7 +286,10 @@ public class PlayerAdvancements { } public void flushDirty(ServerPlayer serverPlayer) { @@ -539,7 +539,7 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..abe7ffd48766c48fab091947f34db436 Map map = new HashMap<>(); Set set = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically. Set set1 = new HashSet<>(); -@@ -279,16 +290,24 @@ public class PlayerAdvancements { +@@ -286,16 +297,24 @@ public class PlayerAdvancements { for (AdvancementNode advancementNode : this.rootsToUpdate) { this.updateTreeVisibility(advancementNode, set, set1); } @@ -568,7 +568,7 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..abe7ffd48766c48fab091947f34db436 if (!map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) { serverPlayer.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, set, set1, map)); } -@@ -331,10 +350,13 @@ public class PlayerAdvancements { +@@ -338,10 +357,13 @@ public class PlayerAdvancements { AdvancementHolder advancementHolder = node.holder(); if (visible) { if (this.visible.add(advancementHolder)) { @@ -900,7 +900,7 @@ index 75fb49f1596f475278d12c8c7aea9ad4952b6056..de601491b7ecb83f1bb64a95989d6ed4 player.isRealPlayer = true; // Paper player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed GameProfile gameProfile = player.getGameProfile(); -@@ -891,6 +893,15 @@ public abstract class PlayerList { +@@ -933,6 +935,15 @@ public abstract class PlayerList { return this.respawn(player, keepInventory, reason, eventReason, null); } public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, org.bukkit.Location location) { @@ -916,7 +916,7 @@ index 75fb49f1596f475278d12c8c7aea9ad4952b6056..de601491b7ecb83f1bb64a95989d6ed4 player.stopRiding(); // CraftBukkit this.players.remove(player); this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot -@@ -902,6 +913,7 @@ public abstract class PlayerList { +@@ -944,6 +955,7 @@ public abstract class PlayerList { ServerPlayer serverPlayer = player; Level fromWorld = player.level(); player.wonGame = false; diff --git a/leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch index 16dfdbb5..1cd528ac 100644 --- a/leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch @@ -110,10 +110,10 @@ index 2e9eb04c7c4342393c05339906c267bca9ff29b1..53b9daa909c2b89046d5af515e17afe0 try { PlayerList playerList = this.server.getPlayerList(); diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 114b25f933c6a1b011523581a5a02a5a2c1e827e..5907f1c75002be5e2ef1f9875974e665f964db7a 100644 +index 3da6dad3dd0f4c5750609b382f47a6cd14f18e7a..c1e4ea2f28aba688b5b61e5bea2c295e9f219aba 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -494,11 +494,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -495,11 +495,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); } diff --git a/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch b/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch index 8eae877f..5c6eb13d 100644 --- a/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch +++ b/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Async playerdata saving diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java -index f2d87c12dd19210ce7e2147fada5c10191008632..14da4c731391f69fef104b6b3b7f2f977fe5ee95 100644 +index f2d87c12dd19210ce7e2147fada5c10191008632..ad66046d31c24ba2a7d2b115f6c70adb95b9735b 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -207,7 +207,7 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa @@ -13,7 +13,7 @@ index f2d87c12dd19210ce7e2147fada5c10191008632..14da4c731391f69fef104b6b3b7f2f97 private CompoundTag getData() { - return this.storage.load(this.profile.getName(), this.profile.getId().toString()).orElse(null); -+ return this.storage.load(this.profile.getName(), this.profile.getId().toString(), this.profile.getId()).orElse(null); // Leaf - Async playerdata saving ++ return this.storage.load(this.profile.getName(), this.profile.getId().toString(), this.profile.getId()).orElse(null); // Leaf - Async player IO } private CompoundTag getBukkitData() { @@ -31,7 +31,7 @@ index f2d87c12dd19210ce7e2147fada5c10191008632..14da4c731391f69fef104b6b3b7f2f97 - } catch (java.io.IOException e) { - e.printStackTrace(); - } -+ server.console.playerDataStorage.save(this.getName(), this.getUniqueId(), this.getUniqueId().toString(), compoundTag); // Leaf - Async playerdata saving ++ server.console.playerDataStorage.save(this.getName(), this.getUniqueId(), this.getUniqueId().toString(), compoundTag); // Leaf - Async player IO } // Purpur end - OfflinePlayer API } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java deleted file mode 100644 index 3df32778..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.dreeam.leaf.async; - -import net.minecraft.Util; -import org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave; - -import java.util.Optional; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -public class AsyncPlayerDataSaving { - - public static final ExecutorService IO_POOL = new ThreadPoolExecutor( - 1, 1, 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - new com.google.common.util.concurrent.ThreadFactoryBuilder() - .setPriority(Thread.NORM_PRIORITY - 2) - .setNameFormat("Leaf IO Thread") - .setUncaughtExceptionHandler(Util::onThreadException) - .build(), - new ThreadPoolExecutor.DiscardPolicy() - ); - - private AsyncPlayerDataSaving() { - } - - public static Optional> submit(Runnable runnable) { - if (!AsyncPlayerDataSave.enabled) { - runnable.run(); - return Optional.empty(); - } else { - return Optional.of(IO_POOL.submit(runnable)); - } - } -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java index ee6d490d..df2d6ef6 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java @@ -1,6 +1,7 @@ package org.dreeam.leaf.async; import net.minecraft.server.MinecraftServer; +import org.dreeam.leaf.async.storage.AsyncPlayerDataSaving; import org.dreeam.leaf.async.tracker.MultithreadedTracker; public class ShutdownExecutors { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/storage/AsyncPlayerDataSaving.java b/leaf-server/src/main/java/org/dreeam/leaf/async/storage/AsyncPlayerDataSaving.java new file mode 100644 index 00000000..7e5060d7 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/storage/AsyncPlayerDataSaving.java @@ -0,0 +1,307 @@ +package org.dreeam.leaf.async.storage; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.Util; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.SignStyle; +import java.time.temporal.ChronoField; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.*; + +public class AsyncPlayerDataSaving { + public static final AsyncPlayerDataSaving INSTANCE = new AsyncPlayerDataSaving(); + private static final Logger LOGGER = LogManager.getLogger("Leaf Async Player IO"); + public static ExecutorService IO_POOL = null; + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendValue(ChronoField.MONTH_OF_YEAR, 2) + .appendValue(ChronoField.DAY_OF_MONTH, 2) + .appendValue(ChronoField.HOUR_OF_DAY, 2) + .appendValue(ChronoField.MINUTE_OF_HOUR, 2) + .appendValue(ChronoField.SECOND_OF_MINUTE, 2) + .appendValue(ChronoField.NANO_OF_SECOND, 9) + .toFormatter(); + + private record SaveTask(Ty ty, Callable callable, String name, UUID uuid) implements Runnable { + @Override + public void run() { + try { + callable.call(); + } catch (Exception e) { + LOGGER.error("Failed to save player {} data for {}", ty, name, e); + } finally { + switch (ty) { + case ENTITY -> INSTANCE.entityFut.remove(uuid); + case STATS -> INSTANCE.statsFut.remove(uuid); + case ADVANCEMENTS -> INSTANCE.advancementsFut.remove(uuid); + } + } + } + } + + private enum Ty { + ENTITY, + STATS, + ADVANCEMENTS, + } + + // use same lock + private final Object2ObjectMap> entityFut = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(), this); + private final Object2ObjectMap> statsFut = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(), this); + private final Object2ObjectMap> advancementsFut = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(), this); + + private final Object2ObjectMap> levelDatFut = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(), this); + + private AsyncPlayerDataSaving() { + } + + public static void init() { + if (AsyncPlayerDataSaving.IO_POOL != null) { + throw new IllegalStateException("Already initialized"); + } + AsyncPlayerDataSaving.IO_POOL = new ThreadPoolExecutor( + 1, 1, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + new ThreadFactoryBuilder() + .setPriority(Thread.NORM_PRIORITY - 2) + .setNameFormat("Leaf Async Player IO Thread") + .setUncaughtExceptionHandler(Util::onThreadException) + .build(), + new ThreadPoolExecutor.DiscardPolicy() + ); + } + + + public void saveLevelData(Path path, @Nullable Runnable runnable) { + if (!AsyncPlayerDataSave.enabled) { + if (runnable != null) { + runnable.run(); + } + return; + } + var fut = levelDatFut.get(path); + if (fut != null) { + try { + while (true) { + try { + fut.get(); + break; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } catch (ExecutionException e) { + LOGGER.error("Failed to save level.dat for {}", path, e); + } finally { + levelDatFut.remove(path); + } + } + if (runnable != null) { + levelDatFut.put(path, IO_POOL.submit(() -> { + try { + runnable.run(); + } catch (Exception e) { + LOGGER.error(e); + } finally { + levelDatFut.remove(path); + } + })); + } + } + + public boolean isSaving(UUID uuid) { + var entity = entityFut.get(uuid); + var advancements = advancementsFut.get(uuid); + var stats = statsFut.get(uuid); + return entity != null || advancements != null || stats != null; + } + + public void submitStats(UUID uuid, String playerName, Callable callable) { + submit(Ty.STATS, uuid, playerName, callable); + } + + public void submitEntity(UUID uuid, String playerName, Callable callable) { + submit(Ty.ENTITY, uuid, playerName, callable); + } + + public void submitAdvancements(UUID uuid, String playerName, Callable callable) { + submit(Ty.ADVANCEMENTS, uuid, playerName, callable); + } + + private void submit(Ty type, UUID uuid, String playerName, Callable callable) { + if (!AsyncPlayerDataSave.enabled) { + try { + callable.call(); + } catch (Exception e) { + LOGGER.error("Failed to save player {} data for {}", type, playerName, e); + } + return; + } + block(type, uuid, playerName); + var fut = IO_POOL.submit(new SaveTask(type, callable, playerName, uuid)); + switch (type) { + case ENTITY -> entityFut.put(uuid, fut); + case ADVANCEMENTS -> advancementsFut.put(uuid, fut); + case STATS -> statsFut.put(uuid, fut); + } + } + + public void blockStats(UUID uuid, String playerName) { + block(Ty.STATS, uuid, playerName); + } + + public void blockEntity(UUID uuid, String playerName) { + block(Ty.ENTITY, uuid, playerName); + } + + public void blockAdvancements(UUID uuid, String playerName) { + block(Ty.ADVANCEMENTS, uuid, playerName); + } + + private void block(Ty type, UUID uuid, String playerName) { + if (!AsyncPlayerDataSave.enabled) { + return; + } + + Future fut = switch (type) { + case ENTITY -> entityFut.get(uuid); + case ADVANCEMENTS -> advancementsFut.get(uuid); + case STATS -> statsFut.get(uuid); + }; + if (fut == null) { + return; + } + try { + while (true) { + try { + fut.get(); + break; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } catch (ExecutionException exception) { + LOGGER.warn("Failed to save player {} data for {}", type, playerName, exception); + fut.cancel(true); + } finally { + switch (type) { + case ENTITY -> entityFut.remove(uuid); + case ADVANCEMENTS -> advancementsFut.remove(uuid); + case STATS -> statsFut.remove(uuid); + } + } + } + + private static final StandardCopyOption[] ATOMIC_MOVE = new StandardCopyOption[]{StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING}; + private static final StandardCopyOption[] NO_ATOMIC_MOVE = new StandardCopyOption[]{StandardCopyOption.REPLACE_EXISTING}; + + public static void safeReplace(Path current, String content) { + byte[] bytes = content.getBytes(StandardCharsets.UTF_8); + safeReplace(current, bytes, 0, bytes.length); + } + + @SuppressWarnings("unused") + public static void safeReplaceBackup(Path current, Path old, String content) { + byte[] bytes = content.getBytes(StandardCharsets.UTF_8); + safeReplaceBackup(current, old, bytes, 0, bytes.length); + } + + public static void safeReplace(Path current, byte[] bytes, int offset, int length) { + File latest = writeTempFile(current, bytes, offset, length); + Objects.requireNonNull(latest); + for (int i = 1; i <= 10; i++) { + try { + try { + Files.move(latest.toPath(), current, ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException e) { + Files.move(latest.toPath(), current, NO_ATOMIC_MOVE); + } + break; + } catch (IOException e) { + LOGGER.error("Failed move {} to {} retries ({} / 10)", latest, current, i, e); + } + } + } + + public static void safeReplaceBackup(Path current, Path backup, byte[] bytes, int offset, int length) { + File latest = writeTempFile(current, bytes, offset, length); + Objects.requireNonNull(latest); + for (int i = 1; i <= 10; i++) { + try { + try { + Files.move(current, backup, ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException e) { + Files.move(current, backup, NO_ATOMIC_MOVE); + } + break; + } catch (IOException e) { + LOGGER.error("Failed move {} to {} retries ({} / 10)", current, backup, i, e); + } + } + for (int i = 1; i <= 10; i++) { + try { + try { + Files.move(latest.toPath(), current, ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException e) { + Files.move(latest.toPath(), current, NO_ATOMIC_MOVE); + } + break; + } catch (IOException e) { + LOGGER.error("Failed move {} to {} retries ({} / 10)", latest, current, i, e); + } + } + } + + private static File writeTempFile(Path current, byte[] bytes, int offset, int length) { + Path dir = current.getParent(); + for (int i = 1; i <= 10; i++) { + File temp = null; + try { + if (!dir.toFile().isDirectory()) { + Files.createDirectories(dir); + } + temp = tempFileDateTime(current).toFile(); + if (temp.exists()) { + throw new FileAlreadyExistsException(temp.getPath()); + } + // sync content and metadata to device + try (RandomAccessFile stream = new RandomAccessFile(temp, "rws")) { + stream.write(bytes, offset, length); + } + return temp; + } catch (IOException e) { + LOGGER.error("Failed write {} retries ({} / 10)", temp == null ? current : temp, i, e); + } + } + return null; + } + + private static Path tempFileDateTime(Path path) { + String now = LocalDateTime.now().format(FORMATTER); + String last = path.getFileName().toString(); + int dot = last.lastIndexOf('.'); + + String base = (dot == -1) ? last : last.substring(0, dot); + String ext = (dot == -1) ? "" : last.substring(dot); + + String newExt = switch (ext) { + case ".json", ".dat" -> ext; + default -> ".temp"; + }; + return path.resolveSibling(base + "-" + now + newExt); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java index 6d555ce0..e8ac71f7 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java @@ -1,7 +1,9 @@ package org.dreeam.leaf.config.modules.async; +import org.dreeam.leaf.async.storage.AsyncPlayerDataSaving; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; +import org.dreeam.leaf.config.annotations.Experimental; public class AsyncPlayerDataSave extends ConfigModules { @@ -9,7 +11,9 @@ public class AsyncPlayerDataSave extends ConfigModules { return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-playerdata-save"; } + @Experimental public static boolean enabled = false; + private static boolean asyncPlayerDataSaveInitialized; @Override public void onLoaded() { @@ -18,6 +22,13 @@ public class AsyncPlayerDataSave extends ConfigModules { """ 异步保存玩家数据."""); + if (asyncPlayerDataSaveInitialized) { + config.getConfigSection(getBasePath()); + return; + } + asyncPlayerDataSaveInitialized = true; + enabled = config.getBoolean(getBasePath() + ".enabled", enabled); + if (enabled) AsyncPlayerDataSaving.init(); } } diff --git a/leaf-server/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java b/leaf-server/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java index 10494446..6dfb82e4 100644 --- a/leaf-server/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java +++ b/leaf-server/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java @@ -14,7 +14,7 @@ public class BotStatsCounter extends ServerStatsCounter { private static final File UNKOWN_FILE = new File("BOT_STATS_REMOVE_THIS"); public BotStatsCounter(MinecraftServer server) { - super(server, UNKOWN_FILE); + super(server, UNKOWN_FILE, "", net.minecraft.Util.NIL_UUID); // Leaf } @Override From 2e822d3714957be62e7263f6a8413b0fb9b603b7 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 8 Jun 2025 13:21:54 +0900 Subject: [PATCH 43/57] optimize random tick (#357) * random tick * cleanup * [ci skip] cleanup --- .../features/0192-optimize-random-tick.patch | 97 ++++++++ .../modules/opt/OptimizeRandomTick.java | 20 ++ .../dreeam/leaf/world/RandomTickSystem.java | 222 ++++++++++++++++++ 3 files changed, 339 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java diff --git a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch new file mode 100644 index 00000000..54a9bf0d --- /dev/null +++ b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch @@ -0,0 +1,97 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Fri, 6 Jun 2025 20:46:10 +0900 +Subject: [PATCH] optimize random tick + + +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index 2f927b422c2c4f2f65d822befe3cbfd9e3bb3708..d0fcfeaf093b718c8acd6e057176d569651299f2 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -693,6 +693,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + this.level.tickChunk(levelChunk, _int); + } + } ++ this.level.randomTickSystem.tick(this.level); // Leaf - random tick + + if (flagAndHasNaturalSpawn) { // Gale - MultiPaper - skip unnecessary mob spawning computations + this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index eb849c57992658005e0f514c6f7923f8ca43bebf..2efcdb9bc91b9106b4aef9e24cc20596be4a5661 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -1128,6 +1128,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + + private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking + ++ public org.dreeam.leaf.world.RandomTickSystem randomTickSystem = new org.dreeam.leaf.world.RandomTickSystem(); // Leaf + public void tickChunk(LevelChunk chunk, int randomTickSpeed) { + final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting + ChunkPos pos = chunk.getPos(); +@@ -1177,7 +1178,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } // Paper - Option to disable ice and snow + + if (randomTickSpeed > 0) { +- this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking ++ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.randomTickChunk(chunk, randomTickSpeed); // Leaf - random tick ++ else this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking // Leaf - random tick + } + } + +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index 624a177695580510c0a49d4503dee72da7fd7114..8be4f0978451179fca8b9603743e875817040a08 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -151,6 +151,52 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + } + // Gale end - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively + ++ // Leaf start - random tick ++ private long leaf$randomTickChance; ++ private long leaf$countTickingBlocks; ++ private long leaf$countTickingSections; ++ ++ public final long leaf$randomTickChance() { ++ return leaf$randomTickChance; ++ } ++ public final void leaf$setRandomTickChance(long chance) { ++ leaf$randomTickChance = chance; ++ } ++ public final long leaf$countTickingBlocks() { ++ return leaf$countTickingBlocks; ++ } ++ public final long leaf$countTickingSections() { ++ return leaf$countTickingSections; ++ } ++ public final void leaf$recompute() { ++ long total1 = 0L; ++ long total2 = 0L; ++ for (LevelChunkSection section : sections) { ++ total1 += section.moonrise$getTickingBlockList().size(); ++ if (section.isRandomlyTickingBlocks()) { ++ total2++; ++ } ++ } ++ leaf$countTickingBlocks = total1; ++ leaf$countTickingSections = total2; ++ } ++ public final java.util.OptionalLong leaf$tickingPos(int idx) { ++ for (int i = 0; i < sections.length; i++) { ++ LevelChunkSection section = sections[i]; ++ var l = section.moonrise$getTickingBlockList(); ++ int size = l.size(); ++ if (size > idx) { ++ short loc = l.getRaw(size - idx); ++ return java.util.OptionalLong.of(BlockPos.asLong( ++ (loc & 15) | (chunkPos.x << 4), ++ (loc >>> 8) | (((getMinSectionY() + i) << 4)), ++ ((loc >>> 4) & 15) | (chunkPos.z << 4))); ++ } ++ idx -= size; ++ } ++ return java.util.OptionalLong.empty(); ++ } ++ // Leaf end - random tick + public LevelChunk(Level level, ChunkPos pos) { + this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null); + } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java new file mode 100644 index 00000000..5a265d8e --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java @@ -0,0 +1,20 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; +import org.dreeam.leaf.config.annotations.Experimental; + +public class OptimizeRandomTick extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".optimise-random-tick"; + } + + @Experimental + public static boolean enabled = true; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath(), enabled); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java new file mode 100644 index 00000000..5907d2c6 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java @@ -0,0 +1,222 @@ +package org.dreeam.leaf.world; + +import it.unimi.dsi.fastutil.longs.LongArrayList; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.levelgen.BitRandomSource; +import net.minecraft.world.level.levelgen.PositionalRandomFactory; +import net.minecraft.world.level.levelgen.RandomSupport; +import net.minecraft.world.level.material.FluidState; +import org.jetbrains.annotations.NotNull; + +import java.util.OptionalLong; + +public final class RandomTickSystem { + private final LongArrayList tickPos = new LongArrayList(); + private final WyRand rand = new WyRand(RandomSupport.generateUniqueSeed()); + private static final long SCALE = 0x100000L; + private static final long MASK = 0xfffffL; + private long cache = rand.next(); + private int cacheIdx = 0; + + public void tick(ServerLevel world) { + var simpleRandom = world.simpleRandom; + int j = tickPos.size(); + for (int i = 0; i < j; i++) { + tickBlock(world, tickPos.getLong(i), simpleRandom); + } + tickPos.clear(); + } + + private static void tickBlock(ServerLevel world, long packed, RandomSource tickRand) { + final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); + BlockPos pos = BlockPos.of(packed); + LevelChunk chunk = world.chunkSource.getChunkAtIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); + if (chunk == null) { + return; + } + BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); + if (state == null) { + return; + } + state.randomTick(world, pos, tickRand); + if (doubleTickFluids) { + final FluidState fluidState = state.getFluidState(); + if (fluidState.isRandomlyTicking()) { + fluidState.randomTick(world, pos, tickRand); + } + } + } + + private long recompute(LevelChunk chunk, long tickSpeed) { + chunk.leaf$recompute(); + long tickingCount = chunk.leaf$countTickingBlocks(); + long numSections = chunk.leaf$countTickingSections(); + if (tickingCount == 0L || numSections == 0L) { + chunk.leaf$setRandomTickChance(0L); + return 0L; + } + long product = tickSpeed * tickingCount; + long chance = ((product + 2048L) * SCALE) / 4096L; + chunk.leaf$setRandomTickChance(chance); + return chance; + } +/* + public void randomTickChunkOrigin( + ServerLevel level, + LevelChunk chunk, + long tickSpeed + ) { + final LevelChunkSection[] sections = chunk.getSections(); + final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level); // Leaf - Micro optimizations for random tick - no redundant cast + final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = level.simpleRandom; // Leaf - Faster random generator - upcasting + final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); + + final ChunkPos cpos = chunk.getPos(); + final int offsetX = cpos.x << 4; + final int offsetZ = cpos.z << 4; + + for (int sectionIndex = 0, sectionsLen = sections.length; sectionIndex < sectionsLen; sectionIndex++) { + // Leaf start - Micro optimizations for random tick + final LevelChunkSection section = sections[sectionIndex]; + if (!section.isRandomlyTickingBlocks()) { + continue; + } + final int offsetY = (sectionIndex + minSection) << 4; + final net.minecraft.world.level.chunk.PalettedContainer states = section.states; + // Leaf end - Micro optimizations for random tick + + final ca.spottedleaf.moonrise.common.list.ShortList tickList = section.moonrise$getTickingBlockList(); // Leaf - Micro optimizations for random tick - no redundant cast + + for (int i = 0; i < tickSpeed; ++i) { + final int index = simpleRandom.nextInt() & ((16 * 16 * 16) - 1); + + if (index >= tickList.size()) { // Leaf - Micro optimizations for random tick - inline one-time value + // most of the time we fall here + continue; + } + + final int location = tickList.getRaw(index); // Leaf - Micro optimizations for random tick - no unnecessary operations + final BlockState state = states.get(location); + + // do not use a mutable pos, as some random tick implementations store the input without calling immutable()! + final BlockPos pos = new BlockPos((location & 15) | offsetX, (location >>> (4 + 4)) | offsetY, ((location >>> 4) & 15) | offsetZ); // Leaf - Micro optimizations for random tick - no redundant mask + + state.randomTick(level, pos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast + if (doubleTickFluids) { + final FluidState fluidState = state.getFluidState(); + if (fluidState.isRandomlyTicking()) { + fluidState.randomTick(level, pos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast + } + } + } + } + } +*/ + + public void randomTickChunk( + LevelChunk chunk, + long tickSpeed + ) { + long a; + if (cacheIdx == 0) { + a = cache; + cacheIdx = 1; + } else if (cacheIdx == 1) { + a = cache >>> 32; + cacheIdx = 2; + } else { + a = cache = rand.next(); + cacheIdx = 0; + } + if ((a & 0x300000L) != 0L) { + return; + } + tickSpeed = tickSpeed * 4; + long chance = chunk.leaf$randomTickChance(); + if (chance == 0L && (chance = recompute(chunk, tickSpeed)) == 0) { + return; + } + if (chance >= (a & MASK)) { + return; + } + if ((chance = recompute(chunk, tickSpeed)) == 0) { + return; + } + + long tickingCount = chunk.leaf$countTickingBlocks(); + long shouldTick = rand.next() & MASK; + int randPos = (int) ((rand.next() & Integer.MAX_VALUE) % tickingCount); + OptionalLong pos = chunk.leaf$tickingPos(randPos); + if (pos.isPresent()) { + tickPos.add(pos.getAsLong()); + } + while (shouldTick <= chance) { + randPos = (int) ((rand.next() & Integer.MAX_VALUE) % tickingCount); + pos = chunk.leaf$tickingPos(randPos); + if (pos.isPresent()) tickPos.add(pos.getAsLong()); + chance -= SCALE; + } + } + + private final static class WyRand implements BitRandomSource { + private long state; + + private static final long WY0 = 0x2d35_8dcc_aa6c_78a5L; + private static final long WY1 = 0x8bb8_4b93_962e_acc9L; + private static final int BITS = 64; + + public WyRand(long seed) { + this.state = seed; + } + + @Override + public int next(int bits) { + return (int)(this.next() >>> (BITS - bits)); + } + + @Override + public @NotNull RandomSource fork() { + return new WyRand(next()); + } + + @Override + public @NotNull PositionalRandomFactory forkPositional() { + throw new UnsupportedOperationException("forkPositional"); + } + + @Override + public void setSeed(long seed) { + this.state = seed; + } + + public int nextInt() { + return (int) (next() & Integer.MAX_VALUE); + } + + @Override + public double nextGaussian() { + throw new UnsupportedOperationException("nextGaussian"); + } + + public long next() { + long seed = this.state; + seed += WY0; + long aLow = seed & 0xFFFFFFFFL; + long aHigh = seed >>> 32; + long bLow = (seed ^ WY1) & 0xFFFFFFFFL; + long bHigh = (seed ^ WY1) >>> 32; + long loLo = aLow * bLow; + long hiLo = aHigh * bLow; + long loHi = aLow * bHigh; + long hiHi = aHigh * bHigh; + long mid1 = (loLo >>> 32) + (hiLo & 0xFFFFFFFFL) + (loHi & 0xFFFFFFFFL); + long mid2 = (hiLo >>> 32) + (loHi >>> 32) + (mid1 >>> 32); + this.state = seed; + return ((loLo & 0xFFFFFFFFL) | (mid1 << 32)) ^ hiHi + (mid2 & 0xFFFFFFFFL); + } + } +} From 20cc10e45ff7909625d814d9c4956008735b8a36 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 8 Jun 2025 13:26:11 +0900 Subject: [PATCH 44/57] disable optimise-random-tick by default --- .../org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java index 5a265d8e..525bcac7 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java @@ -11,7 +11,7 @@ public class OptimizeRandomTick extends ConfigModules { } @Experimental - public static boolean enabled = true; + public static boolean enabled = false; @Override public void onLoaded() { From 4bf675075afd0f3175ce3538a12e8ff78634ce00 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 8 Jun 2025 15:20:34 +0900 Subject: [PATCH 45/57] fix random tick do extra tick --- .../dreeam/leaf/world/RandomTickSystem.java | 68 +++++++++++++++++-- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java index 5907d2c6..ba234e07 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java @@ -19,6 +19,10 @@ public final class RandomTickSystem { private final WyRand rand = new WyRand(RandomSupport.generateUniqueSeed()); private static final long SCALE = 0x100000L; private static final long MASK = 0xfffffL; + private static final long MASK_ONE_FOURTH = 0x300000L; + private static final long CHUNK_BLOCKS = 4096L; + private static final long CHUNK_BLOCKS_HALF = 2048L; + private long cache = rand.next(); private int cacheIdx = 0; @@ -60,7 +64,7 @@ public final class RandomTickSystem { return 0L; } long product = tickSpeed * tickingCount; - long chance = ((product + 2048L) * SCALE) / 4096L; + long chance = ((product + CHUNK_BLOCKS_HALF) * SCALE) / CHUNK_BLOCKS; chunk.leaf$setRandomTickChance(chance); return chance; } @@ -121,6 +125,7 @@ public final class RandomTickSystem { LevelChunk chunk, long tickSpeed ) { + // reuse the random number one time long a; if (cacheIdx == 0) { a = cache; @@ -132,32 +137,83 @@ public final class RandomTickSystem { a = cache = rand.next(); cacheIdx = 0; } - if ((a & 0x300000L) != 0L) { + + // 25% chance tick + if ((a & MASK_ONE_FOURTH) != 0L) { return; } + // tick speed mul 4 tickSpeed = tickSpeed * 4; + long chance = chunk.leaf$randomTickChance(); + + // the chunk not exists random tickable block if (chance == 0L && (chance = recompute(chunk, tickSpeed)) == 0) { return; } + + // this is correct, don't modify + // + // when chance eq 0.1 + // - skip: 0.1 >= 0.1..1.0 + // - tick: 0.1 < 0.0..0.1 + // + // (chance not newest) if (chance >= (a & MASK)) { return; } + + // recompute tickable block and chance. + // + // chance for next randomTickChunk + // tickingCount used this tick + // + // chance != 0 + // + // always tick block when chance > 1 if ((chance = recompute(chunk, tickSpeed)) == 0) { return; } - long tickingCount = chunk.leaf$countTickingBlocks(); - long shouldTick = rand.next() & MASK; + + // tick one block base on chance + // fairly pick a random tickable block + // vanilla may do more tick + // recompute computed chance for that part + // so the tick count should same as vanilla int randPos = (int) ((rand.next() & Integer.MAX_VALUE) % tickingCount); OptionalLong pos = chunk.leaf$tickingPos(randPos); + // always true if (pos.isPresent()) { tickPos.add(pos.getAsLong()); } - while (shouldTick <= chance) { + + // chance less than one + // most case + if (chance < SCALE) { + return; + } + + // attempt to do more tick + // + // chance == 1.5 + // - loop 1: 50% tick + // - loop 2: never + // + // chance == 2.5 + // - loop 1: always + // - loop 2: 50% + // - loop 3: never + + chance -= SCALE; + long last = rand.next() & MASK; + while (last < chance) { randPos = (int) ((rand.next() & Integer.MAX_VALUE) % tickingCount); pos = chunk.leaf$tickingPos(randPos); - if (pos.isPresent()) tickPos.add(pos.getAsLong()); + // always true + if (pos.isPresent()) { + tickPos.add(pos.getAsLong()); + } chance -= SCALE; } } From 0eeb6e719c79efdb3cdc8368e11377d430e003f5 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 8 Jun 2025 15:33:21 +0900 Subject: [PATCH 46/57] fix tickingPos out of bounds --- .../features/0192-optimize-random-tick.patch | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch index 54a9bf0d..0eafd9e4 100644 --- a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch +++ b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch @@ -39,7 +39,7 @@ index eb849c57992658005e0f514c6f7923f8ca43bebf..2efcdb9bc91b9106b4aef9e24cc20596 } diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 624a177695580510c0a49d4503dee72da7fd7114..8be4f0978451179fca8b9603743e875817040a08 100644 +index 624a177695580510c0a49d4503dee72da7fd7114..affe9fff1ff2f7e221f8cfe345d40d707e0f3dbc 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -151,6 +151,52 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p @@ -80,12 +80,12 @@ index 624a177695580510c0a49d4503dee72da7fd7114..8be4f0978451179fca8b9603743e8758 + LevelChunkSection section = sections[i]; + var l = section.moonrise$getTickingBlockList(); + int size = l.size(); -+ if (size > idx) { -+ short loc = l.getRaw(size - idx); -+ return java.util.OptionalLong.of(BlockPos.asLong( -+ (loc & 15) | (chunkPos.x << 4), -+ (loc >>> 8) | (((getMinSectionY() + i) << 4)), -+ ((loc >>> 4) & 15) | (chunkPos.z << 4))); ++ if (idx < size) { ++ short loc = l.getRaw(idx); ++ int x = (loc & 15) | (chunkPos.x << 4); ++ int y = (loc >>> 8) | ((getMinSectionY() + i) << 4); ++ int z = ((loc >>> 4) & 15) | (chunkPos.z << 4); ++ return java.util.OptionalLong.of(BlockPos.asLong(x, y, z)); + } + idx -= size; + } From 47c1783afc473a37f29d57b0f2f3f3b029df255e Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 8 Jun 2025 15:55:48 +0900 Subject: [PATCH 47/57] improve ServerStatsCounter compatibility --- .../0086-Nitori-Async-playerdata-saving.patch | 16 ++++++++++++---- ...Skip-dirty-stats-copy-when-requesting-p.patch | 6 +++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch index 5fdb3805..29a7add3 100644 --- a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch +++ b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch @@ -138,10 +138,10 @@ index 75fb49f1596f475278d12c8c7aea9ad4952b6056..52a0fa425a30caa2e592c0fdda44800d } diff --git a/net/minecraft/stats/ServerStatsCounter.java b/net/minecraft/stats/ServerStatsCounter.java -index b26dbe807e5cb0a42f6c06b933397902310e5616..35ad7f249cfb6f5c779136d96f3698ea4de1eb7c 100644 +index b26dbe807e5cb0a42f6c06b933397902310e5616..a682ae890850a5e62acd54029f2a4eb1e69d200a 100644 --- a/net/minecraft/stats/ServerStatsCounter.java +++ b/net/minecraft/stats/ServerStatsCounter.java -@@ -39,12 +39,23 @@ public class ServerStatsCounter extends StatsCounter { +@@ -39,12 +39,31 @@ public class ServerStatsCounter extends StatsCounter { private final File file; private final Set> dirty = Sets.newHashSet(); @@ -150,7 +150,15 @@ index b26dbe807e5cb0a42f6c06b933397902310e5616..35ad7f249cfb6f5c779136d96f3698ea + private final java.util.UUID uuid; + @Deprecated(forRemoval = true) public ServerStatsCounter(MinecraftServer server, File file) { -+ throw new UnsupportedOperationException(); ++ this(server, file, "UNKNOWN", parseUUID(file)); ++ } ++ private static java.util.UUID parseUUID(File file) { ++ try { ++ return java.util.UUID.fromString(org.apache.commons.io.FilenameUtils.getBaseName(file.toString())); ++ } catch (IllegalArgumentException e) { ++ LOGGER.error("failed parse uuid {}", file, e); ++ return net.minecraft.Util.NIL_UUID; ++ } + } + public ServerStatsCounter(MinecraftServer server, File file, String name, java.util.UUID uuid) { + this.name = name; @@ -166,7 +174,7 @@ index b26dbe807e5cb0a42f6c06b933397902310e5616..35ad7f249cfb6f5c779136d96f3698ea } catch (IOException var4) { LOGGER.error("Couldn't read statistics file {}", file, var4); } catch (JsonParseException var5) { -@@ -66,11 +77,37 @@ public class ServerStatsCounter extends StatsCounter { +@@ -66,11 +85,37 @@ public class ServerStatsCounter extends StatsCounter { public void save() { if (org.spigotmc.SpigotConfig.disableStatSaving) return; // Spigot diff --git a/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch b/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch index 9b461562..486b2c51 100644 --- a/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch +++ b/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch @@ -6,10 +6,10 @@ Subject: [PATCH] SparklyPaper: Skip dirty stats copy when requesting player diff --git a/net/minecraft/stats/ServerStatsCounter.java b/net/minecraft/stats/ServerStatsCounter.java -index 35ad7f249cfb6f5c779136d96f3698ea4de1eb7c..523dc12a8866a199eac1b2f418bf206f068ba80c 100644 +index a682ae890850a5e62acd54029f2a4eb1e69d200a..cd2b99f82a56836a4f4e32cd6e02450f77fe9149 100644 --- a/net/minecraft/stats/ServerStatsCounter.java +++ b/net/minecraft/stats/ServerStatsCounter.java -@@ -118,11 +118,15 @@ public class ServerStatsCounter extends StatsCounter { +@@ -126,11 +126,15 @@ public class ServerStatsCounter extends StatsCounter { this.dirty.add(stat); } @@ -25,7 +25,7 @@ index 35ad7f249cfb6f5c779136d96f3698ea4de1eb7c..523dc12a8866a199eac1b2f418bf206f public void parseLocal(DataFixer fixerUpper, String json) { try { -@@ -231,10 +235,12 @@ public class ServerStatsCounter extends StatsCounter { +@@ -239,10 +243,12 @@ public class ServerStatsCounter extends StatsCounter { public void sendStats(ServerPlayer player) { Object2IntMap> map = new Object2IntOpenHashMap<>(); From aad17b0a5b07800720f48f7fad1b8ad1fd054361 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 8 Jun 2025 17:31:34 +0900 Subject: [PATCH 48/57] fix random tick --- .../features/0192-optimize-random-tick.patch | 18 +- .../dreeam/leaf/world/RandomTickSystem.java | 217 ++---------------- 2 files changed, 28 insertions(+), 207 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch index 0eafd9e4..8794f7f0 100644 --- a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch +++ b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch @@ -17,7 +17,7 @@ index 2f927b422c2c4f2f65d822befe3cbfd9e3bb3708..d0fcfeaf093b718c8acd6e057176d569 if (flagAndHasNaturalSpawn) { // Gale - MultiPaper - skip unnecessary mob spawning computations this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index eb849c57992658005e0f514c6f7923f8ca43bebf..2efcdb9bc91b9106b4aef9e24cc20596be4a5661 100644 +index eb849c57992658005e0f514c6f7923f8ca43bebf..0bf765334f20fa5a999400076797d5b1f82c7469 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -1128,6 +1128,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -33,13 +33,13 @@ index eb849c57992658005e0f514c6f7923f8ca43bebf..2efcdb9bc91b9106b4aef9e24cc20596 if (randomTickSpeed > 0) { - this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking -+ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.randomTickChunk(chunk, randomTickSpeed); // Leaf - random tick ++ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.randomTickChunk(this.simpleRandom, chunk, randomTickSpeed); // Leaf - random tick + else this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking // Leaf - random tick } } diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 624a177695580510c0a49d4503dee72da7fd7114..affe9fff1ff2f7e221f8cfe345d40d707e0f3dbc 100644 +index 624a177695580510c0a49d4503dee72da7fd7114..f85d46c23824de177fe0c08b2ce6fbbb81c3535b 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -151,6 +151,52 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p @@ -48,8 +48,8 @@ index 624a177695580510c0a49d4503dee72da7fd7114..affe9fff1ff2f7e221f8cfe345d40d70 + // Leaf start - random tick + private long leaf$randomTickChance; -+ private long leaf$countTickingBlocks; -+ private long leaf$countTickingSections; ++ private int leaf$countTickingBlocks; ++ private int leaf$countTickingSections; + + public final long leaf$randomTickChance() { + return leaf$randomTickChance; @@ -57,15 +57,15 @@ index 624a177695580510c0a49d4503dee72da7fd7114..affe9fff1ff2f7e221f8cfe345d40d70 + public final void leaf$setRandomTickChance(long chance) { + leaf$randomTickChance = chance; + } -+ public final long leaf$countTickingBlocks() { ++ public final int leaf$countTickingBlocks() { + return leaf$countTickingBlocks; + } -+ public final long leaf$countTickingSections() { ++ public final int leaf$countTickingSections() { + return leaf$countTickingSections; + } + public final void leaf$recompute() { -+ long total1 = 0L; -+ long total2 = 0L; ++ int total1 = 0; ++ int total2 = 0; + for (LevelChunkSection section : sections) { + total1 += section.moonrise$getTickingBlockList().size(); + if (section.isRandomlyTickingBlocks()) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java index ba234e07..afff1346 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java @@ -16,15 +16,10 @@ import java.util.OptionalLong; public final class RandomTickSystem { private final LongArrayList tickPos = new LongArrayList(); - private final WyRand rand = new WyRand(RandomSupport.generateUniqueSeed()); private static final long SCALE = 0x100000L; - private static final long MASK = 0xfffffL; - private static final long MASK_ONE_FOURTH = 0x300000L; private static final long CHUNK_BLOCKS = 4096L; - private static final long CHUNK_BLOCKS_HALF = 2048L; - - private long cache = rand.next(); - private int cacheIdx = 0; + private static final int MASK = 0xfffff; + private static final int MASK_ONE_FOURTH = 0x300000; public void tick(ServerLevel world) { var simpleRandom = world.simpleRandom; @@ -43,9 +38,6 @@ public final class RandomTickSystem { return; } BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); - if (state == null) { - return; - } state.randomTick(world, pos, tickRand); if (doubleTickFluids) { final FluidState fluidState = state.getFluidState(); @@ -63,216 +55,45 @@ public final class RandomTickSystem { chunk.leaf$setRandomTickChance(0L); return 0L; } - long product = tickSpeed * tickingCount; - long chance = ((product + CHUNK_BLOCKS_HALF) * SCALE) / CHUNK_BLOCKS; + long chance = (tickSpeed * tickingCount * SCALE) / CHUNK_BLOCKS; chunk.leaf$setRandomTickChance(chance); return chance; } -/* - public void randomTickChunkOrigin( - ServerLevel level, - LevelChunk chunk, - long tickSpeed - ) { - final LevelChunkSection[] sections = chunk.getSections(); - final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level); // Leaf - Micro optimizations for random tick - no redundant cast - final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = level.simpleRandom; // Leaf - Faster random generator - upcasting - final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); - - final ChunkPos cpos = chunk.getPos(); - final int offsetX = cpos.x << 4; - final int offsetZ = cpos.z << 4; - - for (int sectionIndex = 0, sectionsLen = sections.length; sectionIndex < sectionsLen; sectionIndex++) { - // Leaf start - Micro optimizations for random tick - final LevelChunkSection section = sections[sectionIndex]; - if (!section.isRandomlyTickingBlocks()) { - continue; - } - final int offsetY = (sectionIndex + minSection) << 4; - final net.minecraft.world.level.chunk.PalettedContainer states = section.states; - // Leaf end - Micro optimizations for random tick - - final ca.spottedleaf.moonrise.common.list.ShortList tickList = section.moonrise$getTickingBlockList(); // Leaf - Micro optimizations for random tick - no redundant cast - - for (int i = 0; i < tickSpeed; ++i) { - final int index = simpleRandom.nextInt() & ((16 * 16 * 16) - 1); - - if (index >= tickList.size()) { // Leaf - Micro optimizations for random tick - inline one-time value - // most of the time we fall here - continue; - } - - final int location = tickList.getRaw(index); // Leaf - Micro optimizations for random tick - no unnecessary operations - final BlockState state = states.get(location); - - // do not use a mutable pos, as some random tick implementations store the input without calling immutable()! - final BlockPos pos = new BlockPos((location & 15) | offsetX, (location >>> (4 + 4)) | offsetY, ((location >>> 4) & 15) | offsetZ); // Leaf - Micro optimizations for random tick - no redundant mask - - state.randomTick(level, pos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast - if (doubleTickFluids) { - final FluidState fluidState = state.getFluidState(); - if (fluidState.isRandomlyTicking()) { - fluidState.randomTick(level, pos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast - } - } - } - } - } -*/ public void randomTickChunk( + RandomSource randomSource, LevelChunk chunk, long tickSpeed ) { - // reuse the random number one time - long a; - if (cacheIdx == 0) { - a = cache; - cacheIdx = 1; - } else if (cacheIdx == 1) { - a = cache >>> 32; - cacheIdx = 2; - } else { - a = cache = rand.next(); - cacheIdx = 0; - } - - // 25% chance tick - if ((a & MASK_ONE_FOURTH) != 0L) { + int a = randomSource.nextInt(); + if ((a & MASK_ONE_FOURTH) != 0) { return; } - // tick speed mul 4 tickSpeed = tickSpeed * 4; long chance = chunk.leaf$randomTickChance(); - - // the chunk not exists random tickable block - if (chance == 0L && (chance = recompute(chunk, tickSpeed)) == 0) { + if (chance == 0L && (chance = recompute(chunk, tickSpeed)) == 0L) { return; } - - // this is correct, don't modify - // - // when chance eq 0.1 - // - skip: 0.1 >= 0.1..1.0 - // - tick: 0.1 < 0.0..0.1 - // - // (chance not newest) - if (chance >= (a & MASK)) { + if (chance >= (long) (a & MASK) || (chance = recompute(chunk, tickSpeed)) == 0L) { return; } - - // recompute tickable block and chance. - // - // chance for next randomTickChunk - // tickingCount used this tick - // - // chance != 0 - // - // always tick block when chance > 1 - if ((chance = recompute(chunk, tickSpeed)) == 0) { - return; - } - long tickingCount = chunk.leaf$countTickingBlocks(); - - // tick one block base on chance - // fairly pick a random tickable block - // vanilla may do more tick - // recompute computed chance for that part - // so the tick count should same as vanilla - int randPos = (int) ((rand.next() & Integer.MAX_VALUE) % tickingCount); - OptionalLong pos = chunk.leaf$tickingPos(randPos); - // always true + int tickingCount = chunk.leaf$countTickingBlocks(); + OptionalLong pos = chunk.leaf$tickingPos(randomSource.nextInt(tickingCount)); if (pos.isPresent()) { tickPos.add(pos.getAsLong()); } - // chance less than one - // most case - if (chance < SCALE) { - return; - } - - // attempt to do more tick - // - // chance == 1.5 - // - loop 1: 50% tick - // - loop 2: never - // - // chance == 2.5 - // - loop 1: always - // - loop 2: 50% - // - loop 3: never - - chance -= SCALE; - long last = rand.next() & MASK; - while (last < chance) { - randPos = (int) ((rand.next() & Integer.MAX_VALUE) % tickingCount); - pos = chunk.leaf$tickingPos(randPos); - // always true - if (pos.isPresent()) { - tickPos.add(pos.getAsLong()); - } + if (chance > SCALE) { chance -= SCALE; - } - } - - private final static class WyRand implements BitRandomSource { - private long state; - - private static final long WY0 = 0x2d35_8dcc_aa6c_78a5L; - private static final long WY1 = 0x8bb8_4b93_962e_acc9L; - private static final int BITS = 64; - - public WyRand(long seed) { - this.state = seed; - } - - @Override - public int next(int bits) { - return (int)(this.next() >>> (BITS - bits)); - } - - @Override - public @NotNull RandomSource fork() { - return new WyRand(next()); - } - - @Override - public @NotNull PositionalRandomFactory forkPositional() { - throw new UnsupportedOperationException("forkPositional"); - } - - @Override - public void setSeed(long seed) { - this.state = seed; - } - - public int nextInt() { - return (int) (next() & Integer.MAX_VALUE); - } - - @Override - public double nextGaussian() { - throw new UnsupportedOperationException("nextGaussian"); - } - - public long next() { - long seed = this.state; - seed += WY0; - long aLow = seed & 0xFFFFFFFFL; - long aHigh = seed >>> 32; - long bLow = (seed ^ WY1) & 0xFFFFFFFFL; - long bHigh = (seed ^ WY1) >>> 32; - long loLo = aLow * bLow; - long hiLo = aHigh * bLow; - long loHi = aLow * bHigh; - long hiHi = aHigh * bHigh; - long mid1 = (loLo >>> 32) + (hiLo & 0xFFFFFFFFL) + (loHi & 0xFFFFFFFFL); - long mid2 = (hiLo >>> 32) + (loHi >>> 32) + (mid1 >>> 32); - this.state = seed; - return ((loLo & 0xFFFFFFFFL) | (mid1 << 32)) ^ hiHi + (mid2 & 0xFFFFFFFFL); + long last = randomSource.nextInt() & MASK; + while (last < chance) { + pos = chunk.leaf$tickingPos(randomSource.nextInt(tickingCount)); + if (pos.isPresent()) { + tickPos.add(pos.getAsLong()); + } + chance -= SCALE; + } } } } From 8d36c9a5f73af1ef62f961b1d47f636aa287b209 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 8 Jun 2025 19:05:34 +0900 Subject: [PATCH 49/57] revert level dat --- .../0086-Nitori-Async-playerdata-saving.patch | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch index 29a7add3..96e1863f 100644 --- a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch +++ b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch @@ -212,49 +212,6 @@ index b26dbe807e5cb0a42f6c06b933397902310e5616..a682ae890850a5e62acd54029f2a4eb1 } @Override -diff --git a/net/minecraft/world/level/storage/LevelStorageSource.java b/net/minecraft/world/level/storage/LevelStorageSource.java -index de43e54698125ce9f319d4889dd49f7029fe95e0..360e54b87db68fad60cdec63af466765baae0a07 100644 ---- a/net/minecraft/world/level/storage/LevelStorageSource.java -+++ b/net/minecraft/world/level/storage/LevelStorageSource.java -@@ -520,15 +520,24 @@ public class LevelStorageSource { - private void saveLevelData(CompoundTag tag) { - Path path = this.levelDirectory.path(); - -+ // Leaf start - Async player IO -+ // Save level.dat asynchronously -+ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536); - try { -- Path path1 = Files.createTempFile(path, "level", ".dat"); -- NbtIo.writeCompressed(tag, path1); -- Path path2 = this.levelDirectory.oldDataFile(); -- Path path3 = this.levelDirectory.dataFile(); -- Util.safeReplaceFile(path3, path1, path2); -+ NbtIo.writeCompressed(tag, nbtBytes); - } catch (Exception var6) { -- LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6); -+ LevelStorageSource.LOGGER.error("Failed to encode level {}", path, var6); - } -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.saveLevelData(path, () -> { -+ try { -+ Path old = this.levelDirectory.oldDataFile(); -+ Path current = this.levelDirectory.dataFile(); -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplaceBackup(current, old, nbtBytes.array, 0, nbtBytes.length); -+ } catch (Exception var6) { -+ LevelStorageSource.LOGGER.error("Failed to save level.dat {}", path, var6); -+ } -+ }); -+ // Leaf end - Async player IO - } - - public Optional getIconFile() { -@@ -645,6 +654,7 @@ public class LevelStorageSource { - - @Override - public void close() throws IOException { -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.saveLevelData(this.levelDirectory.path(), null); // Leaf - Async player IO - this.lock.close(); - } - diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java index c44110b123ba5912af18faf0065e9ded780da9b7..2eae5ccb37b942b94964c28391b96989ae85b072 100644 --- a/net/minecraft/world/level/storage/PlayerDataStorage.java From f6079308275bdf1b5a5f556708176032008ba190 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 8 Jun 2025 19:22:17 +0900 Subject: [PATCH 50/57] Revert Revert "async saving player stats and advancements (#334)" This reverts commit 107ae7954f024b1fb250373a1929766561c5a8ed. Revert "optimize random tick (#357)" This reverts commit 2e822d3714957be62e7263f6a8413b0fb9b603b7. Revert "disable optimise-random-tick by default" This reverts commit 20cc10e45ff7909625d814d9c4956008735b8a36. Revert "fix random tick do extra tick" This reverts commit 4bf675075afd0f3175ce3538a12e8ff78634ce00. Revert "fix tickingPos out of bounds" This reverts commit 0eeb6e719c79efdb3cdc8368e11377d430e003f5. Revert "improve ServerStatsCounter compatibility" This reverts commit 47c1783afc473a37f29d57b0f2f3f3b029df255e. Revert "fix random tick" This reverts commit aad17b0a5b07800720f48f7fad1b8ad1fd054361. Revert "revert level dat" This reverts commit 8d36c9a5f73af1ef62f961b1d47f636aa287b209. --- .../server/0100-Smooth-teleport-config.patch | 4 +- .../0086-Nitori-Async-playerdata-saving.patch | 344 ++++++------------ ...p-dirty-stats-copy-when-requesting-p.patch | 6 +- ...-SparklyPaper-Parallel-world-ticking.patch | 18 +- .../0163-Async-switch-connection-state.patch | 4 +- .../features/0192-optimize-random-tick.patch | 97 ----- .../0033-Async-playerdata-saving.patch | 6 +- .../leaf/async/AsyncPlayerDataSaving.java | 37 ++ .../dreeam/leaf/async/ShutdownExecutors.java | 1 - .../async/storage/AsyncPlayerDataSaving.java | 307 ---------------- .../modules/async/AsyncPlayerDataSave.java | 11 - .../modules/opt/OptimizeRandomTick.java | 20 - .../dreeam/leaf/world/RandomTickSystem.java | 99 ----- .../leavesmc/leaves/bot/BotStatsCounter.java | 2 +- 14 files changed, 171 insertions(+), 785 deletions(-) delete mode 100644 leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/storage/AsyncPlayerDataSaving.java delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java diff --git a/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch b/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch index b5c7d20c..30429f8d 100644 --- a/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch +++ b/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch @@ -32,10 +32,10 @@ index 4f01b53bf801f99253efd27df6216912705d18af..82a1715fea41e6a41c4ff441ea89f424 level.addDuringTeleport(this); this.triggerDimensionChangeTriggers(serverLevel); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 52a0fa425a30caa2e592c0fdda44800da169c2a0..3f5c5b6234eb400838973c37e5a48bb121d1ff16 100644 +index 75fb49f1596f475278d12c8c7aea9ad4952b6056..b17c8a2f5294ac28cc05fb05c84a041b2c6c8721 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java -@@ -997,11 +997,11 @@ public abstract class PlayerList { +@@ -955,11 +955,11 @@ public abstract class PlayerList { byte b = (byte)(keepInventory ? 1 : 0); ServerLevel serverLevel = serverPlayer.serverLevel(); LevelData levelData = serverLevel.getLevelData(); diff --git a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch index 96e1863f..337ffac9 100644 --- a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch +++ b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch @@ -6,221 +6,60 @@ Subject: [PATCH] Nitori: Async playerdata saving Original license: GPL v3 Original project: https://github.com/Gensokyo-Reimagined/Nitori -diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index 00a82873d226f113278632a53c0faca420dd67d4..2c4423eb2d465c2782a8dab851619ce539f69ae8 100644 ---- a/net/minecraft/network/Connection.java -+++ b/net/minecraft/network/Connection.java -@@ -586,7 +586,7 @@ public class Connection extends SimpleChannelInboundHandler> { - // Paper end - Optimize network +diff --git a/net/minecraft/world/level/storage/LevelStorageSource.java b/net/minecraft/world/level/storage/LevelStorageSource.java +index de43e54698125ce9f319d4889dd49f7029fe95e0..742bd4b60321adc9e63c3de910ea95f4990b618d 100644 +--- a/net/minecraft/world/level/storage/LevelStorageSource.java ++++ b/net/minecraft/world/level/storage/LevelStorageSource.java +@@ -520,15 +520,26 @@ public class LevelStorageSource { + private void saveLevelData(CompoundTag tag) { + Path path = this.levelDirectory.path(); - private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world -- private static int joinAttemptsThisTick; // Paper - Buffer joins to world -+ public static int joinAttemptsThisTick; // Paper - Buffer joins to world // Leaf - Async player IO - private static int currTick; // Paper - Buffer joins to world - private static int tickSecond; // Purpur - Max joins per second - public void tick() { -diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java -index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..d5196b181a0a633cb04ce18b0471cda2dcaa8816 100644 ---- a/net/minecraft/server/PlayerAdvancements.java -+++ b/net/minecraft/server/PlayerAdvancements.java -@@ -111,6 +111,7 @@ public class PlayerAdvancements { - } - - private void load(ServerAdvancementManager manager) { -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.blockAdvancements(player.getUUID(), player.getScoreboardName()); // Leaf - Async player IO - if (Files.isRegularFile(this.playerSavePath)) { - try (JsonReader jsonReader = new JsonReader(Files.newBufferedReader(this.playerSavePath, StandardCharsets.UTF_8))) { - jsonReader.setLenient(false); -@@ -133,12 +134,18 @@ public class PlayerAdvancements { - JsonElement jsonElement = this.codec.encodeStart(JsonOps.INSTANCE, this.asData()).getOrThrow(); - - try { -- FileUtil.createDirectoriesSafe(this.playerSavePath.getParent()); -- -- try (Writer bufferedWriter = Files.newBufferedWriter(this.playerSavePath, StandardCharsets.UTF_8)) { -- GSON.toJson(jsonElement, GSON.newJsonWriter(bufferedWriter)); -- } -- } catch (JsonIOException | IOException var7) { -+ // Leaf start - Async player IO -+ String content = GSON.toJson(jsonElement); -+ final Path path = this.playerSavePath; -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.submitAdvancements( -+ this.player.getUUID(), -+ this.player.getScoreboardName(), -+ () -> { -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplace(path, content); -+ return null; -+ }); -+ } catch (JsonIOException /*| IOException*/ var7) { -+ // Leaf end - Async player IO - LOGGER.error("Couldn't save player advancements to {}", this.playerSavePath, var7); - } - } -diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 114b25f933c6a1b011523581a5a02a5a2c1e827e..3da6dad3dd0f4c5750609b382f47a6cd14f18e7a 100644 ---- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -79,6 +79,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, - .expireAfterWrite(org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResultTimeout, java.util.concurrent.TimeUnit.MINUTES) - .build(); - // Leaf end - Cache player profileResult -+ @Nullable public java.util.UUID[] duplicateDisconnect = null; // Leaf - Async player IO - - public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) { - this.server = server; -diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 75fb49f1596f475278d12c8c7aea9ad4952b6056..52a0fa425a30caa2e592c0fdda44800da169c2a0 100644 ---- a/net/minecraft/server/players/PlayerList.java -+++ b/net/minecraft/server/players/PlayerList.java -@@ -782,6 +782,31 @@ public abstract class PlayerList { - // UserBanListEntry userBanListEntry = this.bans.get(gameProfile); - // Moved from processLogin - UUID uuid = gameProfile.getId(); -+ -+ // Leaf start - Async player IO -+ if (org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.enabled) { -+ if (org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.isSaving(uuid)) { -+ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1; -+ return null; -+ } -+ if (loginlistener.duplicateDisconnect != null -+ && loginlistener.duplicateDisconnect.length != 0) { -+ // check last one -+ var last = loginlistener.duplicateDisconnect[loginlistener.duplicateDisconnect.length - 1]; -+ if (org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.isSaving(last)) { -+ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1; -+ return null; -+ } -+ for (UUID uuid1 : loginlistener.duplicateDisconnect) { -+ if (org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.isSaving(uuid1)) { -+ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1; -+ return null; -+ } -+ } -+ loginlistener.duplicateDisconnect = null; -+ } -+ } -+ // Leaf end - Async player IO - List list = Lists.newArrayList(); - - ServerPlayer entityplayer; -@@ -793,6 +818,23 @@ public abstract class PlayerList { - } - } - -+ // Leaf start - Async player IO -+ if (org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.enabled && !list.isEmpty()) { -+ loginlistener.duplicateDisconnect = new UUID[list.size()]; -+ java.util.Iterator iterator = list.iterator(); -+ -+ int index = 0; -+ while (iterator.hasNext()) { -+ entityplayer = iterator.next(); -+ // this.save(entityplayer); // CraftBukkit - Force the player's inventory to be saved -+ entityplayer.connection.disconnect(Component.translatable("multiplayer.disconnect.duplicate_login"), org.bukkit.event.player.PlayerKickEvent.Cause.DUPLICATE_LOGIN); // Paper - kick event cause -+ loginlistener.duplicateDisconnect[index] = entityplayer.getUUID(); -+ index++; -+ } -+ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1; -+ return null; -+ } -+ // Leaf end - Async player IO - java.util.Iterator iterator = list.iterator(); - - while (iterator.hasNext()) { -@@ -1582,7 +1624,7 @@ public abstract class PlayerList { - */ - // Leaf end - Remove useless creating stats json bases on player name logic - -- serverStatsCounter = new ServerStatsCounter(this.server, file1); -+ serverStatsCounter = new ServerStatsCounter(this.server, file1, displayName, uuid); - // this.stats.put(uuid, serverStatsCounter); // CraftBukkit - } - -diff --git a/net/minecraft/stats/ServerStatsCounter.java b/net/minecraft/stats/ServerStatsCounter.java -index b26dbe807e5cb0a42f6c06b933397902310e5616..a682ae890850a5e62acd54029f2a4eb1e69d200a 100644 ---- a/net/minecraft/stats/ServerStatsCounter.java -+++ b/net/minecraft/stats/ServerStatsCounter.java -@@ -39,12 +39,31 @@ public class ServerStatsCounter extends StatsCounter { - private final File file; - private final Set> dirty = Sets.newHashSet(); - -+ // Leaf start - Async player IO -+ private final String name; -+ private final java.util.UUID uuid; -+ @Deprecated(forRemoval = true) - public ServerStatsCounter(MinecraftServer server, File file) { -+ this(server, file, "UNKNOWN", parseUUID(file)); -+ } -+ private static java.util.UUID parseUUID(File file) { -+ try { -+ return java.util.UUID.fromString(org.apache.commons.io.FilenameUtils.getBaseName(file.toString())); -+ } catch (IllegalArgumentException e) { -+ LOGGER.error("failed parse uuid {}", file, e); -+ return net.minecraft.Util.NIL_UUID; -+ } -+ } -+ public ServerStatsCounter(MinecraftServer server, File file, String name, java.util.UUID uuid) { -+ this.name = name; -+ this.uuid = uuid; -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.blockStats(uuid, name); -+ // Leaf end - Async player IO - this.server = server; - this.file = file; - if (file.isFile()) { ++ // Leaf start - Async playerdata saving ++ // Save level.dat asynchronously ++ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536); try { -- this.parseLocal(server.getFixerUpper(), FileUtils.readFileToString(file)); -+ this.parseLocal(server.getFixerUpper(), FileUtils.readFileToString(file, java.nio.charset.StandardCharsets.UTF_8)); // Leaf - UTF-8 - } catch (IOException var4) { - LOGGER.error("Couldn't read statistics file {}", file, var4); - } catch (JsonParseException var5) { -@@ -66,11 +85,37 @@ public class ServerStatsCounter extends StatsCounter { - - public void save() { - if (org.spigotmc.SpigotConfig.disableStatSaving) return; // Spigot -+ // Leaf start - Async player IO -+ Map, JsonObject> map = Maps.newHashMap(); -+ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry> entry : this.stats.object2IntEntrySet()) { -+ Stat stat = entry.getKey(); -+ map.computeIfAbsent(stat.getType(), type -> new JsonObject()).addProperty(getKey(stat).toString(), entry.getIntValue()); -+ } -+ final File file = this.file; -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.submitStats( -+ uuid, -+ name, -+ () -> { -+ JsonObject jsonObject = new JsonObject(); -+ -+ for (Entry, JsonObject> entry1 : map.entrySet()) { -+ jsonObject.add(BuiltInRegistries.STAT_TYPE.getKey(entry1.getKey()).toString(), entry1.getValue()); +- Path path1 = Files.createTempFile(path, "level", ".dat"); +- NbtIo.writeCompressed(tag, path1); +- Path path2 = this.levelDirectory.oldDataFile(); +- Path path3 = this.levelDirectory.dataFile(); +- Util.safeReplaceFile(path3, path1, path2); ++ NbtIo.writeCompressed(tag, nbtBytes); + } catch (Exception var6) { +- LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6); ++ LevelStorageSource.LOGGER.error("Failed to encode level {}", path, var6); + } ++ org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> { ++ try { ++ Path path1 = Files.createTempFile(path, "level", ".dat"); ++ org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false); ++ Path path2 = this.levelDirectory.oldDataFile(); ++ Path path3 = this.levelDirectory.dataFile(); ++ Util.safeReplaceFile(path3, path1, path2); ++ } catch (Exception var6) { ++ LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6); + } -+ -+ JsonObject jsonObject1 = new JsonObject(); -+ jsonObject1.add("stats", jsonObject); -+ jsonObject1.addProperty("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplace(file.toPath(), jsonObject1.toString()); -+ return null; + }); -+ /* - try { - FileUtils.writeStringToFile(this.file, this.toJson()); - } catch (IOException var2) { - LOGGER.error("Couldn't save stats", (Throwable)var2); ++ // Leaf end - Async playerdata saving } -+ */ -+ // Leaf end - Async player IO - } - @Override + public Optional getIconFile() { diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java -index c44110b123ba5912af18faf0065e9ded780da9b7..2eae5ccb37b942b94964c28391b96989ae85b072 100644 +index c44110b123ba5912af18faf0065e9ded780da9b7..fd8b4832c8b4a52bd8f9b3ea59111af85127b573 100644 --- a/net/minecraft/world/level/storage/PlayerDataStorage.java +++ b/net/minecraft/world/level/storage/PlayerDataStorage.java -@@ -34,19 +34,37 @@ public class PlayerDataStorage { +@@ -25,6 +25,7 @@ public class PlayerDataStorage { + private final File playerDir; + protected final DataFixer fixerUpper; + private static final DateTimeFormatter FORMATTER = FileNameDateFormatter.create(); ++ private final java.util.Map> savingLocks = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // Leaf - Async playerdata saving + + public PlayerDataStorage(LevelStorageSource.LevelStorageAccess levelStorageAccess, DataFixer fixerUpper) { + this.fixerUpper = fixerUpper; +@@ -34,19 +35,82 @@ public class PlayerDataStorage { public void save(Player player) { if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return; // Spigot -+ // Leaf start - Async player IO ++ // Leaf start - Async playerdata saving + CompoundTag compoundTag; try { - CompoundTag compoundTag = player.saveWithoutId(new CompoundTag()); @@ -238,60 +77,105 @@ index c44110b123ba5912af18faf0065e9ded780da9b7..2eae5ccb37b942b94964c28391b96989 + return; } + save(player.getScoreboardName(), player.getUUID(), player.getStringUUID(), compoundTag); -+ // Leaf end - Async player IO ++ // Leaf end - Async playerdata saving } -+ // Leaf start - Async player IO -+ public void save(String playerName, java.util.UUID uuid, String stringId, CompoundTag compoundTag) { -+ final File playerDir = this.playerDir; -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.submitEntity( -+ uuid, -+ playerName, -+ () -> { -+ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536); -+ NbtIo.writeCompressed(compoundTag, nbtBytes); -+ Path path = playerDir.toPath(); -+ -+ Path current = path.resolve(stringId + ".dat"); -+ Path old = path.resolve(stringId + ".dat_old"); -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplaceBackup(current, old, nbtBytes.array, 0, nbtBytes.length); -+ return null; -+ }); ++ // Leaf start - Async playerdata saving ++ public void save(String playerName, java.util.UUID uniqueId, String stringId, CompoundTag compoundTag) { ++ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536); ++ try { ++ NbtIo.writeCompressed(compoundTag, nbtBytes); ++ } catch (Exception exception) { ++ LOGGER.warn("Failed to encode player data for {}", stringId, exception); ++ } ++ lockFor(uniqueId, playerName); ++ synchronized (PlayerDataStorage.this) { ++ org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> { ++ try { ++ Path path = this.playerDir.toPath(); ++ Path path1 = Files.createTempFile(path, stringId + "-", ".dat"); ++ org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false); ++ Path path2 = path.resolve(stringId + ".dat"); ++ Path path3 = path.resolve(stringId + ".dat_old"); ++ Util.safeReplaceFile(path2, path1, path3); ++ } catch (Exception var7) { ++ LOGGER.warn("Failed to save player data for {}", playerName, var7); ++ } finally { ++ synchronized (PlayerDataStorage.this) { ++ savingLocks.remove(uniqueId); ++ } ++ } ++ }).ifPresent(future -> savingLocks.put(uniqueId, future)); ++ } + } -+ // Leaf end - Async player IO ++ ++ private void lockFor(java.util.UUID uniqueId, String playerName) { ++ java.util.concurrent.Future fut; ++ synchronized (this) { ++ fut = savingLocks.get(uniqueId); ++ } ++ if (fut == null) { ++ return; ++ } ++ while (true) { ++ try { ++ fut.get(10_000L, java.util.concurrent.TimeUnit.MILLISECONDS); ++ break; ++ } catch (InterruptedException ignored) { ++ } catch (java.util.concurrent.ExecutionException ++ | java.util.concurrent.TimeoutException exception) { ++ LOGGER.warn("Failed to save player data for {}", playerName, exception); ++ ++ String threadDump = ""; ++ var threadMXBean = java.lang.management.ManagementFactory.getThreadMXBean(); ++ for (var threadInfo : threadMXBean.dumpAllThreads(true, true)) { ++ if (threadInfo.getThreadName().equals("Leaf IO Thread")) { ++ threadDump = threadInfo.toString(); ++ break; ++ } ++ } ++ LOGGER.warn(threadDump); ++ fut.cancel(true); ++ break; ++ } finally { ++ savingLocks.remove(uniqueId); ++ } ++ } ++ } ++ // Leaf end - Async playerdata saving + private void backup(String name, String stringUuid, String suffix) { // CraftBukkit Path path = this.playerDir.toPath(); Path path1 = path.resolve(stringUuid + suffix); // CraftBukkit -@@ -60,7 +78,13 @@ public class PlayerDataStorage { +@@ -60,7 +124,13 @@ public class PlayerDataStorage { } } - private Optional load(String name, String stringUuid, String suffix) { // CraftBukkit -+ // Leaf start - Async player IO ++ // Leaf start - Async playerdata saving + private Optional load(String name, String stringUuid, String suffix) { + return load(name, stringUuid, suffix, java.util.UUID.fromString(stringUuid)); + } + private Optional load(String name, String stringUuid, String suffix, java.util.UUID playerUuid) { // CraftBukkit -+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.blockEntity(playerUuid, name); -+ // Leaf end - Async player IO ++ lockFor(playerUuid, name); ++ // Leaf end - Async playerdata saving File file = new File(this.playerDir, stringUuid + suffix); // CraftBukkit // Spigot start boolean usingWrongFile = false; -@@ -91,7 +115,7 @@ public class PlayerDataStorage { +@@ -91,7 +161,7 @@ public class PlayerDataStorage { public Optional load(Player player) { // CraftBukkit start - return this.load(player.getName().getString(), player.getStringUUID()).map((tag) -> { -+ return this.load(player.getName().getString(), player.getStringUUID(), player.getUUID()).map((tag) -> { // Leaf - Async player IO ++ return this.load(player.getName().getString(), player.getStringUUID(), player.getUUID()).map((tag) -> { // Leaf - Async playerdata saving if (player instanceof ServerPlayer serverPlayer) { CraftPlayer craftPlayer = serverPlayer.getBukkitEntity(); // Only update first played if it is older than the one we have -@@ -106,20 +130,25 @@ public class PlayerDataStorage { +@@ -106,20 +176,25 @@ public class PlayerDataStorage { }); } -+ // Leaf start - Async player IO ++ // Leaf start - Async playerdata saving public Optional load(String name, String uuid) { + return this.load(name, uuid, java.util.UUID.fromString(uuid)); + } @@ -311,7 +195,7 @@ index c44110b123ba5912af18faf0065e9ded780da9b7..2eae5ccb37b942b94964c28391b96989 return compoundTag; }); } -+ // Leaf end - Async player IO ++ // Leaf end - Async playerdata saving // CraftBukkit start public File getPlayerDir() { diff --git a/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch b/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch index 486b2c51..ea9f51d9 100644 --- a/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch +++ b/leaf-server/minecraft-patches/features/0110-SparklyPaper-Skip-dirty-stats-copy-when-requesting-p.patch @@ -6,10 +6,10 @@ Subject: [PATCH] SparklyPaper: Skip dirty stats copy when requesting player diff --git a/net/minecraft/stats/ServerStatsCounter.java b/net/minecraft/stats/ServerStatsCounter.java -index a682ae890850a5e62acd54029f2a4eb1e69d200a..cd2b99f82a56836a4f4e32cd6e02450f77fe9149 100644 +index b26dbe807e5cb0a42f6c06b933397902310e5616..ce89060bd01b253af7577fd0e6c03fc95f046b91 100644 --- a/net/minecraft/stats/ServerStatsCounter.java +++ b/net/minecraft/stats/ServerStatsCounter.java -@@ -126,11 +126,15 @@ public class ServerStatsCounter extends StatsCounter { +@@ -81,11 +81,15 @@ public class ServerStatsCounter extends StatsCounter { this.dirty.add(stat); } @@ -25,7 +25,7 @@ index a682ae890850a5e62acd54029f2a4eb1e69d200a..cd2b99f82a56836a4f4e32cd6e02450f public void parseLocal(DataFixer fixerUpper, String json) { try { -@@ -239,10 +243,12 @@ public class ServerStatsCounter extends StatsCounter { +@@ -194,10 +198,12 @@ public class ServerStatsCounter extends StatsCounter { public void sendStats(ServerPlayer player) { Object2IntMap> map = new Object2IntOpenHashMap<>(); diff --git a/leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch index 71743a1a..9f02149e 100644 --- a/leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0132-SparklyPaper-Parallel-world-ticking.patch @@ -458,7 +458,7 @@ index 63ff20f467c7508486a8f274442269b90faea108..15de8904a43c0ee1e6d55d511ebd84df } // CraftBukkit end diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java -index d5196b181a0a633cb04ce18b0471cda2dcaa8816..46433f0e6b37d31ec5468b3f4a5b2524d3cb29ed 100644 +index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..abe7ffd48766c48fab091947f34db436b3c883d0 100644 --- a/net/minecraft/server/PlayerAdvancements.java +++ b/net/minecraft/server/PlayerAdvancements.java @@ -53,8 +53,11 @@ public class PlayerAdvancements { @@ -483,7 +483,7 @@ index d5196b181a0a633cb04ce18b0471cda2dcaa8816..46433f0e6b37d31ec5468b3f4a5b2524 this.isFirstPacket = true; this.lastSelectedTab = null; this.tree = manager.tree(); -@@ -158,7 +162,7 @@ public class PlayerAdvancements { +@@ -151,7 +155,7 @@ public class PlayerAdvancements { if (org.galemc.gale.configuration.GaleGlobalConfiguration.get().logToConsole.ignoredAdvancements) LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", path, this.playerSavePath); // Gale - Purpur - do not log ignored advancements } else { this.startProgress(advancementHolder, progress); @@ -492,7 +492,7 @@ index d5196b181a0a633cb04ce18b0471cda2dcaa8816..46433f0e6b37d31ec5468b3f4a5b2524 this.markForVisibilityUpdate(advancementHolder); } }); -@@ -190,10 +194,12 @@ public class PlayerAdvancements { +@@ -183,10 +187,12 @@ public class PlayerAdvancements { return false; } // Paper end - Add PlayerAdvancementCriterionGrantEvent @@ -509,7 +509,7 @@ index d5196b181a0a633cb04ce18b0471cda2dcaa8816..46433f0e6b37d31ec5468b3f4a5b2524 // Paper start - Add Adventure message to PlayerAdvancementDoneEvent final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> { return java.util.Optional.ofNullable( -@@ -227,12 +233,14 @@ public class PlayerAdvancements { +@@ -220,12 +226,14 @@ public class PlayerAdvancements { AdvancementProgress orStartProgress = this.getOrStartProgress(advancement); boolean isDone = orStartProgress.isDone(); if (orStartProgress.revokeProgress(criterionKey)) { @@ -527,7 +527,7 @@ index d5196b181a0a633cb04ce18b0471cda2dcaa8816..46433f0e6b37d31ec5468b3f4a5b2524 this.markForVisibilityUpdate(advancement); } -@@ -278,7 +286,10 @@ public class PlayerAdvancements { +@@ -271,7 +279,10 @@ public class PlayerAdvancements { } public void flushDirty(ServerPlayer serverPlayer) { @@ -539,7 +539,7 @@ index d5196b181a0a633cb04ce18b0471cda2dcaa8816..46433f0e6b37d31ec5468b3f4a5b2524 Map map = new HashMap<>(); Set set = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically. Set set1 = new HashSet<>(); -@@ -286,16 +297,24 @@ public class PlayerAdvancements { +@@ -279,16 +290,24 @@ public class PlayerAdvancements { for (AdvancementNode advancementNode : this.rootsToUpdate) { this.updateTreeVisibility(advancementNode, set, set1); } @@ -568,7 +568,7 @@ index d5196b181a0a633cb04ce18b0471cda2dcaa8816..46433f0e6b37d31ec5468b3f4a5b2524 if (!map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) { serverPlayer.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, set, set1, map)); } -@@ -338,10 +357,13 @@ public class PlayerAdvancements { +@@ -331,10 +350,13 @@ public class PlayerAdvancements { AdvancementHolder advancementHolder = node.holder(); if (visible) { if (this.visible.add(advancementHolder)) { @@ -900,7 +900,7 @@ index 75fb49f1596f475278d12c8c7aea9ad4952b6056..de601491b7ecb83f1bb64a95989d6ed4 player.isRealPlayer = true; // Paper player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed GameProfile gameProfile = player.getGameProfile(); -@@ -933,6 +935,15 @@ public abstract class PlayerList { +@@ -891,6 +893,15 @@ public abstract class PlayerList { return this.respawn(player, keepInventory, reason, eventReason, null); } public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, org.bukkit.Location location) { @@ -916,7 +916,7 @@ index 75fb49f1596f475278d12c8c7aea9ad4952b6056..de601491b7ecb83f1bb64a95989d6ed4 player.stopRiding(); // CraftBukkit this.players.remove(player); this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot -@@ -944,6 +955,7 @@ public abstract class PlayerList { +@@ -902,6 +913,7 @@ public abstract class PlayerList { ServerPlayer serverPlayer = player; Level fromWorld = player.level(); player.wonGame = false; diff --git a/leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch index 1cd528ac..16dfdbb5 100644 --- a/leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0163-Async-switch-connection-state.patch @@ -110,10 +110,10 @@ index 2e9eb04c7c4342393c05339906c267bca9ff29b1..53b9daa909c2b89046d5af515e17afe0 try { PlayerList playerList = this.server.getPlayerList(); diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 3da6dad3dd0f4c5750609b382f47a6cd14f18e7a..c1e4ea2f28aba688b5b61e5bea2c295e9f219aba 100644 +index 114b25f933c6a1b011523581a5a02a5a2c1e827e..5907f1c75002be5e2ef1f9875974e665f964db7a 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -495,11 +495,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -494,11 +494,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); } diff --git a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch deleted file mode 100644 index 8794f7f0..00000000 --- a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: hayanesuru -Date: Fri, 6 Jun 2025 20:46:10 +0900 -Subject: [PATCH] optimize random tick - - -diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 2f927b422c2c4f2f65d822befe3cbfd9e3bb3708..d0fcfeaf093b718c8acd6e057176d569651299f2 100644 ---- a/net/minecraft/server/level/ServerChunkCache.java -+++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -693,6 +693,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - this.level.tickChunk(levelChunk, _int); - } - } -+ this.level.randomTickSystem.tick(this.level); // Leaf - random tick - - if (flagAndHasNaturalSpawn) { // Gale - MultiPaper - skip unnecessary mob spawning computations - this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); -diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index eb849c57992658005e0f514c6f7923f8ca43bebf..0bf765334f20fa5a999400076797d5b1f82c7469 100644 ---- a/net/minecraft/server/level/ServerLevel.java -+++ b/net/minecraft/server/level/ServerLevel.java -@@ -1128,6 +1128,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - - private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking - -+ public org.dreeam.leaf.world.RandomTickSystem randomTickSystem = new org.dreeam.leaf.world.RandomTickSystem(); // Leaf - public void tickChunk(LevelChunk chunk, int randomTickSpeed) { - final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting - ChunkPos pos = chunk.getPos(); -@@ -1177,7 +1178,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - } // Paper - Option to disable ice and snow - - if (randomTickSpeed > 0) { -- this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking -+ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.randomTickChunk(this.simpleRandom, chunk, randomTickSpeed); // Leaf - random tick -+ else this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking // Leaf - random tick - } - } - -diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 624a177695580510c0a49d4503dee72da7fd7114..f85d46c23824de177fe0c08b2ce6fbbb81c3535b 100644 ---- a/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/net/minecraft/world/level/chunk/LevelChunk.java -@@ -151,6 +151,52 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p - } - // Gale end - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively - -+ // Leaf start - random tick -+ private long leaf$randomTickChance; -+ private int leaf$countTickingBlocks; -+ private int leaf$countTickingSections; -+ -+ public final long leaf$randomTickChance() { -+ return leaf$randomTickChance; -+ } -+ public final void leaf$setRandomTickChance(long chance) { -+ leaf$randomTickChance = chance; -+ } -+ public final int leaf$countTickingBlocks() { -+ return leaf$countTickingBlocks; -+ } -+ public final int leaf$countTickingSections() { -+ return leaf$countTickingSections; -+ } -+ public final void leaf$recompute() { -+ int total1 = 0; -+ int total2 = 0; -+ for (LevelChunkSection section : sections) { -+ total1 += section.moonrise$getTickingBlockList().size(); -+ if (section.isRandomlyTickingBlocks()) { -+ total2++; -+ } -+ } -+ leaf$countTickingBlocks = total1; -+ leaf$countTickingSections = total2; -+ } -+ public final java.util.OptionalLong leaf$tickingPos(int idx) { -+ for (int i = 0; i < sections.length; i++) { -+ LevelChunkSection section = sections[i]; -+ var l = section.moonrise$getTickingBlockList(); -+ int size = l.size(); -+ if (idx < size) { -+ short loc = l.getRaw(idx); -+ int x = (loc & 15) | (chunkPos.x << 4); -+ int y = (loc >>> 8) | ((getMinSectionY() + i) << 4); -+ int z = ((loc >>> 4) & 15) | (chunkPos.z << 4); -+ return java.util.OptionalLong.of(BlockPos.asLong(x, y, z)); -+ } -+ idx -= size; -+ } -+ return java.util.OptionalLong.empty(); -+ } -+ // Leaf end - random tick - public LevelChunk(Level level, ChunkPos pos) { - this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null); - } diff --git a/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch b/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch index 5c6eb13d..8eae877f 100644 --- a/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch +++ b/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Async playerdata saving diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java -index f2d87c12dd19210ce7e2147fada5c10191008632..ad66046d31c24ba2a7d2b115f6c70adb95b9735b 100644 +index f2d87c12dd19210ce7e2147fada5c10191008632..14da4c731391f69fef104b6b3b7f2f977fe5ee95 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -207,7 +207,7 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa @@ -13,7 +13,7 @@ index f2d87c12dd19210ce7e2147fada5c10191008632..ad66046d31c24ba2a7d2b115f6c70adb private CompoundTag getData() { - return this.storage.load(this.profile.getName(), this.profile.getId().toString()).orElse(null); -+ return this.storage.load(this.profile.getName(), this.profile.getId().toString(), this.profile.getId()).orElse(null); // Leaf - Async player IO ++ return this.storage.load(this.profile.getName(), this.profile.getId().toString(), this.profile.getId()).orElse(null); // Leaf - Async playerdata saving } private CompoundTag getBukkitData() { @@ -31,7 +31,7 @@ index f2d87c12dd19210ce7e2147fada5c10191008632..ad66046d31c24ba2a7d2b115f6c70adb - } catch (java.io.IOException e) { - e.printStackTrace(); - } -+ server.console.playerDataStorage.save(this.getName(), this.getUniqueId(), this.getUniqueId().toString(), compoundTag); // Leaf - Async player IO ++ server.console.playerDataStorage.save(this.getName(), this.getUniqueId(), this.getUniqueId().toString(), compoundTag); // Leaf - Async playerdata saving } // Purpur end - OfflinePlayer API } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java new file mode 100644 index 00000000..3df32778 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java @@ -0,0 +1,37 @@ +package org.dreeam.leaf.async; + +import net.minecraft.Util; +import org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave; + +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class AsyncPlayerDataSaving { + + public static final ExecutorService IO_POOL = new ThreadPoolExecutor( + 1, 1, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + new com.google.common.util.concurrent.ThreadFactoryBuilder() + .setPriority(Thread.NORM_PRIORITY - 2) + .setNameFormat("Leaf IO Thread") + .setUncaughtExceptionHandler(Util::onThreadException) + .build(), + new ThreadPoolExecutor.DiscardPolicy() + ); + + private AsyncPlayerDataSaving() { + } + + public static Optional> submit(Runnable runnable) { + if (!AsyncPlayerDataSave.enabled) { + runnable.run(); + return Optional.empty(); + } else { + return Optional.of(IO_POOL.submit(runnable)); + } + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java index df2d6ef6..ee6d490d 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java @@ -1,7 +1,6 @@ package org.dreeam.leaf.async; import net.minecraft.server.MinecraftServer; -import org.dreeam.leaf.async.storage.AsyncPlayerDataSaving; import org.dreeam.leaf.async.tracker.MultithreadedTracker; public class ShutdownExecutors { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/storage/AsyncPlayerDataSaving.java b/leaf-server/src/main/java/org/dreeam/leaf/async/storage/AsyncPlayerDataSaving.java deleted file mode 100644 index 7e5060d7..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/storage/AsyncPlayerDataSaving.java +++ /dev/null @@ -1,307 +0,0 @@ -package org.dreeam.leaf.async.storage; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.Util; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.*; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.time.format.SignStyle; -import java.time.temporal.ChronoField; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.*; - -public class AsyncPlayerDataSaving { - public static final AsyncPlayerDataSaving INSTANCE = new AsyncPlayerDataSaving(); - private static final Logger LOGGER = LogManager.getLogger("Leaf Async Player IO"); - public static ExecutorService IO_POOL = null; - private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() - .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) - .appendValue(ChronoField.MONTH_OF_YEAR, 2) - .appendValue(ChronoField.DAY_OF_MONTH, 2) - .appendValue(ChronoField.HOUR_OF_DAY, 2) - .appendValue(ChronoField.MINUTE_OF_HOUR, 2) - .appendValue(ChronoField.SECOND_OF_MINUTE, 2) - .appendValue(ChronoField.NANO_OF_SECOND, 9) - .toFormatter(); - - private record SaveTask(Ty ty, Callable callable, String name, UUID uuid) implements Runnable { - @Override - public void run() { - try { - callable.call(); - } catch (Exception e) { - LOGGER.error("Failed to save player {} data for {}", ty, name, e); - } finally { - switch (ty) { - case ENTITY -> INSTANCE.entityFut.remove(uuid); - case STATS -> INSTANCE.statsFut.remove(uuid); - case ADVANCEMENTS -> INSTANCE.advancementsFut.remove(uuid); - } - } - } - } - - private enum Ty { - ENTITY, - STATS, - ADVANCEMENTS, - } - - // use same lock - private final Object2ObjectMap> entityFut = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(), this); - private final Object2ObjectMap> statsFut = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(), this); - private final Object2ObjectMap> advancementsFut = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(), this); - - private final Object2ObjectMap> levelDatFut = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(), this); - - private AsyncPlayerDataSaving() { - } - - public static void init() { - if (AsyncPlayerDataSaving.IO_POOL != null) { - throw new IllegalStateException("Already initialized"); - } - AsyncPlayerDataSaving.IO_POOL = new ThreadPoolExecutor( - 1, 1, 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - new ThreadFactoryBuilder() - .setPriority(Thread.NORM_PRIORITY - 2) - .setNameFormat("Leaf Async Player IO Thread") - .setUncaughtExceptionHandler(Util::onThreadException) - .build(), - new ThreadPoolExecutor.DiscardPolicy() - ); - } - - - public void saveLevelData(Path path, @Nullable Runnable runnable) { - if (!AsyncPlayerDataSave.enabled) { - if (runnable != null) { - runnable.run(); - } - return; - } - var fut = levelDatFut.get(path); - if (fut != null) { - try { - while (true) { - try { - fut.get(); - break; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } catch (ExecutionException e) { - LOGGER.error("Failed to save level.dat for {}", path, e); - } finally { - levelDatFut.remove(path); - } - } - if (runnable != null) { - levelDatFut.put(path, IO_POOL.submit(() -> { - try { - runnable.run(); - } catch (Exception e) { - LOGGER.error(e); - } finally { - levelDatFut.remove(path); - } - })); - } - } - - public boolean isSaving(UUID uuid) { - var entity = entityFut.get(uuid); - var advancements = advancementsFut.get(uuid); - var stats = statsFut.get(uuid); - return entity != null || advancements != null || stats != null; - } - - public void submitStats(UUID uuid, String playerName, Callable callable) { - submit(Ty.STATS, uuid, playerName, callable); - } - - public void submitEntity(UUID uuid, String playerName, Callable callable) { - submit(Ty.ENTITY, uuid, playerName, callable); - } - - public void submitAdvancements(UUID uuid, String playerName, Callable callable) { - submit(Ty.ADVANCEMENTS, uuid, playerName, callable); - } - - private void submit(Ty type, UUID uuid, String playerName, Callable callable) { - if (!AsyncPlayerDataSave.enabled) { - try { - callable.call(); - } catch (Exception e) { - LOGGER.error("Failed to save player {} data for {}", type, playerName, e); - } - return; - } - block(type, uuid, playerName); - var fut = IO_POOL.submit(new SaveTask(type, callable, playerName, uuid)); - switch (type) { - case ENTITY -> entityFut.put(uuid, fut); - case ADVANCEMENTS -> advancementsFut.put(uuid, fut); - case STATS -> statsFut.put(uuid, fut); - } - } - - public void blockStats(UUID uuid, String playerName) { - block(Ty.STATS, uuid, playerName); - } - - public void blockEntity(UUID uuid, String playerName) { - block(Ty.ENTITY, uuid, playerName); - } - - public void blockAdvancements(UUID uuid, String playerName) { - block(Ty.ADVANCEMENTS, uuid, playerName); - } - - private void block(Ty type, UUID uuid, String playerName) { - if (!AsyncPlayerDataSave.enabled) { - return; - } - - Future fut = switch (type) { - case ENTITY -> entityFut.get(uuid); - case ADVANCEMENTS -> advancementsFut.get(uuid); - case STATS -> statsFut.get(uuid); - }; - if (fut == null) { - return; - } - try { - while (true) { - try { - fut.get(); - break; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } catch (ExecutionException exception) { - LOGGER.warn("Failed to save player {} data for {}", type, playerName, exception); - fut.cancel(true); - } finally { - switch (type) { - case ENTITY -> entityFut.remove(uuid); - case ADVANCEMENTS -> advancementsFut.remove(uuid); - case STATS -> statsFut.remove(uuid); - } - } - } - - private static final StandardCopyOption[] ATOMIC_MOVE = new StandardCopyOption[]{StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING}; - private static final StandardCopyOption[] NO_ATOMIC_MOVE = new StandardCopyOption[]{StandardCopyOption.REPLACE_EXISTING}; - - public static void safeReplace(Path current, String content) { - byte[] bytes = content.getBytes(StandardCharsets.UTF_8); - safeReplace(current, bytes, 0, bytes.length); - } - - @SuppressWarnings("unused") - public static void safeReplaceBackup(Path current, Path old, String content) { - byte[] bytes = content.getBytes(StandardCharsets.UTF_8); - safeReplaceBackup(current, old, bytes, 0, bytes.length); - } - - public static void safeReplace(Path current, byte[] bytes, int offset, int length) { - File latest = writeTempFile(current, bytes, offset, length); - Objects.requireNonNull(latest); - for (int i = 1; i <= 10; i++) { - try { - try { - Files.move(latest.toPath(), current, ATOMIC_MOVE); - } catch (AtomicMoveNotSupportedException e) { - Files.move(latest.toPath(), current, NO_ATOMIC_MOVE); - } - break; - } catch (IOException e) { - LOGGER.error("Failed move {} to {} retries ({} / 10)", latest, current, i, e); - } - } - } - - public static void safeReplaceBackup(Path current, Path backup, byte[] bytes, int offset, int length) { - File latest = writeTempFile(current, bytes, offset, length); - Objects.requireNonNull(latest); - for (int i = 1; i <= 10; i++) { - try { - try { - Files.move(current, backup, ATOMIC_MOVE); - } catch (AtomicMoveNotSupportedException e) { - Files.move(current, backup, NO_ATOMIC_MOVE); - } - break; - } catch (IOException e) { - LOGGER.error("Failed move {} to {} retries ({} / 10)", current, backup, i, e); - } - } - for (int i = 1; i <= 10; i++) { - try { - try { - Files.move(latest.toPath(), current, ATOMIC_MOVE); - } catch (AtomicMoveNotSupportedException e) { - Files.move(latest.toPath(), current, NO_ATOMIC_MOVE); - } - break; - } catch (IOException e) { - LOGGER.error("Failed move {} to {} retries ({} / 10)", latest, current, i, e); - } - } - } - - private static File writeTempFile(Path current, byte[] bytes, int offset, int length) { - Path dir = current.getParent(); - for (int i = 1; i <= 10; i++) { - File temp = null; - try { - if (!dir.toFile().isDirectory()) { - Files.createDirectories(dir); - } - temp = tempFileDateTime(current).toFile(); - if (temp.exists()) { - throw new FileAlreadyExistsException(temp.getPath()); - } - // sync content and metadata to device - try (RandomAccessFile stream = new RandomAccessFile(temp, "rws")) { - stream.write(bytes, offset, length); - } - return temp; - } catch (IOException e) { - LOGGER.error("Failed write {} retries ({} / 10)", temp == null ? current : temp, i, e); - } - } - return null; - } - - private static Path tempFileDateTime(Path path) { - String now = LocalDateTime.now().format(FORMATTER); - String last = path.getFileName().toString(); - int dot = last.lastIndexOf('.'); - - String base = (dot == -1) ? last : last.substring(0, dot); - String ext = (dot == -1) ? "" : last.substring(dot); - - String newExt = switch (ext) { - case ".json", ".dat" -> ext; - default -> ".temp"; - }; - return path.resolveSibling(base + "-" + now + newExt); - } -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java index e8ac71f7..6d555ce0 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java @@ -1,9 +1,7 @@ package org.dreeam.leaf.config.modules.async; -import org.dreeam.leaf.async.storage.AsyncPlayerDataSaving; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; -import org.dreeam.leaf.config.annotations.Experimental; public class AsyncPlayerDataSave extends ConfigModules { @@ -11,9 +9,7 @@ public class AsyncPlayerDataSave extends ConfigModules { return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-playerdata-save"; } - @Experimental public static boolean enabled = false; - private static boolean asyncPlayerDataSaveInitialized; @Override public void onLoaded() { @@ -22,13 +18,6 @@ public class AsyncPlayerDataSave extends ConfigModules { """ 异步保存玩家数据."""); - if (asyncPlayerDataSaveInitialized) { - config.getConfigSection(getBasePath()); - return; - } - asyncPlayerDataSaveInitialized = true; - enabled = config.getBoolean(getBasePath() + ".enabled", enabled); - if (enabled) AsyncPlayerDataSaving.init(); } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java deleted file mode 100644 index 525bcac7..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.dreeam.leaf.config.modules.opt; - -import org.dreeam.leaf.config.ConfigModules; -import org.dreeam.leaf.config.EnumConfigCategory; -import org.dreeam.leaf.config.annotations.Experimental; - -public class OptimizeRandomTick extends ConfigModules { - - public String getBasePath() { - return EnumConfigCategory.PERF.getBaseKeyName() + ".optimise-random-tick"; - } - - @Experimental - public static boolean enabled = false; - - @Override - public void onLoaded() { - enabled = config.getBoolean(getBasePath(), enabled); - } -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java deleted file mode 100644 index afff1346..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.dreeam.leaf.world; - -import it.unimi.dsi.fastutil.longs.LongArrayList; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.levelgen.BitRandomSource; -import net.minecraft.world.level.levelgen.PositionalRandomFactory; -import net.minecraft.world.level.levelgen.RandomSupport; -import net.minecraft.world.level.material.FluidState; -import org.jetbrains.annotations.NotNull; - -import java.util.OptionalLong; - -public final class RandomTickSystem { - private final LongArrayList tickPos = new LongArrayList(); - private static final long SCALE = 0x100000L; - private static final long CHUNK_BLOCKS = 4096L; - private static final int MASK = 0xfffff; - private static final int MASK_ONE_FOURTH = 0x300000; - - public void tick(ServerLevel world) { - var simpleRandom = world.simpleRandom; - int j = tickPos.size(); - for (int i = 0; i < j; i++) { - tickBlock(world, tickPos.getLong(i), simpleRandom); - } - tickPos.clear(); - } - - private static void tickBlock(ServerLevel world, long packed, RandomSource tickRand) { - final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); - BlockPos pos = BlockPos.of(packed); - LevelChunk chunk = world.chunkSource.getChunkAtIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); - if (chunk == null) { - return; - } - BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); - state.randomTick(world, pos, tickRand); - if (doubleTickFluids) { - final FluidState fluidState = state.getFluidState(); - if (fluidState.isRandomlyTicking()) { - fluidState.randomTick(world, pos, tickRand); - } - } - } - - private long recompute(LevelChunk chunk, long tickSpeed) { - chunk.leaf$recompute(); - long tickingCount = chunk.leaf$countTickingBlocks(); - long numSections = chunk.leaf$countTickingSections(); - if (tickingCount == 0L || numSections == 0L) { - chunk.leaf$setRandomTickChance(0L); - return 0L; - } - long chance = (tickSpeed * tickingCount * SCALE) / CHUNK_BLOCKS; - chunk.leaf$setRandomTickChance(chance); - return chance; - } - - public void randomTickChunk( - RandomSource randomSource, - LevelChunk chunk, - long tickSpeed - ) { - int a = randomSource.nextInt(); - if ((a & MASK_ONE_FOURTH) != 0) { - return; - } - tickSpeed = tickSpeed * 4; - - long chance = chunk.leaf$randomTickChance(); - if (chance == 0L && (chance = recompute(chunk, tickSpeed)) == 0L) { - return; - } - if (chance >= (long) (a & MASK) || (chance = recompute(chunk, tickSpeed)) == 0L) { - return; - } - int tickingCount = chunk.leaf$countTickingBlocks(); - OptionalLong pos = chunk.leaf$tickingPos(randomSource.nextInt(tickingCount)); - if (pos.isPresent()) { - tickPos.add(pos.getAsLong()); - } - - if (chance > SCALE) { - chance -= SCALE; - long last = randomSource.nextInt() & MASK; - while (last < chance) { - pos = chunk.leaf$tickingPos(randomSource.nextInt(tickingCount)); - if (pos.isPresent()) { - tickPos.add(pos.getAsLong()); - } - chance -= SCALE; - } - } - } -} diff --git a/leaf-server/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java b/leaf-server/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java index 6dfb82e4..10494446 100644 --- a/leaf-server/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java +++ b/leaf-server/src/main/java/org/leavesmc/leaves/bot/BotStatsCounter.java @@ -14,7 +14,7 @@ public class BotStatsCounter extends ServerStatsCounter { private static final File UNKOWN_FILE = new File("BOT_STATS_REMOVE_THIS"); public BotStatsCounter(MinecraftServer server) { - super(server, UNKOWN_FILE, "", net.minecraft.Util.NIL_UUID); // Leaf + super(server, UNKOWN_FILE); } @Override From 5bcf0f94004b7814357e6c9ab778d4787f33ef46 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 9 Jun 2025 03:26:52 +0800 Subject: [PATCH 51/57] [ci skip] Update README.md & Gradle --- .gitignore | 2 +- README.md | 39 +++++++++++------------ gradle.properties | 3 -- gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 6 ++-- gradlew.bat | 4 +-- settings.gradle.kts | 2 +- 8 files changed, 27 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 62b6c22a..44a9d442 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ build # Leaf build-data/dev-imports.txt -patches/todo +leaf-archived-patches/todo run leaf-api/build.gradle.kts diff --git a/README.md b/README.md index 04813f4a..72145c99 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,30 @@ Leaf

-[![Github Releases](https://img.shields.io/badge/Download-Releases-blue?&style=for-the-badge&colorA=19201a&colorB=298046)](https://github.com/Winds-Studio/Leaf/releases)⠀ +[![Download](https://img.shields.io/badge/releases-blue?label=download&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/download)⠀ [![Github Actions Build](https://img.shields.io/github/actions/workflow/status/Winds-Studio/Leaf/build-1214.yml?&style=for-the-badge&colorA=19201a&colorB=298046)](https://github.com/Winds-Studio/Leaf/actions)⠀ [![Discord](https://img.shields.io/discord/1145991395388162119?label=discord&style=for-the-badge&colorA=19201a&colorB=298046)](https://discord.gg/gfgAwdSEuM) -[![Docs](https://img.shields.io/badge/Docs-leafmc.one/docs/-blue?label=docs&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/docs/) +[![Docs](https://img.shields.io/badge/leafmc.one/docs/-blue?label=docs&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/docs) **Leaf** is a [Paper](https://papermc.io/) fork designed to be customizable and high-performance, built on top of [Gale](https://github.com/Dreeam-qwq/Gale) with optimizations and fixes from other forks.
> [!WARNING] -> Leaf is a performance-oriented fork. Make sure to take backups **before** switching to it. Everyone is welcome to contribute by optimizing or reporting issues. +> Leaf is a performance-oriented fork. Make sure to take backups **before** switching to it. Everyone is welcome to contribute optimizations or report issues to help us improve. ## 🍃 Features - - **Based on [Gale](https://github.com/Dreeam-qwq/Gale)** for better performance - - **Async** pathfinding, mob spawning and entity tracker - - **Various optimizations** blending from [other forks](https://github.com/Winds-Studio/Leaf#-credits) - - **Fully compatible** with Bukkit, Spigot and Paper plugins - - **Latest dependencies**, keeping all dependencies up-to-date - - **Allows all characters in usernames**, including Chinese and other characters - - **Fixes** some Minecraft bugs - - **Configurable UseItem distance** for anarchy servers - - **Mod Protocols** support - - **More customized** relying on features of [Purpur](https://github.com/PurpurMC/Purpur) - - Support for **Linear region file format** - - **Maintenance friendly**, integrating with [Sentry](https://sentry.io/welcome/) of [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) to easily track all errors coming from your server in extreme detail - - And more... +- **Based on [Gale](https://github.com/Dreeam-qwq/Gale)** for better performance +- **Async** pathfinding, mob spawning and entity tracker +- **Various optimizations** blending from [other forks](https://github.com/Winds-Studio/Leaf#-credits) and our own +- **Fully compatible** with Spigot and Paper plugins +- **Latest dependencies**, keeping all dependencies up-to-date +- **Allows all characters in usernames**, including Chinese and other characters +- **Fixes** some Minecraft bugs +- **Mod Protocols** support +- **More customized** relying on features of [Purpur](https://github.com/PurpurMC/Purpur) +- **Linear region file format**, to save disk space +- **Maintenance friendly**, integrating with [Sentry](https://sentry.io/welcome/) of [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) to easily track all errors coming from your server in extreme detail +- And more... ## 📈 bStats [![bStats Graph Data](https://bstats.org/signatures/server-implementation/Leaf.svg)](https://bstats.org/plugin/server-implementation/Leaf) @@ -38,12 +37,12 @@ If you love our work, feel free to donate via our [Open Collective](https://opencollective.com/Winds-Studio) or [Dreeam's AFDIAN](https://afdian.com/a/Dreeam) :) ## 📥 Download -You can find the latest successful build in [GitHub Action](https://github.com/Winds-Studio/Leaf/actions) or [Releases](https://github.com/Winds-Studio/Leaf/releases) +Download Leaf from our [website](https://www.leafmc.one/download) or get latest build in [GitHub Action](https://github.com/Winds-Studio/Leaf/actions) **Please note Java >= 21 is required.** ## 📄 Documentation -Documentation on how to use/configure Leaf: [www.leafmc.one/docs](https://www.leafmc.one/docs) +Documentation about how to use/configure Leaf: [www.leafmc.one/docs](https://www.leafmc.one/docs) ## 📦 Building Building a Paperclip JAR for distribution: @@ -90,7 +89,7 @@ Paperweight files are licensed under [MIT](licenses/MIT.txt). Patches are licensed under [MIT](licenses/MIT.txt), unless indicated differently in their header. Binaries are licensed under [GPL-3.0](licenses/GPL-3.0.txt). -Also see [PaperMC/Paper](https://github.com/PaperMC/Paper) and [PaperMC/Paperweight](https://github.com/PaperMC/paperweight) for the licenses of some materials used by this project. +Also see [PaperMC/Paper](https://github.com/PaperMC/Paper) and [PaperMC/paperweight](https://github.com/PaperMC/paperweight) for the licenses of some materials used by this project. ## 📜 Credits Thanks to these projects below. Leaf includes some patches taken from them.
@@ -129,7 +128,7 @@ cloud of swordsman | 剑客云 If you want to find a cheaper, high performance, stable, lower latency host, then cloud of swordsman is a good choice! Registers and purchases in [here](https://cloud.swordsman.com.cn/?i8ab42c). -如果你想找一个低价高性能, 低延迟的云服务商,剑客云是个不错的选择! 你可以在[这里](https://cloud.swordsman.com.cn/?i8ab42c)注册. +如果你想找一个低价高性能、低延迟的云服务商,剑客云是个不错的选择!你可以在 [这里](https://cloud.swordsman.com.cn/?i8ab42c) 注册。 --- ![YourKit](https://www.yourkit.com/images/yklogo.png) diff --git a/gradle.properties b/gradle.properties index f39575df..040eb13b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,6 +8,3 @@ org.gradle.configuration-cache=true org.gradle.caching=true org.gradle.parallel=true org.gradle.vfs.watch=false -leaf.patcher.repo-cache=enabled -leaf.patcher.patch-cache=enabled -leaf.patcher.fast-update=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 34943 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

Gn4F+B~IivciU9^t0j-Mgao3 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 DDw4oP diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f853b1..ff23a68d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f3b75f3b..23d15a93 100755 --- a/gradlew +++ b/gradlew @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -205,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a218..db3a6ac2 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle.kts b/settings.gradle.kts index 124b899f..fd2d4a01 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,7 @@ pluginManagement { } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } rootProject.name = "leaf" From 08e1802690df4c54610dba7a308b640ba88fdd33 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 9 Jun 2025 05:16:07 +0800 Subject: [PATCH 52/57] [ci skip] Add README CN --- README.md | 2 + public/readme/README_CN.md | 141 +++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 public/readme/README_CN.md diff --git a/README.md b/README.md index 72145c99..2db202d6 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ > [!WARNING] > Leaf is a performance-oriented fork. Make sure to take backups **before** switching to it. Everyone is welcome to contribute optimizations or report issues to help us improve. +**English** | [中文](public/readme/README_CN.md) + ## 🍃 Features - **Based on [Gale](https://github.com/Dreeam-qwq/Gale)** for better performance - **Async** pathfinding, mob spawning and entity tracker diff --git a/public/readme/README_CN.md b/public/readme/README_CN.md new file mode 100644 index 00000000..bd676622 --- /dev/null +++ b/public/readme/README_CN.md @@ -0,0 +1,141 @@ +Leaf +

+ +[![下载](https://img.shields.io/badge/releases-blue?label=%e4%b8%8b%e8%bd%bd&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/zh/download)⠀ +[![Github Actions 构建](https://img.shields.io/github/actions/workflow/status/Winds-Studio/Leaf/build-1214.yml?label=%e6%9e%84%e5%bb%ba&style=for-the-badge&colorA=19201a&colorB=298046)](https://github.com/Winds-Studio/Leaf/actions)⠀ +![QQ](https://img.shields.io/badge/619278377-blue?label=QQ%e7%be%a4&style=for-the-badge&colorA=19201a&colorB=298046) +[![文档](https://img.shields.io/badge/leafmc.one/zh/docs-blue?label=%e6%96%87%e6%a1%a3&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/zh/docs) + +**Leaf** 是一个基于 [Paper](https://papermc.io/) 的分支,专为高自定义和高性能而设计,基于 [Gale](https://github.com/Dreeam-qwq/Gale) 之上,并融合了其他核心的优化和修复。 +
+ +> [!WARNING] +> Leaf 是一个面向性能的分支。在迁移到 Leaf 之前,请务必**提前备份**。欢迎任何人贡献优化或报告问题来帮助我们改进。 + +[English](../../README.md) | **中文** + +## 🍃 特点 +- **基于 [Gale](https://github.com/Dreeam-qwq/Gale)**,以获得更好的性能 +- **异步**寻路、生物生成和实体追踪 +- **大量优化**融合自 [其他核心](https://github.com/Winds-Studio/Leaf#-致谢) 的补丁, 和我们自己的 +- **完全兼容** Spigot 和 Paper 插件 +- **最新依赖**,保持所有依赖项为最新版本 +- **允许用户名使用所有字符**,包括中文和其他字符 +- **修复**一些 Minecraft 的 bug +- **模组协议**支持 +- **更多自定义配置项**,源自 [Purpur](https://github.com/PurpurMC/Purpur) 的特性 +- **线性区域文件格式**,节省磁盘空间 +- **运维友好**,集成 [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) 的 [Sentry](https://sentry.io/welcome/),轻松详细追踪服务器的所有报错 +- 以及更多... + +## 📈 bStats 统计 +[![bStats Graph Data](https://bstats.org/signatures/server-implementation/Leaf.svg)](https://bstats.org/plugin/server-implementation/Leaf) + +## 📫 联系方式 +- Discord: [`https://discord.com/invite/gfgAwdSEuM`](https://discord.com/invite/gfgAwdSEuM) +- QQ社区群: `619278377` + +## 📫 赞助 +如果您喜欢我们的工作,欢迎通过我们的 [Open Collective](https://opencollective.com/Winds-Studio) 或 [Dreeam 的爱发电](https://afdian.com/a/Dreeam) 进行赞助 :) + +## 📥 下载 +从我们的 [官网](https://www.leafmc.one/zh/download) 下载 Leaf,或在 [GitHub Action](https://github.com/Winds-Studio/Leaf/actions) 获取最新构建版本 + +**请注意需要 Java 21 以上。** + +## 📄 文档 +关于如何使用/配置 Leaf 的文档:[www.leafmc.one/zh/docs](https://www.leafmc.one/zh/docs) + +## 📦 构建 +构建用于分发的 Paperclip JAR: +```bash +./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar +``` + +## 🧪 API + +### Maven +```xml + + leafmc + https://maven.nostal.ink/repository/maven-snapshots/ + +``` +```xml + + cn.dreeam.leaf + leaf-api + 1.21.4-R0.1-SNAPSHOT + provided + +``` +### Gradle +```kotlin +repositories { + maven { + url = uri("https://maven.nostal.ink/repository/maven-snapshots/") + } +} + +dependencies { + compileOnly("cn.dreeam.leaf:leaf-api:1.21.4-R0.1-SNAPSHOT") +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} +``` + +## ⚖️ 许可证 +Paperweight 文件基于 [MIT](licenses/MIT.txt) 许可证。 +补丁基于 [MIT](licenses/MIT.txt) 许可证,除非在补丁顶部注释中另有说明。 +二进制文件基于 [GPL-3.0](licenses/GPL-3.0.txt) 许可证。 + +另请参阅 [PaperMC/Paper](https://github.com/PaperMC/Paper) 和 [PaperMC/paperweight](https://github.com/PaperMC/paperweight) 了解本项目使用的一些材料的许可证。 + +## 📜 致谢 +感谢以下项目。Leaf 包含了一些取自这些项目的补丁。
+如果没有这些优秀的项目,Leaf 就不会变得如此出色。 + +- [Gale](https://github.com/Dreeam-qwq/Gale) ([原始仓库](https://github.com/GaleMC/Gale)) +- [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) +- [Purpur](https://github.com/PurpurMC/Purpur) +-
+ 🍴 展开查看 Leaf 采用补丁的核心 +

+ • KeYi (R.I.P.) + (备份仓库)
+ • Mirai
+ • Petal
+ • Carpet Fixes
+ • Akarin
+ • Slice
+ • Parchment
+ • Leaves
+ • Kaiiju
+ • Plazma
+ • SparklyPaper
+ • Polpot
+ • Matter
+ • Luminol
+ • Nitori
+ • Moonrise (在 1.21.1 期间)
+ • Sakura
+

+
+ +## 🔥 特别感谢 +Jianke Cloud Host +剑客云 | cloud of swordsman + +如果你想找一个低价高性能、低延迟的云服务商,剑客云是个不错的选择!你可以在 [这里](https://cloud.swordsman.com.cn/?i8ab42c) 注册。 + +If you want to find a cheaper, high performance, stable, lower latency host, then cloud of swordsman is a good choice! Registers and purchases in [here](https://cloud.swordsman.com.cn/?i8ab42c). + +--- +![YourKit](https://www.yourkit.com/images/yklogo.png) + +YourKit 通过创新和智能的工具支持开源项目,用于监控和分析 Java 和 .NET 应用程序。 +YourKit 是 [YourKit Java Profiler](https://www.yourkit.com/java/profiler/)、 +[YourKit .NET Profiler](https://www.yourkit.com/dotnet-profiler/) 和 +[YourKit YouMonitor](https://www.yourkit.com/youmonitor/) 的创造者。 \ No newline at end of file From 48c39c35703d77d9564115f9edbe8c3ae24fd3c6 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 9 Jun 2025 07:39:27 +0800 Subject: [PATCH 53/57] Cache direction values micro opt --- README.md | 2 +- build-data/leaf.at | 1 + .../features/0188-Paw-optimization.patch | 308 ++++++++++++++++++ public/readme/README_CN.md | 2 +- 4 files changed, 311 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2db202d6..08609ec3 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ **Leaf** is a [Paper](https://papermc.io/) fork designed to be customizable and high-performance, built on top of [Gale](https://github.com/Dreeam-qwq/Gale) with optimizations and fixes from other forks. -> [!WARNING] +> [!WARNING] > Leaf is a performance-oriented fork. Make sure to take backups **before** switching to it. Everyone is welcome to contribute optimizations or report issues to help us improve. **English** | [中文](public/readme/README_CN.md) diff --git a/build-data/leaf.at b/build-data/leaf.at index 21a98f49..f5530961 100644 --- a/build-data/leaf.at +++ b/build-data/leaf.at @@ -4,6 +4,7 @@ protected net.minecraft.world.entity.Entity dimensions protected net.minecraft.world.entity.ai.goal.RemoveBlockGoal blockToRemove protected net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal getTargetConditions()Lnet/minecraft/world/entity/ai/targeting/TargetingConditions; protected-f net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase$Cache largeCollisionShape +public net.minecraft.core.Direction VALUES public net.minecraft.server.level.ServerChunkCache fullChunks public net.minecraft.server.level.ServerEntity sendDirtyEntityData()V public net.minecraft.util.Mth SIN diff --git a/leaf-server/minecraft-patches/features/0188-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0188-Paw-optimization.patch index c584cf3f..5c1e1b4b 100644 --- a/leaf-server/minecraft-patches/features/0188-Paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0188-Paw-optimization.patch @@ -7,6 +7,7 @@ Some random optimizations - Remove Paper's dead code - Only set shuffle random seed if is really used +- Cache direction values to skip copy - Secret patches (WIP) diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java @@ -234,6 +235,248 @@ index 80baa2dff5c1034a72271fc727fdb2acc1b69488..9f581d5bdc3f658694bbd8c80abbce4e int floor = Mth.floor(x); int floor1 = Mth.floor(y); int floor2 = Mth.floor(z); +diff --git a/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java b/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java +index e804ca8d6c9c9b8d9f982a970cc3edddf5c03aa1..0a4ca9f0dba64c5539b99af5d95292159f51b6ba 100644 +--- a/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java ++++ b/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java +@@ -43,7 +43,7 @@ public abstract class BaseCoralPlantTypeBlock extends Block implements SimpleWat + if (state.getValue(WATERLOGGED)) { + return true; + } else { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (level.getFluidState(pos.relative(direction)).is(FluidTags.WATER)) { + return true; + } +diff --git a/net/minecraft/world/level/block/BaseFireBlock.java b/net/minecraft/world/level/block/BaseFireBlock.java +index 45df9f008b74dd0d6d790c91102e9afe1da45633..baee320ad6f1918cc9009db9f65c28e33a544e19 100644 +--- a/net/minecraft/world/level/block/BaseFireBlock.java ++++ b/net/minecraft/world/level/block/BaseFireBlock.java +@@ -204,7 +204,7 @@ public abstract class BaseFireBlock extends Block { + BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); + boolean flag = false; + +- for (Direction direction1 : Direction.values()) { ++ for (Direction direction1 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (level.getBlockState(mutableBlockPos.set(pos).move(direction1)).is(Blocks.OBSIDIAN)) { + flag = true; + break; +diff --git a/net/minecraft/world/level/block/FireBlock.java b/net/minecraft/world/level/block/FireBlock.java +index 7340c664fdcf991a2549c8f07f6ab093bbe6e4e8..90f1a971888a76ab3f8522d86a9f9f240ef76a53 100644 +--- a/net/minecraft/world/level/block/FireBlock.java ++++ b/net/minecraft/world/level/block/FireBlock.java +@@ -159,7 +159,7 @@ public class FireBlock extends BaseFireBlock { + if (!this.canBurn(blockState) && !blockState.isFaceSturdy(level, blockPos, Direction.UP)) { + BlockState blockState1 = this.defaultBlockState(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + BooleanProperty booleanProperty = PROPERTY_BY_DIRECTION.get(direction); + if (booleanProperty != null) { + blockState1 = blockState1.setValue(booleanProperty, Boolean.valueOf(this.canBurn(level.getBlockState(pos.relative(direction))))); +@@ -331,7 +331,7 @@ public class FireBlock extends BaseFireBlock { + } + + private boolean isValidFireLocation(BlockGetter level, BlockPos pos) { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (this.canBurn(level.getBlockState(pos.relative(direction)))) { + return true; + } +@@ -346,7 +346,7 @@ public class FireBlock extends BaseFireBlock { + } else { + int i = 0; + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + BlockState blockState = level.getBlockState(pos.relative(direction)); + i = Math.max(this.getIgniteOdds(blockState), i); + } +diff --git a/net/minecraft/world/level/block/FrostedIceBlock.java b/net/minecraft/world/level/block/FrostedIceBlock.java +index 8eb24bf0477c8d0cf4bfa7f122bbcd20aa2a5d5b..3035455cd9d8c238e33d493935a8498687d6a303 100644 +--- a/net/minecraft/world/level/block/FrostedIceBlock.java ++++ b/net/minecraft/world/level/block/FrostedIceBlock.java +@@ -48,7 +48,7 @@ public class FrostedIceBlock extends IceBlock { + && this.slightlyMelt(state, level, pos)) { + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(pos, direction); + BlockState blockState = level.getBlockState(mutableBlockPos); + if (blockState.is(this) && !this.slightlyMelt(blockState, level, mutableBlockPos)) { +@@ -84,7 +84,7 @@ public class FrostedIceBlock extends IceBlock { + int i = 0; + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(pos, direction); + if (level.getBlockState(mutableBlockPos).is(this)) { + if (++i >= neighborsRequired) { +diff --git a/net/minecraft/world/level/block/LeavesBlock.java b/net/minecraft/world/level/block/LeavesBlock.java +index a97b22e5acb791e959c528ccb330fa5ff92251e4..ca1a1661642a8ddd66d0e180be996a978a150730 100644 +--- a/net/minecraft/world/level/block/LeavesBlock.java ++++ b/net/minecraft/world/level/block/LeavesBlock.java +@@ -117,7 +117,7 @@ public class LeavesBlock extends Block implements SimpleWaterloggedBlock { + int i = 7; + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(pos, direction); + i = Math.min(i, getDistanceAt(level.getBlockState(mutableBlockPos)) + 1); + if (i == 1) { +diff --git a/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/net/minecraft/world/level/block/piston/PistonBaseBlock.java +index 41802482875bd4d4b505eb758740140de0db415a..8301a6d0fa0eeefc527aa79933a83da29ebaf451 100644 +--- a/net/minecraft/world/level/block/piston/PistonBaseBlock.java ++++ b/net/minecraft/world/level/block/piston/PistonBaseBlock.java +@@ -150,7 +150,7 @@ public class PistonBaseBlock extends DirectionalBlock { + } + + private boolean getNeighborSignal(SignalGetter signalGetter, BlockPos pos, Direction direction) { +- for (Direction direction1 : Direction.values()) { ++ for (Direction direction1 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction1 != direction && signalGetter.hasSignal(pos.relative(direction1), direction1)) { + return true; + } +@@ -161,7 +161,7 @@ public class PistonBaseBlock extends DirectionalBlock { + } else { + BlockPos blockPos = pos.above(); + +- for (Direction direction2 : Direction.values()) { ++ for (Direction direction2 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction2 != Direction.DOWN && signalGetter.hasSignal(blockPos.relative(direction2), direction2)) { + return true; + } +diff --git a/net/minecraft/world/level/block/state/pattern/BlockPattern.java b/net/minecraft/world/level/block/state/pattern/BlockPattern.java +index f7bb979f08634a7e1b77c59040f59fb5e11aafa5..9b60bbebd32f045bd0f5b91cd30264396e673d30 100644 +--- a/net/minecraft/world/level/block/state/pattern/BlockPattern.java ++++ b/net/minecraft/world/level/block/state/pattern/BlockPattern.java +@@ -79,8 +79,8 @@ public class BlockPattern { + int max = Math.max(Math.max(this.width, this.height), this.depth); + + for (BlockPos blockPos : BlockPos.betweenClosed(pos, pos.offset(max - 1, max - 1, max - 1))) { +- for (Direction direction : Direction.values()) { +- for (Direction direction1 : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values ++ for (Direction direction1 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction1 != direction && direction1 != direction.getOpposite()) { + BlockPattern.BlockPatternMatch blockPatternMatch = this.matches(blockPos, direction, direction1, loadingCache); + if (blockPatternMatch != null) { +diff --git a/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java b/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java +index 60d34309f6a961448944faedafd242e7496d14d9..e6cceee73b41604d3e034ad2a7edf7e281cf017b 100644 +--- a/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java ++++ b/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java +@@ -256,7 +256,7 @@ public interface VibrationSystem { + Vec3 vec3 = new Vec3(Mth.floor(eventPos.x) + 0.5, Mth.floor(eventPos.y) + 0.5, Mth.floor(eventPos.z) + 0.5); + Vec3 vec31 = new Vec3(Mth.floor(vibrationUserPos.x) + 0.5, Mth.floor(vibrationUserPos.y) + 0.5, Mth.floor(vibrationUserPos.z) + 0.5); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + Vec3 vec32 = vec3.relative(direction, 1.0E-5F); + if (level.isBlockInLine(new ClipBlockStateContext(vec32, vec31, state -> state.is(BlockTags.OCCLUDES_VIBRATION_SIGNALS))).getType() + != HitResult.Type.BLOCK) { +diff --git a/net/minecraft/world/level/levelgen/feature/BlueIceFeature.java b/net/minecraft/world/level/levelgen/feature/BlueIceFeature.java +index 5bbc9cacd8675c96d99f8b46399742753888d2f7..0c818d43c3d58e1f61e30d63556ba82356d53290 100644 +--- a/net/minecraft/world/level/levelgen/feature/BlueIceFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/BlueIceFeature.java +@@ -26,7 +26,7 @@ public class BlueIceFeature extends Feature { + } else { + boolean flag = false; + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction != Direction.DOWN && worldGenLevel.getBlockState(blockPos.relative(direction)).is(Blocks.PACKED_ICE)) { + flag = true; + break; +@@ -51,7 +51,7 @@ public class BlueIceFeature extends Feature { + ); + BlockState blockState = worldGenLevel.getBlockState(blockPos1); + if (blockState.isAir() || blockState.is(Blocks.WATER) || blockState.is(Blocks.PACKED_ICE) || blockState.is(Blocks.ICE)) { +- for (Direction direction1 : Direction.values()) { ++ for (Direction direction1 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + BlockState blockState1 = worldGenLevel.getBlockState(blockPos1.relative(direction1)); + if (blockState1.is(Blocks.BLUE_ICE)) { + worldGenLevel.setBlock(blockPos1, Blocks.BLUE_ICE.defaultBlockState(), 2); +diff --git a/net/minecraft/world/level/levelgen/feature/Feature.java b/net/minecraft/world/level/levelgen/feature/Feature.java +index 6d7c2c6f0469f934b9599b59f00580cd1919be58..c868fbf333d59bee5256c07f7878c2dc6462a637 100644 +--- a/net/minecraft/world/level/levelgen/feature/Feature.java ++++ b/net/minecraft/world/level/levelgen/feature/Feature.java +@@ -205,7 +205,7 @@ public abstract class Feature { + public static boolean checkNeighbors(Function adjacentStateAccessor, BlockPos pos, Predicate filter) { + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(pos, direction); + if (filter.test(adjacentStateAccessor.apply(mutableBlockPos))) { + return true; +diff --git a/net/minecraft/world/level/levelgen/feature/GlowstoneFeature.java b/net/minecraft/world/level/levelgen/feature/GlowstoneFeature.java +index 0062e6ba05ff73ed80c5bc5d3765c8872ef081e0..e002a1ad27bc18a7fc5ac9d1ecae523a9e66886f 100644 +--- a/net/minecraft/world/level/levelgen/feature/GlowstoneFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/GlowstoneFeature.java +@@ -35,7 +35,7 @@ public class GlowstoneFeature extends Feature { + if (worldGenLevel.getBlockState(blockPos1).isAir()) { + int i1 = 0; + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (worldGenLevel.getBlockState(blockPos1.relative(direction)).is(Blocks.GLOWSTONE)) { + i1++; + } +diff --git a/net/minecraft/world/level/levelgen/feature/TreeFeature.java b/net/minecraft/world/level/levelgen/feature/TreeFeature.java +index bd17dbe53c82001b60f4cfdd9d2dacb06a98f5ea..59957ddf6b7742c0a8eac53eddb1b9fa0c7a7bec 100644 +--- a/net/minecraft/world/level/levelgen/feature/TreeFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/TreeFeature.java +@@ -203,7 +203,7 @@ public class TreeFeature extends Feature { + + discreteVoxelShape.fill(blockPos1.getX() - box.minX(), blockPos1.getY() - box.minY(), blockPos1.getZ() - box.minZ()); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(blockPos1, direction); + if (box.isInside(mutableBlockPos)) { + int i3 = mutableBlockPos.getX() - box.minX(); +diff --git a/net/minecraft/world/level/levelgen/feature/VinesFeature.java b/net/minecraft/world/level/levelgen/feature/VinesFeature.java +index 256e130ff43f826c3eac2a0faa526dc12e5fda0e..c744ea5d2df9692e7e8c53ea96fedc8a2b77c0c9 100644 +--- a/net/minecraft/world/level/levelgen/feature/VinesFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/VinesFeature.java +@@ -21,7 +21,7 @@ public class VinesFeature extends Feature { + if (!worldGenLevel.isEmptyBlock(blockPos)) { + return false; + } else { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction != Direction.DOWN && VineBlock.isAcceptableNeighbour(worldGenLevel, blockPos.relative(direction), direction)) { + worldGenLevel.setBlock( + blockPos, Blocks.VINE.defaultBlockState().setValue(VineBlock.getPropertyForFace(direction), Boolean.valueOf(true)), 2 +diff --git a/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java b/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java +index 75ae614790d46ba01003a13fb9d521299c675c56..ccb5a5d7536d4a3b57e08ff81a06784aed863dc7 100644 +--- a/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java ++++ b/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java +@@ -37,7 +37,7 @@ public class CreakingHeartDecorator extends TreeDecorator { + List list1 = new ArrayList<>(list); + Util.shuffle(list1, randomSource); + Optional optional = list1.stream().filter(pos -> { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (!context.checkBlock(pos.relative(direction), blockState -> blockState.is(BlockTags.LOGS))) { + return false; + } +diff --git a/net/minecraft/world/level/levelgen/structure/structures/BuriedTreasurePieces.java b/net/minecraft/world/level/levelgen/structure/structures/BuriedTreasurePieces.java +index f515f7c881d597072db226f60cfae2d7ebe89ea4..ed4d719c7dfbce695a8fdc59ded6aae75c273714 100644 +--- a/net/minecraft/world/level/levelgen/structure/structures/BuriedTreasurePieces.java ++++ b/net/minecraft/world/level/levelgen/structure/structures/BuriedTreasurePieces.java +@@ -54,7 +54,7 @@ public class BuriedTreasurePieces { + || blockState1 == Blocks.DIORITE.defaultBlockState()) { + BlockState blockState2 = !blockState.isAir() && !this.isLiquid(blockState) ? blockState : Blocks.SAND.defaultBlockState(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + BlockPos blockPos = mutableBlockPos.relative(direction); + BlockState blockState3 = level.getBlockState(blockPos); + if (blockState3.isAir() || this.isLiquid(blockState3)) { diff --git a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java index 03ec2264b19e1794b609fe09d1ceaba4e0c4d669..3f38fe0140d13c7c356340ba06b55469ede0a1ad 100644 --- a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java @@ -247,3 +490,68 @@ index 03ec2264b19e1794b609fe09d1ceaba4e0c4d669..3f38fe0140d13c7c356340ba06b55469 RandomSource randomSource = RandomSource.create(level.getSeed()).forkPositional().at(pieces.calculateBoundingBox().getCenter()); Util.shuffle(list, randomSource); int min = Math.min(set.size(), randomSource.nextInt(5, 8)); +diff --git a/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java b/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java +index 1acd506f7c0679fa9f69b6ab221002b28d00c3e5..20502112f4e338984efc264109d0c894b1d33d56 100644 +--- a/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java ++++ b/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java +@@ -565,7 +565,7 @@ public class MineshaftPieces { + BlockPos.MutableBlockPos worldPos = this.getWorldPos(x, y, z); + int i = 0; + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + worldPos.move(direction); + if (box.isInside(worldPos) && level.getBlockState(worldPos).isFaceSturdy(level, worldPos, direction.getOpposite())) { + if (++i >= required) { +diff --git a/net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces.java b/net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces.java +index b70ef62b4e41ee4851b2cdf53e0be1083233d052..f1ac1e1283cb276e5b21330d700cafdb65e60be1 100644 +--- a/net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces.java ++++ b/net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces.java +@@ -244,7 +244,7 @@ public class OceanMonumentPieces { + for (int i2 = 0; i2 < 3; i2++) { + int roomIndex = getRoomIndex(i, i2, i1); + if (roomDefinitions[roomIndex] != null) { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + int i3 = i + direction.getStepX(); + int i4 = i2 + direction.getStepY(); + int i5 = i1 + direction.getStepZ(); +diff --git a/net/minecraft/world/level/material/LavaFluid.java b/net/minecraft/world/level/material/LavaFluid.java +index d66321acb26682a02efa02cf1443b40d2a17f67b..86ed35984760a0a90e92a297d3080378e8b4bf6f 100644 +--- a/net/minecraft/world/level/material/LavaFluid.java ++++ b/net/minecraft/world/level/material/LavaFluid.java +@@ -126,7 +126,7 @@ public abstract class LavaFluid extends FlowingFluid { + } + + private boolean hasFlammableNeighbours(LevelReader level, BlockPos pos) { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (this.isFlammable(level, pos.relative(direction))) { + return true; + } +diff --git a/net/minecraft/world/level/pathfinder/AmphibiousNodeEvaluator.java b/net/minecraft/world/level/pathfinder/AmphibiousNodeEvaluator.java +index 33047e446adf252a179cb0220c20a7d83f361482..9c58425f2afdbe5701910d8473fd5b344ffe31dd 100644 +--- a/net/minecraft/world/level/pathfinder/AmphibiousNodeEvaluator.java ++++ b/net/minecraft/world/level/pathfinder/AmphibiousNodeEvaluator.java +@@ -97,7 +97,7 @@ public class AmphibiousNodeEvaluator extends WalkNodeEvaluator { + if (pathTypeFromState == PathType.WATER) { + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.set(x, y, z).move(direction); + PathType pathTypeFromState1 = context.getPathTypeFromState(mutableBlockPos.getX(), mutableBlockPos.getY(), mutableBlockPos.getZ()); + if (pathTypeFromState1 == PathType.BLOCKED) { +diff --git a/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java b/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java +index b25d3bf3d5c575cd5c9a97a6d5ee7191467fe839..c598d91cd9fb711bc9aaa068fbb806619c0d4fe2 100644 +--- a/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java ++++ b/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java +@@ -51,7 +51,7 @@ public class SwimNodeEvaluator extends NodeEvaluator { + int i = 0; + Map map = Maps.newEnumMap(Direction.class); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + Node node1 = this.findAcceptedNode(node.x + direction.getStepX(), node.y + direction.getStepY(), node.z + direction.getStepZ()); + map.put(direction, node1); + if (this.isNodeValid(node1)) { diff --git a/public/readme/README_CN.md b/public/readme/README_CN.md index bd676622..94fee8df 100644 --- a/public/readme/README_CN.md +++ b/public/readme/README_CN.md @@ -9,7 +9,7 @@ **Leaf** 是一个基于 [Paper](https://papermc.io/) 的分支,专为高自定义和高性能而设计,基于 [Gale](https://github.com/Dreeam-qwq/Gale) 之上,并融合了其他核心的优化和修复。 -> [!WARNING] +> [!WARNING] > Leaf 是一个面向性能的分支。在迁移到 Leaf 之前,请务必**提前备份**。欢迎任何人贡献优化或报告问题来帮助我们改进。 [English](../../README.md) | **中文** From 67c344231d7c3dcc62294a57a44305ce242d0e41 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Mon, 9 Jun 2025 12:30:08 +0200 Subject: [PATCH 54/57] Fix infinite loop in RegionFile IO If an exception is thrown during decompress then the read process would be started again, which of course would eventually throw in the decompress process. --- ...r-Fix-infinite-loop-in-RegionFile-IO.patch | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch diff --git a/leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch b/leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch new file mode 100644 index 00000000..64550a29 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Mon, 9 Jun 2025 12:00:57 +0200 +Subject: [PATCH] Paper: Fix infinite loop in RegionFile IO + + +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java +index 60ed8cff397c964323fbda203ebfab3c7c9a873b..32ee1d3a0ae67738a65545e6a0046a12fb940fa4 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java +@@ -1143,7 +1143,7 @@ public final class MoonriseRegionFileIO { + LOGGER.error("Failed to decompress chunk data for task: " + this.toString(), thr); + } + +- if (compoundTag == null) { ++ if (throwable == null && compoundTag == null) { + // need to re-try from the start + this.scheduleReadIO(); + return; From 0b563bca81cf78425588b0cc5b6b61b27d6b0fb2 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 9 Jun 2025 21:02:21 +0800 Subject: [PATCH 55/57] [ci skip] Add Intellij project icon --- .gitignore | 1 + .idea/icon.svg | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 .idea/icon.svg diff --git a/.gitignore b/.gitignore index 44a9d442..a16f5ea7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ # IntelliJ *.iml .idea +!.idea/icon.svg # Gradle !gradle/wrapper/gradle-wrapper.jar diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 00000000..e4563310 --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 985513a2f7b348b3bb80893559a8b9fb69d8333c Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 9 Jun 2025 23:19:30 +0800 Subject: [PATCH 56/57] [ci skip] Improve grammar for README CN Co-Authored-By: YuanYuanOwO <81153017+YuanYuanOwO@users.noreply.github.com> --- README.md | 2 +- public/readme/README_CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 08609ec3..c974508f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ## 🍃 Features - **Based on [Gale](https://github.com/Dreeam-qwq/Gale)** for better performance - **Async** pathfinding, mob spawning and entity tracker -- **Various optimizations** blending from [other forks](https://github.com/Winds-Studio/Leaf#-credits) and our own +- **Various optimizations** blending from [other forks](#-credits) and our own - **Fully compatible** with Spigot and Paper plugins - **Latest dependencies**, keeping all dependencies up-to-date - **Allows all characters in usernames**, including Chinese and other characters diff --git a/public/readme/README_CN.md b/public/readme/README_CN.md index 94fee8df..4b1be0f8 100644 --- a/public/readme/README_CN.md +++ b/public/readme/README_CN.md @@ -17,7 +17,7 @@ ## 🍃 特点 - **基于 [Gale](https://github.com/Dreeam-qwq/Gale)**,以获得更好的性能 - **异步**寻路、生物生成和实体追踪 -- **大量优化**融合自 [其他核心](https://github.com/Winds-Studio/Leaf#-致谢) 的补丁, 和我们自己的 +- **大量优化**融合自 [其他核心](#-致谢) 和我们自己的的补丁 - **完全兼容** Spigot 和 Paper 插件 - **最新依赖**,保持所有依赖项为最新版本 - **允许用户名使用所有字符**,包括中文和其他字符 From b4ad51dbb5bbf0dc0d7684dfdf02419e9593315b Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Tue, 10 Jun 2025 12:26:14 +0800 Subject: [PATCH 57/57] Correct `optimize-block-entities` config name to keep consistency Co-Authored-By: Pascalpex <68245106+Pascalpex@users.noreply.github.com> --- ...timize-BlockEntities-tickersInLevel.patch} | 6 ++--- ...r-Fix-infinite-loop-in-RegionFile-IO.patch | 16 +++++++++---- .../modules/opt/OptimiseBlockEntities.java | 18 -------------- .../modules/opt/OptimizeBlockEntities.java | 24 +++++++++++++++++++ .../opt/OptimizePlayerMovementProcessing.java | 1 - .../modules/opt/ReduceChunkSourceUpdates.java | 2 +- 6 files changed, 40 insertions(+), 27 deletions(-) rename leaf-server/minecraft-patches/features/{0164-Optimise-BlockEntities-tickersInLevel.patch => 0164-Optimize-BlockEntities-tickersInLevel.patch} (81%) delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBlockEntities.java diff --git a/leaf-server/minecraft-patches/features/0164-Optimise-BlockEntities-tickersInLevel.patch b/leaf-server/minecraft-patches/features/0164-Optimize-BlockEntities-tickersInLevel.patch similarity index 81% rename from leaf-server/minecraft-patches/features/0164-Optimise-BlockEntities-tickersInLevel.patch rename to leaf-server/minecraft-patches/features/0164-Optimize-BlockEntities-tickersInLevel.patch index 1ad19b8d..ae5d6817 100644 --- a/leaf-server/minecraft-patches/features/0164-Optimise-BlockEntities-tickersInLevel.patch +++ b/leaf-server/minecraft-patches/features/0164-Optimize-BlockEntities-tickersInLevel.patch @@ -1,11 +1,11 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 13:30:07 +0200 -Subject: [PATCH] Optimise BlockEntities tickersInLevel +Subject: [PATCH] Optimize BlockEntities tickersInLevel diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 546fb78339c005ed71142cb3c894f816b8c72d08..e6eab6929b08503c49debbbd25497ffedad438e1 100644 +index 546fb78339c005ed71142cb3c894f816b8c72d08..a90bf0d80ae4dac9b19b8e467b402917cc19a271 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -72,7 +72,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p @@ -13,7 +13,7 @@ index 546fb78339c005ed71142cb3c894f816b8c72d08..e6eab6929b08503c49debbbd25497ffe } }; - private final Map tickersInLevel = Maps.newHashMap(); -+ private final Map tickersInLevel = org.dreeam.leaf.config.modules.opt.OptimiseBlockEntities.enabled ? new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>() : Maps.newHashMap(); // Leaf - Optimise BlockEntities tickersInLevel ++ private final Map tickersInLevel = org.dreeam.leaf.config.modules.opt.OptimizeBlockEntities.enabled ? new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>() : Maps.newHashMap(); // Leaf - Optimise BlockEntities tickersInLevel public boolean loaded; public final ServerLevel level; // CraftBukkit - type @Nullable diff --git a/leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch b/leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch index 64550a29..8efc83f0 100644 --- a/leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch +++ b/leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch @@ -1,11 +1,19 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Mon, 9 Jun 2025 12:00:57 +0200 +From: Spottedleaf +Date: Mon, 9 Jun 2025 02:46:34 -0700 Subject: [PATCH] Paper: Fix infinite loop in RegionFile IO +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper + +https://github.com/PaperMC/Paper/commit/519e4224b1ba73a99c58c8fc53aab003eb6af37a + +If an exception is thrown during decompress then the read process +would be started again, which of course would eventually throw in +the decompress process. diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java -index 60ed8cff397c964323fbda203ebfab3c7c9a873b..32ee1d3a0ae67738a65545e6a0046a12fb940fa4 100644 +index 60ed8cff397c964323fbda203ebfab3c7c9a873b..88207a3afd0260f572c7f54003d566c2c1cb1633 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java @@ -1143,7 +1143,7 @@ public final class MoonriseRegionFileIO { @@ -13,7 +21,7 @@ index 60ed8cff397c964323fbda203ebfab3c7c9a873b..32ee1d3a0ae67738a65545e6a0046a12 } - if (compoundTag == null) { -+ if (throwable == null && compoundTag == null) { ++ if (throwable == null && compoundTag == null) { // Paper - Fix infinite loop in RegionFile IO // need to re-try from the start this.scheduleReadIO(); return; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java deleted file mode 100644 index e1ddf5eb..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.dreeam.leaf.config.modules.opt; - -import org.dreeam.leaf.config.ConfigModules; -import org.dreeam.leaf.config.EnumConfigCategory; - -public class OptimiseBlockEntities extends ConfigModules { - - public String getBasePath() { - return EnumConfigCategory.PERF.getBaseKeyName(); - } - - public static boolean enabled = true; - - @Override - public void onLoaded() { - enabled = config.getBoolean(getBasePath() + ".optimise-block-entities", enabled); - } -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBlockEntities.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBlockEntities.java new file mode 100644 index 00000000..4e76dea4 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBlockEntities.java @@ -0,0 +1,24 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class OptimizeBlockEntities extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName(); + } + + public static boolean enabled = true; + + @Override + public void onLoaded() { + // Transfer old config + Boolean optimiseBlockEntities = config.getBoolean(getBasePath() + ".optimise-block-entities"); + if (optimiseBlockEntities != null && optimiseBlockEntities) { + enabled = true; + } + + enabled = config.getBoolean(getBasePath() + ".optimize-block-entities", enabled); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java index 3f407850..f8016d34 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java @@ -17,6 +17,5 @@ public class OptimizePlayerMovementProcessing extends ConfigModules { Whether to optimize player movement processing by skipping unnecessary edge checks and avoiding redundant view distance updates.""", """ 是否优化玩家移动处理,跳过不必要的边缘检查并避免冗余的视距更新。""")); - } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java index e89a0ad2..77fb1a41 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java @@ -15,7 +15,7 @@ public class ReduceChunkSourceUpdates extends ConfigModules { public void onLoaded() { enabled = config.getBoolean(getBasePath() + ".enabled", enabled, config.pickStringRegionBased( - "Reduces chunk source updates on inter-chunk player moves. (Recommended to enable)", + "Reduces chunk source updates on inter-chunk player moves.", "减少玩家跨区块移动时的区块源更新。" ) );