diff --git a/patches/server/Add-feature-patch-hook-for-overrides.patch b/patches/server/Add-feature-patch-hook-for-overrides.patch new file mode 100644 index 0000000000..a4a2ff0b39 --- /dev/null +++ b/patches/server/Add-feature-patch-hook-for-overrides.patch @@ -0,0 +1,290 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke +Date: Thu, 5 Dec 2024 13:00:22 +0100 +Subject: [PATCH] Add feature patch hook for overrides + + +diff --git a/src/main/java/io/papermc/paper/FeatureHooks.java b/src/main/java/io/papermc/paper/FeatureHooks.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/FeatureHooks.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper; ++ ++import io.papermc.paper.command.PaperSubcommand; ++import it.unimi.dsi.fastutil.longs.LongOpenHashSet; ++import it.unimi.dsi.fastutil.longs.LongSet; ++import it.unimi.dsi.fastutil.longs.LongSets; ++import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++import it.unimi.dsi.fastutil.objects.ObjectSets; ++import java.util.List; ++import java.util.Map; ++import java.util.Set; ++import net.minecraft.core.Registry; ++import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; ++import net.minecraft.server.level.ServerPlayer; ++import net.minecraft.world.level.ChunkPos; ++import net.minecraft.world.level.Level; ++import net.minecraft.world.level.biome.Biome; ++import net.minecraft.world.level.block.Block; ++import net.minecraft.world.level.block.Blocks; ++import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.chunk.LevelChunk; ++import net.minecraft.world.level.chunk.LevelChunkSection; ++import net.minecraft.world.level.chunk.PalettedContainer; ++import org.bukkit.Chunk; ++import org.bukkit.World; ++ ++public final class FeatureHooks { ++ ++ public static void initChunkTaskScheduler(final boolean useParallelGen) { ++ } ++ ++ public static void registerPaperCommands(final Map, PaperSubcommand> commands) { ++ } ++ ++ public static LevelChunkSection createSection(final Registry biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) { ++ return new LevelChunkSection(biomeRegistry); ++ } ++ ++ public static void sendChunkRefreshPackets(final List playersInRange, final LevelChunk chunk) { ++ final ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null); ++ for (final ServerPlayer player : playersInRange) { ++ if (player.connection == null) continue; ++ ++ player.connection.send(refreshPacket); ++ } ++ } ++ ++ public static PalettedContainer emptyPalettedBlockContainer() { ++ return new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); ++ } ++ ++ public static Set getSentChunkKeys(final ServerPlayer player) { ++ final LongSet keys = new LongOpenHashSet(); ++ player.getChunkTrackingView().forEach(pos -> keys.add(pos.longKey)); ++ return LongSets.unmodifiable(keys); ++ } ++ ++ public static Set getSentChunks(final ServerPlayer player) { ++ final ObjectSet chunks = new ObjectOpenHashSet<>(); ++ final World world = player.serverLevel().getWorld(); ++ player.getChunkTrackingView().forEach(pos -> { ++ final org.bukkit.Chunk chunk = world.getChunkAt(pos.longKey); ++ chunks.add(chunk); ++ }); ++ return ObjectSets.unmodifiable(chunks); ++ } ++ ++ public static boolean isChunkSent(final ServerPlayer player, final long chunkKey) { ++ return player.getChunkTrackingView().contains(new ChunkPos(chunkKey)); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/io/papermc/paper/command/PaperCommand.java ++++ b/src/main/java/io/papermc/paper/command/PaperCommand.java +@@ -0,0 +0,0 @@ + package io.papermc.paper.command; + ++import io.papermc.paper.FeatureHooks; + import io.papermc.paper.command.subcommands.*; + import it.unimi.dsi.fastutil.Pair; + import java.util.ArrayList; +@@ -0,0 +0,0 @@ public final class PaperCommand extends Command { + commands.put(Set.of("dumpitem"), new DumpItemCommand()); + commands.put(Set.of("mobcaps", "playermobcaps"), new MobcapsCommand()); + commands.put(Set.of("dumplisteners"), new DumpListenersCommand()); ++ FeatureHooks.registerPaperCommands(commands); + + return commands.entrySet().stream() + .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue()))) +diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java ++++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +@@ -0,0 +0,0 @@ + package io.papermc.paper.configuration; + + import com.mojang.logging.LogUtils; ++import io.papermc.paper.FeatureHooks; + import io.papermc.paper.configuration.constraint.Constraints; + import io.papermc.paper.configuration.type.number.DoubleOr; + import io.papermc.paper.configuration.type.number.IntOr; +@@ -0,0 +0,0 @@ public class GlobalConfiguration extends ConfigurationPart { + @PostProcess + private void postProcess() { + ca.spottedleaf.moonrise.common.util.MoonriseCommon.adjustWorkerThreads(this.workerThreads, this.ioThreads); ++ String newChunkSystemGenParallelism = this.genParallelism; ++ if (newChunkSystemGenParallelism.equalsIgnoreCase("default")) { ++ newChunkSystemGenParallelism = "true"; ++ } ++ ++ final boolean useParallelGen; ++ if (newChunkSystemGenParallelism.equalsIgnoreCase("on") || newChunkSystemGenParallelism.equalsIgnoreCase("enabled") ++ || newChunkSystemGenParallelism.equalsIgnoreCase("true")) { ++ useParallelGen = true; ++ } else if (newChunkSystemGenParallelism.equalsIgnoreCase("off") || newChunkSystemGenParallelism.equalsIgnoreCase("disabled") ++ || newChunkSystemGenParallelism.equalsIgnoreCase("false")) { ++ useParallelGen = false; ++ } else { ++ throw new IllegalStateException("Invalid option for gen-parallelism: must be one of [on, off, enabled, disabled, true, false, default]"); ++ } ++ FeatureHooks.initChunkTaskScheduler(useParallelGen); + } + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +@@ -0,0 +0,0 @@ package org.bukkit.craftbukkit; + import com.google.common.base.Preconditions; + import com.google.common.base.Predicates; + import com.mojang.serialization.Codec; ++import io.papermc.paper.FeatureHooks; + import java.util.Arrays; + import java.util.Collection; + import java.util.Objects; +@@ -0,0 +0,0 @@ public class CraftChunk implements Chunk { + private final ServerLevel worldServer; + private final int x; + private final int z; +- private static final PalettedContainer emptyBlockIDs = new PalettedContainer<>(net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); ++ private static final PalettedContainer emptyBlockIDs = FeatureHooks.emptyPalettedBlockContainer(); + private static final byte[] FULL_LIGHT = new byte[2048]; + private static final byte[] EMPTY_LIGHT = new byte[2048]; + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -0,0 +0,0 @@ public final class CraftServer implements Server { + public ChunkGenerator.ChunkData createChunkData(World world) { + Preconditions.checkArgument(world != null, "World cannot be null"); + ServerLevel handle = ((CraftWorld) world).getHandle(); +- return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().lookupOrThrow(Registries.BIOME)); ++ return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().lookupOrThrow(Registries.BIOME), world); + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -0,0 +0,0 @@ import com.google.common.base.Predicates; + import com.google.common.collect.ImmutableList; + import com.google.common.collect.ImmutableMap; + import com.mojang.datafixers.util.Pair; ++import io.papermc.paper.FeatureHooks; + import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; + import it.unimi.dsi.fastutil.longs.Long2ObjectMap; + import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { + List playersInRange = playerChunk.playerProvider.getPlayers(playerChunk.getPos(), false); + if (playersInRange.isEmpty()) return; + +- ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, this.world.getLightEngine(), null, null); +- for (ServerPlayer player : playersInRange) { +- if (player.connection == null) continue; +- +- player.connection.send(refreshPacket); +- } ++ FeatureHooks.sendChunkRefreshPackets(playersInRange, chunk); + }); + }); + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -0,0 +0,0 @@ import com.google.common.io.BaseEncoding; + import com.mojang.authlib.GameProfile; + import com.mojang.datafixers.util.Pair; + import io.netty.buffer.Unpooled; ++import io.papermc.paper.FeatureHooks; + import it.unimi.dsi.fastutil.shorts.ShortArraySet; + import it.unimi.dsi.fastutil.shorts.ShortSet; + import java.io.ByteArrayOutputStream; +@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + @Override + public Set getSentChunkKeys() { + org.spigotmc.AsyncCatcher.catchOp("accessing sent chunks"); +- final it.unimi.dsi.fastutil.longs.LongOpenHashSet keys = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(); +- this.getHandle().getChunkTrackingView().forEach(pos -> keys.add(pos.longKey)); +- return it.unimi.dsi.fastutil.longs.LongSets.unmodifiable(keys); ++ return FeatureHooks.getSentChunkKeys(this.getHandle()); + } + + @Override + public Set getSentChunks() { + org.spigotmc.AsyncCatcher.catchOp("accessing sent chunks"); +- final it.unimi.dsi.fastutil.objects.ObjectOpenHashSet chunks = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(); +- final org.bukkit.World world = this.getWorld(); +- this.getHandle().getChunkTrackingView().forEach(pos -> { +- final org.bukkit.Chunk chunk = world.getChunkAt(pos.longKey); +- chunks.add(chunk); +- }); +- return it.unimi.dsi.fastutil.objects.ObjectSets.unmodifiable(chunks); ++ return FeatureHooks.getSentChunks(this.getHandle()); + } + + @Override + public boolean isChunkSent(final long chunkKey) { + org.spigotmc.AsyncCatcher.catchOp("accessing sent chunks"); +- return this.getHandle().getChunkTrackingView().contains(new net.minecraft.world.level.ChunkPos(chunkKey)); ++ return FeatureHooks.isChunkSent(this.getHandle(), chunkKey); + } + // Paper end + +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java +@@ -0,0 +0,0 @@ + package org.bukkit.craftbukkit.generator; + ++import io.papermc.paper.FeatureHooks; + import java.util.HashSet; + import java.util.Set; + import net.minecraft.core.BlockPos; +@@ -0,0 +0,0 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData { + private final Registry biomes; + private Set tiles; + private final Set lights = new HashSet<>(); ++ private final org.bukkit.World world; + ++ @Deprecated @io.papermc.paper.annotation.DoNotUse + public OldCraftChunkData(int minHeight, int maxHeight, Registry biomes) { ++ this(minHeight, maxHeight, biomes, null); ++ } ++ ++ public OldCraftChunkData(int minHeight, int maxHeight, Registry biomes, org.bukkit.World world) { ++ this.world = world; + this.minHeight = minHeight; + this.maxHeight = maxHeight; + this.biomes = biomes; +@@ -0,0 +0,0 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData { + int offset = (y - this.minHeight) >> 4; + LevelChunkSection section = this.sections[offset]; + if (create && section == null) { +- this.sections[offset] = section = new LevelChunkSection(this.biomes); ++ this.sections[offset] = section = FeatureHooks.createSection(this.biomes, this.world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) this.world).getHandle() : null, null, offset + (this.minHeight >> 4)); // Paper - Anti-Xray - Add parameters + } + return section; + } +diff --git a/src/main/java/org/bukkit/craftbukkit/map/RenderData.java b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/map/RenderData.java ++++ b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java +@@ -0,0 +0,0 @@ import org.bukkit.map.MapCursor; + + public class RenderData { + +- public final byte[] buffer; ++ public byte[] buffer; + public final ArrayList cursors; + + public RenderData() { diff --git a/patches/server/Anti-Xray.patch b/patches/server/Anti-Xray.patch index c88549b1fe..f002f23549 100644 --- a/patches/server/Anti-Xray.patch +++ b/patches/server/Anti-Xray.patch @@ -1000,6 +1000,48 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + chunkPacketBlockControllerAntiXray.obfuscate(this); + } +} +diff --git a/src/main/java/io/papermc/paper/FeatureHooks.java b/src/main/java/io/papermc/paper/FeatureHooks.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/io/papermc/paper/FeatureHooks.java ++++ b/src/main/java/io/papermc/paper/FeatureHooks.java +@@ -0,0 +0,0 @@ import it.unimi.dsi.fastutil.longs.LongSets; + import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; + import it.unimi.dsi.fastutil.objects.ObjectSet; + import it.unimi.dsi.fastutil.objects.ObjectSets; ++import java.util.HashMap; + import java.util.List; + import java.util.Map; + import java.util.Set; +@@ -0,0 +0,0 @@ public final class FeatureHooks { + } + + public static LevelChunkSection createSection(final Registry biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) { +- return new LevelChunkSection(biomeRegistry); ++ return new LevelChunkSection(biomeRegistry, level, chunkPos, chunkSection); // Paper - Anti-Xray - Add parameters + } + + public static void sendChunkRefreshPackets(final List playersInRange, final LevelChunk chunk) { +- final ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null); ++ // Paper start - Anti-Xray ++ final Map refreshPackets = new HashMap<>(); + for (final ServerPlayer player : playersInRange) { + if (player.connection == null) continue; + +- player.connection.send(refreshPacket); ++ final Boolean shouldModify = chunk.getLevel().chunkPacketBlockController.shouldModify(player, chunk); ++ player.connection.send(refreshPackets.computeIfAbsent(shouldModify, s -> { // Use connection to prevent creating firing event ++ return new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null, (Boolean) s); ++ })); + } ++ // Paper end - Anti-Xray + } + + public static PalettedContainer emptyPalettedBlockContainer() { +- return new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); ++ return new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); // Paper - Anti-Xray - Add preset block states + } + + public static Set getSentChunkKeys(final ServerPlayer player) { diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java @@ -1584,80 +1626,3 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } // CraftBukkit end -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -@@ -0,0 +0,0 @@ public class CraftChunk implements Chunk { - private final ServerLevel worldServer; - private final int x; - private final int z; -- private static final PalettedContainer emptyBlockIDs = new PalettedContainer<>(net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); -+ private static final PalettedContainer emptyBlockIDs = new PalettedContainer<>(net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); // Paper - Anti-Xray - Add preset block states - private static final byte[] FULL_LIGHT = new byte[2048]; - private static final byte[] EMPTY_LIGHT = new byte[2048]; - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -0,0 +0,0 @@ public final class CraftServer implements Server { - public ChunkGenerator.ChunkData createChunkData(World world) { - Preconditions.checkArgument(world != null, "World cannot be null"); - ServerLevel handle = ((CraftWorld) world).getHandle(); -- return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().lookupOrThrow(Registries.BIOME)); -+ return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().lookupOrThrow(Registries.BIOME), world); // Paper - Anti-Xray - Add parameters - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { - List playersInRange = playerChunk.playerProvider.getPlayers(playerChunk.getPos(), false); - if (playersInRange.isEmpty()) return; - -- ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, this.world.getLightEngine(), null, null); -+ // Paper start - Anti-Xray bypass -+ final Map refreshPackets = new HashMap<>(); - for (ServerPlayer player : playersInRange) { - if (player.connection == null) continue; - -- player.connection.send(refreshPacket); -+ Boolean shouldModify = chunk.getLevel().chunkPacketBlockController.shouldModify(player, chunk); -+ player.connection.send(refreshPackets.computeIfAbsent(shouldModify, s -> { // Use connection to prevent creating firing event -+ return new ClientboundLevelChunkWithLightPacket(chunk, this.world.getLightEngine(), null, null, (Boolean) s); -+ })); -+ // Paper end - Anti-Xray bypass - } - }); - }); -diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java -+++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java -@@ -0,0 +0,0 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData { - private final Registry biomes; - private Set tiles; - private final Set lights = new HashSet<>(); -+ // Paper start - Anti-Xray - Add parameters -+ private final org.bukkit.World world; - -- public OldCraftChunkData(int minHeight, int maxHeight, Registry biomes) { -+ @Deprecated @io.papermc.paper.annotation.DoNotUse public OldCraftChunkData(int minHeight, int maxHeight, Registry biomes) { this(minHeight, maxHeight, biomes, null); } -+ public OldCraftChunkData(int minHeight, int maxHeight, Registry biomes, org.bukkit.World world) { -+ this.world = world; -+ // Paper end - this.minHeight = minHeight; - this.maxHeight = maxHeight; - this.biomes = biomes; -@@ -0,0 +0,0 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData { - int offset = (y - this.minHeight) >> 4; - LevelChunkSection section = this.sections[offset]; - if (create && section == null) { -- this.sections[offset] = section = new LevelChunkSection(this.biomes); -+ this.sections[offset] = section = new LevelChunkSection(this.biomes, this.world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) this.world).getHandle() : null, null, offset + (this.minHeight >> 4)); // Paper - Anti-Xray - Add parameters - } - return section; - } diff --git a/patches/server/Implement-chunk-view-API.patch b/patches/server/Implement-chunk-view-API.patch index 06179987d9..ede618a2ed 100644 --- a/patches/server/Implement-chunk-view-API.patch +++ b/patches/server/Implement-chunk-view-API.patch @@ -4,45 +4,48 @@ Date: Thu, 5 Dec 2024 12:15:07 +0100 Subject: [PATCH] Implement chunk view API -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +diff --git a/src/main/java/io/papermc/paper/FeatureHooks.java b/src/main/java/io/papermc/paper/FeatureHooks.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - @Override - public Set getSentChunkKeys() { - org.spigotmc.AsyncCatcher.catchOp("accessing sent chunks"); -- final it.unimi.dsi.fastutil.longs.LongOpenHashSet keys = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(); -- this.getHandle().getChunkTrackingView().forEach(pos -> keys.add(pos.longKey)); -- return it.unimi.dsi.fastutil.longs.LongSets.unmodifiable(keys); -+ return it.unimi.dsi.fastutil.longs.LongSets.unmodifiable( -+ this.getHandle().moonrise$getChunkLoader().getSentChunksRaw().clone() -+ ); +--- a/src/main/java/io/papermc/paper/FeatureHooks.java ++++ b/src/main/java/io/papermc/paper/FeatureHooks.java +@@ -0,0 +0,0 @@ package io.papermc.paper; + import io.papermc.paper.command.PaperSubcommand; + import io.papermc.paper.command.subcommands.ChunkDebugCommand; + import io.papermc.paper.command.subcommands.FixLightCommand; ++import it.unimi.dsi.fastutil.longs.LongIterator; + import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +-import it.unimi.dsi.fastutil.longs.LongSet; + import it.unimi.dsi.fastutil.longs.LongSets; + import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; + import it.unimi.dsi.fastutil.objects.ObjectSet; +@@ -0,0 +0,0 @@ public final class FeatureHooks { } - @Override - public Set getSentChunks() { - org.spigotmc.AsyncCatcher.catchOp("accessing sent chunks"); -- final it.unimi.dsi.fastutil.objects.ObjectOpenHashSet chunks = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(); -+ final it.unimi.dsi.fastutil.longs.LongOpenHashSet rawChunkKeys = this.getHandle().moonrise$getChunkLoader().getSentChunksRaw(); -+ final it.unimi.dsi.fastutil.objects.ObjectOpenHashSet chunks = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(rawChunkKeys.size()); - final org.bukkit.World world = this.getWorld(); -- this.getHandle().getChunkTrackingView().forEach(pos -> { + public static Set getSentChunkKeys(final ServerPlayer player) { +- final LongSet keys = new LongOpenHashSet(); +- player.getChunkTrackingView().forEach(pos -> keys.add(pos.longKey)); +- return LongSets.unmodifiable(keys); ++ return LongSets.unmodifiable(player.moonrise$getChunkLoader().getSentChunksRaw().clone()); + } + + public static Set getSentChunks(final ServerPlayer player) { +- final ObjectSet chunks = new ObjectOpenHashSet<>(); ++ final LongOpenHashSet rawChunkKeys = player.moonrise$getChunkLoader().getSentChunksRaw(); ++ final ObjectSet chunks = new ObjectOpenHashSet<>(rawChunkKeys.size()); + final World world = player.serverLevel().getWorld(); +- player.getChunkTrackingView().forEach(pos -> { - final org.bukkit.Chunk chunk = world.getChunkAt(pos.longKey); - chunks.add(chunk); - }); -+ -+ final it.unimi.dsi.fastutil.longs.LongIterator iter = this.getHandle().moonrise$getChunkLoader().getSentChunksRaw().longIterator(); -+ while (iter.hasNext()) chunks.add(world.getChunkAt(iter.nextLong(), false)); -+ - return it.unimi.dsi.fastutil.objects.ObjectSets.unmodifiable(chunks); ++ final LongIterator iter = player.moonrise$getChunkLoader().getSentChunksRaw().longIterator(); ++ while (iter.hasNext()) { ++ chunks.add(world.getChunkAt(iter.nextLong(), false)); ++ } + return ObjectSets.unmodifiable(chunks); } - @Override - public boolean isChunkSent(final long chunkKey) { - org.spigotmc.AsyncCatcher.catchOp("accessing sent chunks"); -- return this.getHandle().getChunkTrackingView().contains(new net.minecraft.world.level.ChunkPos(chunkKey)); -+ return this.getHandle().moonrise$getChunkLoader().getSentChunksRaw().contains(chunkKey); + public static boolean isChunkSent(final ServerPlayer player, final long chunkKey) { +- return player.getChunkTrackingView().contains(new ChunkPos(chunkKey)); ++ return player.moonrise$getChunkLoader().getSentChunksRaw().contains(chunkKey); } - // Paper end - + } diff --git a/patches/server/Improve-Maps-in-item-frames-performance-and-bug-fixe.patch b/patches/server/Improve-Maps-in-item-frames-performance-and-bug-fixe.patch index 11b55680e0..afafe8972f 100644 --- a/patches/server/Improve-Maps-in-item-frames-performance-and-bug-fixe.patch +++ b/patches/server/Improve-Maps-in-item-frames-performance-and-bug-fixe.patch @@ -122,16 +122,3 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 for (org.bukkit.map.MapCursor cursor : render.cursors) { if (cursor.isVisible()) { icons.add(new MapDecoration(CraftMapCursor.CraftType.bukkitToMinecraftHolder(cursor.getType()), cursor.getX(), cursor.getY(), cursor.getDirection(), Optional.ofNullable(PaperAdventure.asVanilla(cursor.caption())))); -diff --git a/src/main/java/org/bukkit/craftbukkit/map/RenderData.java b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/map/RenderData.java -+++ b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java -@@ -0,0 +0,0 @@ import org.bukkit.map.MapCursor; - - public class RenderData { - -- public final byte[] buffer; -+ public byte[] buffer; // Paper - public final ArrayList cursors; - - public RenderData() { diff --git a/patches/server/Improved-Watchdog-Support.patch b/patches/server/Improved-Watchdog-Support.patch index b8c641deb3..3ed20e43df 100644 --- a/patches/server/Improved-Watchdog-Support.patch +++ b/patches/server/Improved-Watchdog-Support.patch @@ -40,24 +40,6 @@ This is to ensure that if main isn't truely stuck, it's not manipulating state w This also moves all plugins who register "delayed init" tasks to occur just before "Done" so they are properly accounted for and wont trip watchdog on init. -diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/com/destroystokyo/paper/Metrics.java -+++ b/src/main/java/com/destroystokyo/paper/Metrics.java -@@ -0,0 +0,0 @@ public class Metrics { - * Starts the Scheduler which submits our data every 30 minutes. - */ - private void startSubmitting() { -- final Runnable submitTask = this::submitData; -+ final Runnable submitTask = () -> { -+ if (MinecraftServer.getServer().hasStopped()) { -+ return; -+ } -+ submitData(); -+ }; - - // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution of requests on the - // bStats backend. To circumvent this problem, we introduce some randomness into the initial and second delay. diff --git a/src/main/java/io/papermc/paper/util/LogManagerShutdownThread.java b/src/main/java/io/papermc/paper/util/LogManagerShutdownThread.java new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 diff --git a/patches/server/Moonrise-optimisation-patches.patch b/patches/server/Moonrise-optimisation-patches.patch index ad8c4e9d97..5373ef9f77 100644 --- a/patches/server/Moonrise-optimisation-patches.patch +++ b/patches/server/Moonrise-optimisation-patches.patch @@ -22763,19 +22763,32 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + private SaveUtil() {} +} -diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java +diff --git a/src/main/java/io/papermc/paper/FeatureHooks.java b/src/main/java/io/papermc/paper/FeatureHooks.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/io/papermc/paper/command/PaperCommand.java -+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java -@@ -0,0 +0,0 @@ public final class PaperCommand extends Command { - commands.put(Set.of("dumpitem"), new DumpItemCommand()); - commands.put(Set.of("mobcaps", "playermobcaps"), new MobcapsCommand()); - commands.put(Set.of("dumplisteners"), new DumpListenersCommand()); +--- a/src/main/java/io/papermc/paper/FeatureHooks.java ++++ b/src/main/java/io/papermc/paper/FeatureHooks.java +@@ -0,0 +0,0 @@ + package io.papermc.paper; + + import io.papermc.paper.command.PaperSubcommand; ++import io.papermc.paper.command.subcommands.ChunkDebugCommand; ++import io.papermc.paper.command.subcommands.FixLightCommand; + import it.unimi.dsi.fastutil.longs.LongOpenHashSet; + import it.unimi.dsi.fastutil.longs.LongSet; + import it.unimi.dsi.fastutil.longs.LongSets; +@@ -0,0 +0,0 @@ import org.bukkit.World; + public final class FeatureHooks { + + public static void initChunkTaskScheduler(final boolean useParallelGen) { ++ ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.init(useParallelGen); // Paper - Chunk system + } + + public static void registerPaperCommands(final Map, PaperSubcommand> commands) { + commands.put(Set.of("fixlight"), new FixLightCommand()); // Paper - rewrite chunk system + commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand()); // Paper - rewrite chunk system + } - return commands.entrySet().stream() - .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue()))) + public static LevelChunkSection createSection(final Registry biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) { diff --git a/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 @@ -23181,34 +23194,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + sender.getBukkitEntity().sendMessage(text().color(BLUE).append(text("Relighting "), text(pending[0], DARK_AQUA), text(" chunks"))); + } +} -diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java -+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java -@@ -0,0 +0,0 @@ public class GlobalConfiguration extends ConfigurationPart { - @PostProcess - private void postProcess() { - ca.spottedleaf.moonrise.common.util.MoonriseCommon.adjustWorkerThreads(this.workerThreads, this.ioThreads); -+ -+ String newChunkSystemGenParallelism = this.genParallelism; -+ if (newChunkSystemGenParallelism.equalsIgnoreCase("default")) { -+ newChunkSystemGenParallelism = "true"; -+ } -+ -+ boolean useParallelGen; -+ if (newChunkSystemGenParallelism.equalsIgnoreCase("on") || newChunkSystemGenParallelism.equalsIgnoreCase("enabled") -+ || newChunkSystemGenParallelism.equalsIgnoreCase("true")) { -+ useParallelGen = true; -+ } else if (newChunkSystemGenParallelism.equalsIgnoreCase("off") || newChunkSystemGenParallelism.equalsIgnoreCase("disabled") -+ || newChunkSystemGenParallelism.equalsIgnoreCase("false")) { -+ useParallelGen = false; -+ } else { -+ throw new IllegalStateException("Invalid option for gen-parallelism: must be one of [on, off, enabled, disabled, true, false, default]"); -+ } -+ ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.init(useParallelGen); - } - } - diff --git a/src/main/java/io/papermc/paper/threadedregions/TickRegions.java b/src/main/java/io/papermc/paper/threadedregions/TickRegions.java new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 @@ -36163,18 +36148,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 - if (playersInRange.isEmpty()) return; + if (playersInRange.isEmpty()) return true; // Paper - chunk system - // Paper start - Anti-Xray bypass - final Map refreshPackets = new HashMap<>(); -@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { - })); - // Paper end - Anti-Xray bypass - } + FeatureHooks.sendChunkRefreshPackets(playersInRange, chunk); - }); - }); +- + // Paper - chunk system - return true; } + @@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { @Override public Collection getPluginChunkTickets(int x, int z) { diff --git a/patches/server/Paper-Metrics.patch b/patches/server/Paper-Metrics.patch index 8fcd6a8977..6df3ae40a2 100644 --- a/patches/server/Paper-Metrics.patch +++ b/patches/server/Paper-Metrics.patch @@ -113,7 +113,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + * Starts the Scheduler which submits our data every 30 minutes. + */ + private void startSubmitting() { -+ final Runnable submitTask = this::submitData; ++ final Runnable submitTask = () -> { ++ if (!MinecraftServer.getServer().hasStopped()) { ++ submitData(); ++ } ++ }; + + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into the initial and second delay.