From a3bd0b2bbb7ab41986f98a9b88cfd589f17051cb Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Sat, 14 Dec 2024 15:20:06 -0500 Subject: [PATCH] net/minecraft/world/level/chunk/ --- .../world/level/chunk/ChunkAccess.java.patch | 69 ++- .../level/chunk/ChunkGenerator.java.patch | 148 +++++++ .../ChunkGeneratorStructureState.java.patch | 112 +++-- .../world/level/chunk/DataLayer.java.patch | 2 +- .../level/chunk/EmptyLevelChunk.java.patch | 2 +- .../level/chunk/HashMapPalette.java.patch | 30 ++ .../world/level/chunk/LevelChunk.java.patch | 307 +++++++++++++ .../level/chunk/LevelChunkSection.java.patch | 33 +- .../level/chunk/PalettedContainer.java.patch | 74 ++++ .../world/level/chunk/ProtoChunk.java.patch | 15 +- .../world/level/chunk/UpgradeData.java.patch | 19 +- .../level/chunk/ChunkGenerator.java.patch | 224 ---------- .../level/chunk/HashMapPalette.java.patch | 30 -- .../world/level/chunk/LevelChunk.java.patch | 409 ------------------ .../level/chunk/PalettedContainer.java.patch | 74 ---- 15 files changed, 673 insertions(+), 875 deletions(-) rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/chunk/ChunkAccess.java.patch (65%) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch (55%) rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/chunk/DataLayer.java.patch (92%) rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch (95%) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/chunk/HashMapPalette.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/chunk/LevelChunkSection.java.patch (60%) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainer.java.patch rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/chunk/ProtoChunk.java.patch (80%) rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/chunk/UpgradeData.java.patch (75%) delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkGenerator.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/chunk/HashMapPalette.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/chunk/LevelChunk.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/chunk/PalettedContainer.java.patch diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkAccess.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch similarity index 65% rename from paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkAccess.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch index 54c4c6342e..b70d49f1a0 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkAccess.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch @@ -1,43 +1,45 @@ --- a/net/minecraft/world/level/chunk/ChunkAccess.java +++ b/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -65,7 +65,7 @@ +@@ -64,7 +_,7 @@ protected final ShortList[] postProcessing; private volatile boolean unsaved; private volatile boolean isLightCorrect; - protected final ChunkPos chunkPos; + protected final ChunkPos chunkPos; public final long coordinateKey; public final int locX; public final int locZ; // Paper - cache coordinate key private long inhabitedTime; - /** @deprecated */ @Nullable -@@ -85,8 +85,14 @@ + @Deprecated +@@ -82,6 +_,11 @@ + public final Map blockEntities = new Object2ObjectOpenHashMap<>(); protected final LevelHeightAccessor levelHeightAccessor; protected final LevelChunkSection[] sections; - + // CraftBukkit start - SPIGOT-6814: move to IChunkAccess to account for 1.17 to 1.18 chunk upgrading. + private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); + public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY); + // CraftBukkit end -+ - public ChunkAccess(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor heightLimitView, Registry biomeRegistry, long inhabitedTime, @Nullable LevelChunkSection[] sectionArray, @Nullable BlendingData blendingData) { -- this.chunkPos = pos; -+ this.locX = pos.x; this.locZ = pos.z; // Paper - reduce need for field lookups -+ this.chunkPos = pos; this.coordinateKey = ChunkPos.asLong(locX, locZ); // Paper - cache long key ++ public final Registry biomeRegistry; // CraftBukkit + + public ChunkAccess( + ChunkPos chunkPos, +@@ -92,7 +_,8 @@ + @Nullable LevelChunkSection[] sections, + @Nullable BlendingData blendingData + ) { +- this.chunkPos = chunkPos; ++ this.locX = chunkPos.x; this.locZ = chunkPos.z; // Paper - reduce need for field lookups ++ this.chunkPos = chunkPos; this.coordinateKey = ChunkPos.asLong(locX, locZ); // Paper - cache long key this.upgradeData = upgradeData; - this.levelHeightAccessor = heightLimitView; - this.sections = new LevelChunkSection[heightLimitView.getSectionsCount()]; -@@ -103,7 +109,11 @@ + this.levelHeightAccessor = levelHeightAccessor; + this.sections = new LevelChunkSection[levelHeightAccessor.getSectionsCount()]; +@@ -109,6 +_,7 @@ } - ChunkAccess.replaceMissingSections(biomeRegistry, this.sections); -+ // CraftBukkit start -+ this.biomeRegistry = biomeRegistry; + replaceMissingSections(biomeRegistry, this.sections); ++ this.biomeRegistry = biomeRegistry; // Craftbukkit } -+ public final Registry biomeRegistry; -+ // CraftBukkit end - private static void replaceMissingSections(Registry biomeRegistry, LevelChunkSection[] sectionArray) { - for (int i = 0; i < sectionArray.length; ++i) { -@@ -275,6 +285,7 @@ + private static void replaceMissingSections(Registry biomeRegistry, LevelChunkSection[] sections) { +@@ -273,6 +_,7 @@ public boolean tryMarkSaved() { if (this.unsaved) { this.unsaved = false; @@ -45,7 +47,7 @@ return true; } else { return false; -@@ -282,7 +293,7 @@ +@@ -280,7 +_,7 @@ } public boolean isUnsaved() { @@ -54,15 +56,10 @@ } public abstract ChunkStatus getPersistedStatus(); -@@ -458,10 +469,31 @@ - - crashreportsystemdetails.setDetail("Location", () -> { - return CrashReportCategory.formatLocation(this, biomeX, biomeY, biomeZ); -+ }); -+ throw new ReportedException(crashreport); -+ } -+ } -+ +@@ -446,6 +_,26 @@ + throw new ReportedException(crashReport); + } + } + // CraftBukkit start + public void setBiome(int i, int j, int k, Holder biome) { + try { @@ -78,11 +75,11 @@ + + crashreportsystemdetails.setDetail("Location", () -> { + return CrashReportCategory.formatLocation(this, i, j, k); - }); - throw new ReportedException(crashreport); - } - } ++ }); ++ throw new ReportedException(crashreport); ++ } ++ } + // CraftBukkit end - public void fillBiomesFromNoise(BiomeResolver biomeSupplier, Climate.Sampler sampler) { - ChunkPos chunkcoordintpair = this.getPos(); + public void fillBiomesFromNoise(BiomeResolver resolver, Climate.Sampler sampler) { + ChunkPos pos = this.getPos(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch new file mode 100644 index 0000000000..bd04398695 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch @@ -0,0 +1,148 @@ +--- a/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -104,8 +_,8 @@ + + protected abstract MapCodec codec(); + +- public ChunkGeneratorStructureState createState(HolderLookup structureSetLookup, RandomState randomState, long seed) { +- return ChunkGeneratorStructureState.createForNormal(randomState, seed, this.biomeSource, structureSetLookup); ++ public ChunkGeneratorStructureState createState(HolderLookup structureSetLookup, RandomState randomState, long seed, org.spigotmc.SpigotWorldConfig conf) { // Spigot ++ return ChunkGeneratorStructureState.createForNormal(randomState, seed, this.biomeSource, structureSetLookup, conf); // Spigot + } + + public Optional>> getTypeNameForDataFixer() { +@@ -127,6 +_,24 @@ + public Pair> findNearestMapStructure( + ServerLevel level, HolderSet structure, BlockPos pos, int searchRadius, boolean skipKnownStructures + ) { ++ // Paper start - StructuresLocateEvent ++ final org.bukkit.World bukkitWorld = level.getWorld(); ++ final org.bukkit.Location origin = io.papermc.paper.util.MCUtil.toLocation(level, pos); ++ final List apiStructures = structure.stream().map(Holder::value).map(nms -> org.bukkit.craftbukkit.generator.structure.CraftStructure.minecraftToBukkit(nms)).toList(); ++ if (!apiStructures.isEmpty()) { ++ final io.papermc.paper.event.world.StructuresLocateEvent event = new io.papermc.paper.event.world.StructuresLocateEvent(bukkitWorld, origin, apiStructures, searchRadius, skipKnownStructures); ++ if (!event.callEvent()) { ++ return null; ++ } ++ if (event.getResult() != null) { ++ return Pair.of(io.papermc.paper.util.MCUtil.toBlockPos(event.getResult().pos()), level.registryAccess().lookupOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(event.getResult().structure()))); ++ } ++ pos = io.papermc.paper.util.MCUtil.toBlockPosition(event.getOrigin()); ++ searchRadius = event.getRadius(); ++ skipKnownStructures = event.shouldFindUnexplored(); ++ structure = HolderSet.direct(api -> level.registryAccess().lookupOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(api)), event.getStructures()); ++ } ++ // Paper end + ChunkGeneratorStructureState generatorState = level.getChunkSource().getGeneratorState(); + Map>> map = new Object2ObjectArrayMap<>(); + +@@ -222,6 +_,7 @@ + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + + for (ChunkPos chunkPos : ringPositionsFor) { ++ if (!level.paperConfig().environment.locateStructuresOutsideWorldBorder && !level.getWorldBorder().isChunkInBounds(chunkPos.x, chunkPos.z)) { continue; } // Paper - Bound treasure maps to world border + mutableBlockPos.set(SectionPos.sectionToBlockCoord(chunkPos.x, 8), 32, SectionPos.sectionToBlockCoord(chunkPos.z, 8)); + double d1 = mutableBlockPos.distSqr(pos); + boolean flag = pair == null || d1 < d; +@@ -255,11 +_,15 @@ + int spacing = spreadPlacement.spacing(); + + for (int i = -z; i <= z; i++) { +- boolean flag = i == -z || i == z; ++ // Paper start - Perf: iterate over border chunks instead of entire square chunk area ++ final int radius = z; ++ boolean flag = i == -z || i == z; final boolean onBorderAlongZAxis = flag; // Paper - OBFHELPER + +- for (int i1 = -z; i1 <= z; i1++) { +- boolean flag1 = i1 == -z || i1 == z; +- if (flag || flag1) { ++ for (int i1 = -radius; i1 <= radius; i1 += onBorderAlongZAxis ? 1 : radius * 2) { ++ // boolean flag1 = i1 == -z || i1 == z; ++ // if (flag || flag1) { ++ if (true) { ++ // Paper end - Perf: iterate over border chunks instead of entire square chunk area + int i2 = x + spacing * i; + int i3 = y + spacing * i1; + ChunkPos potentialStructureChunk = spreadPlacement.getPotentialStructureChunk(seed, i2, i3); +@@ -312,7 +_,7 @@ + } + } + +- public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { ++ public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { // CraftBukkit - rename + ChunkPos pos = chunk.getPos(); + if (!SharedConstants.debugVoidTerrain(pos)) { + SectionPos sectionPos = SectionPos.of(pos, level.getMinSectionY()); +@@ -385,7 +_,14 @@ + int i3 = ints[i2]; + PlacedFeature placedFeature = stepFeatureData1.features().get(i3); + Supplier supplier1 = () -> registry1.getResourceKey(placedFeature).map(Object::toString).orElseGet(placedFeature::toString); +- worldgenRandom.setFeatureSeed(l, i3, i); ++ // Paper start - Configurable feature seeds; change populationSeed used in random ++ long featurePopulationSeed = i; ++ final long configFeatureSeed = level.getMinecraftWorld().paperConfig().featureSeeds.features.getLong(placedFeature.feature()); ++ if (configFeatureSeed != -1) { ++ featurePopulationSeed = worldgenRandom.setDecorationSeed(configFeatureSeed, blockPos.getX(), blockPos.getZ()); // See seededrandom.setDecorationSeed from above ++ } ++ worldgenRandom.setFeatureSeed(featurePopulationSeed, i3, i); ++ // Paper end - Configurable feature seeds + + try { + level.setCurrentlyGenerating(supplier1); +@@ -407,6 +_,32 @@ + } + } + } ++ // CraftBukkit start ++ public void applyBiomeDecoration(WorldGenLevel world, ChunkAccess chunk, StructureManager structureAccessor) { ++ this.applyBiomeDecoration(world, chunk, structureAccessor, true); ++ } ++ ++ public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) { ++ if (vanilla) { ++ this.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); ++ } ++ ++ org.bukkit.World world = generatoraccessseed.getMinecraftWorld().getWorld(); ++ // only call when a populator is present (prevents unnecessary entity conversion) ++ if (!world.getPopulators().isEmpty()) { ++ org.bukkit.craftbukkit.generator.CraftLimitedRegion limitedRegion = new org.bukkit.craftbukkit.generator.CraftLimitedRegion(generatoraccessseed, ichunkaccess.getPos()); ++ int x = ichunkaccess.getPos().x; ++ int z = ichunkaccess.getPos().z; ++ for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { ++ WorldgenRandom seededrandom = new WorldgenRandom(new net.minecraft.world.level.levelgen.LegacyRandomSource(generatoraccessseed.getSeed())); ++ seededrandom.setDecorationSeed(generatoraccessseed.getSeed(), x, z); ++ populator.populate(world, new org.bukkit.craftbukkit.util.RandomSourceWrapper.RandomWrapper(seededrandom), x, z, limitedRegion); ++ } ++ limitedRegion.saveEntities(); ++ limitedRegion.breakLink(); ++ } ++ } ++ // CraftBukkit end + + private static BoundingBox getWritableArea(ChunkAccess chunk) { + ChunkPos pos = chunk.getPos(); +@@ -483,7 +_,7 @@ + } + } + +- if (structurePlacement.isStructureChunk(structureState, pos.x, pos.z)) { ++ if (structurePlacement.isStructureChunk(structureState, pos.x, pos.z, structurePlacement instanceof net.minecraft.world.level.chunk.ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs + if (list.size() == 1) { + this.tryGenerateStructure( + list.get(0), +@@ -577,6 +_,14 @@ + predicate + ); + if (structureStart.isValid()) { ++ // CraftBukkit start ++ BoundingBox box = structureStart.getBoundingBox(); ++ org.bukkit.event.world.AsyncStructureSpawnEvent event = new org.bukkit.event.world.AsyncStructureSpawnEvent(structureStart.level.getMinecraftWorld().getWorld(), org.bukkit.craftbukkit.generator.structure.CraftStructure.minecraftToBukkit(structure), new org.bukkit.util.BoundingBox(box.minX(), box.minY(), box.minZ(), box.maxX(), box.maxY(), box.maxZ()), chunkPos.x, chunkPos.z); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return true; ++ } ++ // CraftBukkit end + structureManager.setStartForStructure(sectionPos, structure, structureStart, chunk); + return true; + } else { diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch similarity index 55% rename from paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch index f9ebf0e3b1..0b9890be26 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch @@ -1,53 +1,38 @@ --- a/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java +++ b/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java -@@ -1,3 +1,4 @@ +@@ -1,3 +_,4 @@ +// mc-dev import package net.minecraft.world.level.chunk; import com.google.common.base.Stopwatch; -@@ -33,6 +34,11 @@ - import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - import org.slf4j.Logger; - -+// Spigot start -+import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; -+import org.spigotmc.SpigotWorldConfig; -+// Spigot end -+ - public class ChunkGeneratorStructureState { - - private static final Logger LOGGER = LogUtils.getLogger(); -@@ -44,22 +50,109 @@ - private final Map>> ringPositions = new Object2ObjectArrayMap(); +@@ -41,22 +_,109 @@ + private final Map>> ringPositions = new Object2ObjectArrayMap<>(); private boolean hasGeneratedPositions; private final List> possibleStructureSets; -+ public final SpigotWorldConfig conf; // Paper - Add missing structure set seed configs ++ public final org.spigotmc.SpigotWorldConfig conf; // Paper - Add missing structure set seed configs -- public static ChunkGeneratorStructureState createForFlat(RandomState noiseConfig, long seed, BiomeSource biomeSource, Stream> structureSets) { -- List> list = structureSets.filter((holder) -> { -- return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder.value(), biomeSource); -+ public static ChunkGeneratorStructureState createForFlat(RandomState randomstate, long i, BiomeSource worldchunkmanager, Stream> stream, SpigotWorldConfig conf) { // Spigot -+ List> list = stream.filter((holder) -> { -+ return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder.value(), worldchunkmanager); - }).toList(); - -- return new ChunkGeneratorStructureState(noiseConfig, biomeSource, seed, 0L, list); -+ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot + public static ChunkGeneratorStructureState createForFlat( +- RandomState randomState, long levelSeed, BiomeSource biomeSource, Stream> structureSets ++ RandomState randomState, long levelSeed, BiomeSource biomeSource, Stream> structureSets, org.spigotmc.SpigotWorldConfig conf // Spigot + ) { + List> list = structureSets.filter(structureSet -> hasBiomesForStructureSet(structureSet.value(), biomeSource)).toList(); +- return new ChunkGeneratorStructureState(randomState, biomeSource, levelSeed, 0L, list); ++ return new ChunkGeneratorStructureState(randomState, biomeSource, levelSeed, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot } -- public static ChunkGeneratorStructureState createForNormal(RandomState noiseConfig, long seed, BiomeSource biomeSource, HolderLookup structureSetRegistry) { -- List> list = (List) structureSetRegistry.listElements().filter((holder_c) -> { -- return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder_c.value(), biomeSource); -+ public static ChunkGeneratorStructureState createForNormal(RandomState randomstate, long i, BiomeSource worldchunkmanager, HolderLookup holderlookup, SpigotWorldConfig conf) { // Spigot -+ List> list = (List) holderlookup.listElements().filter((holder_c) -> { -+ return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder_c.value(), worldchunkmanager); - }).collect(Collectors.toUnmodifiableList()); - -- return new ChunkGeneratorStructureState(noiseConfig, biomeSource, seed, seed, list); -+ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, i, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot + public static ChunkGeneratorStructureState createForNormal( +- RandomState randomState, long seed, BiomeSource biomeSource, HolderLookup structureSetLookup ++ RandomState randomState, long seed, BiomeSource biomeSource, HolderLookup structureSetLookup, org.spigotmc.SpigotWorldConfig conf // Spigot + ) { + List> list = structureSetLookup.listElements() + .filter(structureSet -> hasBiomesForStructureSet(structureSet.value(), biomeSource)) + .collect(Collectors.toUnmodifiableList()); +- return new ChunkGeneratorStructureState(randomState, biomeSource, seed, seed, list); +- } ++ return new ChunkGeneratorStructureState(randomState, biomeSource, seed, seed, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot + } + // Paper start - Add missing structure set seed configs; horrible hack because spigot creates a ton of direct Holders which lose track of the identifying key -+ public static final class KeyedRandomSpreadStructurePlacement extends RandomSpreadStructurePlacement { ++ public static final class KeyedRandomSpreadStructurePlacement extends net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement { + public final net.minecraft.resources.ResourceKey key; + public KeyedRandomSpreadStructurePlacement(net.minecraft.resources.ResourceKey key, net.minecraft.core.Vec3i locateOffset, FrequencyReductionMethod frequencyReductionMethod, float frequency, int salt, java.util.Optional exclusionZone, int spacing, int separation, net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType spreadType) { + super(locateOffset, frequencyReductionMethod, frequency, salt, exclusionZone, spacing, separation, spreadType); @@ -57,11 +42,11 @@ + // Paper end - Add missing structure set seed configs + + // Spigot start -+ private static List> injectSpigot(List> list, SpigotWorldConfig conf) { ++ private static List> injectSpigot(List> list, org.spigotmc.SpigotWorldConfig conf) { + return list.stream().map((holder) -> { + StructureSet structureset = holder.value(); + final Holder newHolder; // Paper - Add missing structure set seed configs -+ if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig && holder.unwrapKey().orElseThrow().location().getNamespace().equals(net.minecraft.resources.ResourceLocation.DEFAULT_NAMESPACE)) { // Paper - Add missing structure set seed configs; check namespace cause datapacks could add structure sets with the same path ++ if (structureset.placement() instanceof net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement randomConfig && holder.unwrapKey().orElseThrow().location().getNamespace().equals(net.minecraft.resources.ResourceLocation.DEFAULT_NAMESPACE)) { // Paper - Add missing structure set seed configs; check namespace cause datapacks could add structure sets with the same path + String name = holder.unwrapKey().orElseThrow().location().getPath(); + int seed = randomConfig.salt; + @@ -130,46 +115,47 @@ + return newHolder; + // Paper end - Add missing structure set seed configs + }).collect(Collectors.toUnmodifiableList()); - } ++ } + // Spigot end private static boolean hasBiomesForStructureSet(StructureSet structureSet, BiomeSource biomeSource) { - Stream> stream = structureSet.structures().stream().flatMap((structureset_a) -> { -@@ -73,12 +166,13 @@ - return stream.anyMatch(set::contains); + Stream> stream = structureSet.structures().stream().flatMap(structureEntry -> { +@@ -67,13 +_,14 @@ } -- private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List> structureSets) { -+ private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List> structureSets, SpigotWorldConfig conf) { // Paper - Add missing structure set seed configs - this.randomState = noiseConfig; - this.levelSeed = structureSeed; + private ChunkGeneratorStructureState( +- RandomState randomState, BiomeSource biomeSource, long levelSeed, long cocentricRingsSeed, List> possibleStructureSets ++ RandomState randomState, BiomeSource biomeSource, long levelSeed, long cocentricRingsSeed, List> possibleStructureSets, org.spigotmc.SpigotWorldConfig conf // Paper - Add missing structure set seed configs + ) { + this.randomState = randomState; + this.levelSeed = levelSeed; this.biomeSource = biomeSource; - this.concentricRingsSeed = concentricRingSeed; - this.possibleStructureSets = structureSets; + this.concentricRingsSeed = cocentricRingsSeed; + this.possibleStructureSets = possibleStructureSets; + this.conf = conf; // Paper - Add missing structure set seed configs } public List> possibleStructureSets() { -@@ -132,7 +226,13 @@ - HolderSet holderset = placement.preferredBiomes(); - RandomSource randomsource = RandomSource.create(); - +@@ -118,7 +_,13 @@ + int spread = placement.spread(); + HolderSet holderSet = placement.preferredBiomes(); + RandomSource randomSource = RandomSource.create(); + // Paper start - Add missing structure set seed configs -+ if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { -+ randomsource.setSeed(this.conf.strongholdSeed); ++ if (this.conf.strongholdSeed != null && structureSet.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { ++ randomSource.setSeed(this.conf.strongholdSeed); + } else { + // Paper end - Add missing structure set seed configs - randomsource.setSeed(this.concentricRingsSeed); + randomSource.setSeed(this.concentricRingsSeed); + } // Paper - Add missing structure set seed configs - double d0 = randomsource.nextDouble() * Math.PI * 2.0D; - int l = 0; + double d = randomSource.nextDouble() * Math.PI * 2.0; + int i = 0; int i1 = 0; -@@ -209,7 +309,7 @@ +@@ -197,7 +_,7 @@ - for (int l = centerChunkX - chunkCount; l <= centerChunkX + chunkCount; ++l) { - for (int i1 = centerChunkZ - chunkCount; i1 <= centerChunkZ + chunkCount; ++i1) { -- if (structureplacement.isStructureChunk(this, l, i1)) { -+ if (structureplacement.isStructureChunk(this, l, i1, structureplacement instanceof KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs + for (int i = x - range; i <= x + range; i++) { + for (int i1 = z - range; i1 <= z + range; i1++) { +- if (structurePlacement.isStructureChunk(this, i, i1)) { ++ if (structurePlacement.isStructureChunk(this, i, i1, structurePlacement instanceof KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs return true; } } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/DataLayer.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/DataLayer.java.patch similarity index 92% rename from paper-server/patches/unapplied/net/minecraft/world/level/chunk/DataLayer.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/chunk/DataLayer.java.patch index e1be97122d..f3ce378056 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/DataLayer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/DataLayer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/DataLayer.java +++ b/net/minecraft/world/level/chunk/DataLayer.java -@@ -1,3 +1,4 @@ +@@ -1,3 +_,4 @@ +// mc-dev import package net.minecraft.world.level.chunk; diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch similarity index 95% rename from paper-server/patches/unapplied/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch index 1ccf6729a6..a67013f5ff 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/EmptyLevelChunk.java +++ b/net/minecraft/world/level/chunk/EmptyLevelChunk.java -@@ -25,6 +25,12 @@ +@@ -25,6 +_,12 @@ public BlockState getBlockState(BlockPos pos) { return Blocks.VOID_AIR.defaultBlockState(); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/HashMapPalette.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/HashMapPalette.java.patch new file mode 100644 index 0000000000..df3071147b --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/HashMapPalette.java.patch @@ -0,0 +1,30 @@ +--- a/net/minecraft/world/level/chunk/HashMapPalette.java ++++ b/net/minecraft/world/level/chunk/HashMapPalette.java +@@ -20,7 +_,7 @@ + } + + public HashMapPalette(IdMap registry, int bits, PaletteResize resizeHandler) { +- this(registry, bits, resizeHandler, CrudeIncrementalIntIdentityHashBiMap.create(1 << bits)); ++ this(registry, bits, resizeHandler, CrudeIncrementalIntIdentityHashBiMap.create((1 << bits) + 1)); // Paper - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap + } + + private HashMapPalette(IdMap registry, int bits, PaletteResize resizeHandler, CrudeIncrementalIntIdentityHashBiMap values) { +@@ -38,10 +_,16 @@ + public int idFor(T state) { + int id = this.values.getId(state); + if (id == -1) { +- id = this.values.add(state); +- if (id >= 1 << this.bits) { ++ // Paper start - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize ++ // We use size() instead of the result from add(K) ++ // This avoids adding another object unnecessarily ++ // Without this change, + 2 would be required in the constructor ++ if (this.values.size() >= 1 << this.bits) { + id = this.resizeHandler.onResize(this.bits + 1, state); ++ } else { ++ id = this.values.add(state); + } ++ // Paper end - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize + } + + return id; diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch new file mode 100644 index 0000000000..7d3779bd7c --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch @@ -0,0 +1,307 @@ +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -76,7 +_,7 @@ + }; + private final Map tickersInLevel = Maps.newHashMap(); + public boolean loaded; +- public final Level level; ++ public final ServerLevel level; // CraftBukkit - type + @Nullable + private Supplier fullStatus; + @Nullable +@@ -85,6 +_,14 @@ + private final LevelChunkTicks blockTicks; + private final LevelChunkTicks fluidTicks; + private LevelChunk.UnsavedListener unsavedListener = chunkPos -> {}; ++ // CraftBukkit start ++ public boolean mustNotSave; ++ public boolean needsDecoration; ++ // CraftBukkit end ++ ++ // Paper start ++ boolean loadedTicketLevel; ++ // Paper end + + public LevelChunk(Level level, ChunkPos pos) { + this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null); +@@ -102,7 +_,7 @@ + @Nullable BlendingData blendingData + ) { + super(pos, data, level, level.registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); +- this.level = level; ++ this.level = (net.minecraft.server.level.ServerLevel) level; // CraftBukkit - type + this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>(); + + for (Heightmap.Types types : Heightmap.Types.values()) { +@@ -154,6 +_,10 @@ + this.skyLightSources = chunk.skyLightSources; + this.setLightCorrect(chunk.isLightCorrect()); + this.markUnsaved(); ++ this.needsDecoration = true; // CraftBukkit ++ // CraftBukkit start ++ this.persistentDataContainer = chunk.persistentDataContainer; // SPIGOT-6814: copy PDC to account for 1.17 to 1.18 chunk upgrading. ++ // CraftBukkit end + } + + public void setUnsavedListener(LevelChunk.UnsavedListener unsavedListener) { +@@ -162,6 +_,12 @@ + unsavedListener.setUnsaved(this.chunkPos); + } + } ++ // Paper start ++ @Override ++ public long getInhabitedTime() { ++ return this.level.paperConfig().chunks.fixedChunkInhabitedTime < 0 ? super.getInhabitedTime() : this.level.paperConfig().chunks.fixedChunkInhabitedTime; ++ } ++ // Paper end + + @Override + public void markUnsaved() { +@@ -195,8 +_,25 @@ + : super.getListenerRegistry(sectionY); + } + ++ // Paper start - Perf: Reduce instructions and provide final method ++ public BlockState getBlockState(final int x, final int y, final int z) { ++ return this.getBlockStateFinal(x, y, z); ++ } ++ public BlockState getBlockStateFinal(final int x, final int y, final int z) { ++ // Copied and modified from below ++ final int sectionIndex = this.getSectionIndex(y); ++ if (sectionIndex < 0 || sectionIndex >= this.sections.length ++ || this.sections[sectionIndex].nonEmptyBlockCount == 0) { ++ return Blocks.AIR.defaultBlockState(); ++ } ++ return this.sections[sectionIndex].states.get((y & 15) << 8 | (z & 15) << 4 | x & 15); ++ } + @Override + public BlockState getBlockState(BlockPos pos) { ++ if (true) { ++ return this.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); ++ } ++ // Paper end - Perf: Reduce instructions and provide final method + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); +@@ -231,33 +_,54 @@ + } + } + ++ // Paper start - If loaded util ++ @Override ++ public final FluidState getFluidIfLoaded(BlockPos blockposition) { ++ return this.getFluidState(blockposition); ++ } ++ ++ @Override ++ public final BlockState getBlockStateIfLoaded(BlockPos blockposition) { ++ return this.getBlockState(blockposition); ++ } ++ // Paper end ++ + @Override + public FluidState getFluidState(BlockPos pos) { + return this.getFluidState(pos.getX(), pos.getY(), pos.getZ()); + } + + public FluidState getFluidState(int x, int y, int z) { +- try { ++ // try { // Paper start - Perf: Optimise Chunk#getFluid + int sectionIndex = this.getSectionIndex(y); + if (sectionIndex >= 0 && sectionIndex < this.sections.length) { + LevelChunkSection levelChunkSection = this.sections[sectionIndex]; + if (!levelChunkSection.hasOnlyAir()) { +- return levelChunkSection.getFluidState(x & 15, y & 15, z & 15); ++ return levelChunkSection.states.get((y & 15) << 8 | (z & 15) << 4 | x & 15).getFluidState(); // Paper - Perf: Optimise Chunk#getFluid + } + } + + return Fluids.EMPTY.defaultFluidState(); ++ /* // Paper - Perf: Optimise Chunk#getFluid + } catch (Throwable var7) { + CrashReport crashReport = CrashReport.forThrowable(var7, "Getting fluid state"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Block being got"); + crashReportCategory.setDetail("Location", () -> CrashReportCategory.formatLocation(this, x, y, z)); + throw new ReportedException(crashReport); + } ++ */ // Paper - Perf: Optimise Chunk#getFluid + } + + @Nullable + @Override + public BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving) { ++// CraftBukkit start ++ return this.setBlockState(pos, state, isMoving, true); ++ } ++ ++ @Nullable ++ public BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving, boolean doPlace) { ++ // CraftBukkit end + int y = pos.getY(); + LevelChunkSection section = this.getSection(this.getSectionIndex(y)); + boolean hasOnlyAir = section.hasOnlyAir(); +@@ -292,7 +_,7 @@ + } + + boolean hasBlockEntity = blockState.hasBlockEntity(); +- if (!this.level.isClientSide) { ++ if (!this.level.isClientSide && !this.level.isBlockPlaceCancelled) { // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent + blockState.onRemove(this.level, pos, state, isMoving); + } else if (!blockState.is(block) && hasBlockEntity) { + this.removeBlockEntity(pos); +@@ -301,7 +_,7 @@ + if (!section.getBlockState(i, i1, i2).is(block)) { + return null; + } else { +- if (!this.level.isClientSide) { ++ if (!this.level.isClientSide && doPlace && (!this.level.captureBlockStates || block instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled. + state.onPlace(this.level, pos, blockState, isMoving); + } + +@@ -355,7 +_,12 @@ + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos, LevelChunk.EntityCreationType creationType) { +- BlockEntity blockEntity = this.blockEntities.get(pos); ++ // CraftBukkit start ++ BlockEntity blockEntity = this.level.capturedTileEntities.get(pos); ++ if (blockEntity == null) { ++ blockEntity = this.blockEntities.get(pos); ++ } ++ // CraftBukkit end + if (blockEntity == null) { + CompoundTag compoundTag = this.pendingBlockEntities.remove(pos); + if (compoundTag != null) { +@@ -409,7 +_,13 @@ + BlockPos blockPos = blockEntity.getBlockPos(); + BlockState blockState = this.getBlockState(blockPos); + if (!blockState.hasBlockEntity()) { +- LOGGER.warn("Trying to set block entity {} at position {}, but state {} does not allow it", blockEntity, blockPos, blockState); ++ // Paper start - ServerExceptionEvent ++ com.destroystokyo.paper.exception.ServerInternalException e = new com.destroystokyo.paper.exception.ServerInternalException( ++ "Trying to set block entity %s at position %s, but state %s does not allow it".formatted(blockEntity, blockPos, blockState) ++ ); ++ e.printStackTrace(); ++ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(e); ++ // Paper end - ServerExceptionEvent + } else { + BlockState blockState1 = blockEntity.getBlockState(); + if (blockState != blockState1) { +@@ -457,6 +_,11 @@ + public void removeBlockEntity(BlockPos pos) { + if (this.isInLevel()) { + BlockEntity blockEntity = this.blockEntities.remove(pos); ++ // CraftBukkit start - SPIGOT-5561: Also remove from pending map ++ if (!this.pendingBlockEntities.isEmpty()) { ++ this.pendingBlockEntities.remove(pos); ++ } ++ // CraftBukkit end + if (blockEntity != null) { + if (this.level instanceof ServerLevel serverLevel) { + this.removeGameEventListener(blockEntity, serverLevel); +@@ -499,6 +_,65 @@ + } + } + ++ // CraftBukkit start ++ public void loadCallback() { ++ // Paper start ++ this.loadedTicketLevel = true; ++ // Paper end ++ org.bukkit.Server server = this.level.getCraftServer(); ++ this.level.getChunkSource().addLoadedChunk(this); // Paper ++ if (server != null) { ++ /* ++ * If it's a new world, the first few chunks are generated inside ++ * the World constructor. We can't reliably alter that, so we have ++ * no way of creating a CraftWorld/CraftServer at that point. ++ */ ++ org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); ++ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(bukkitChunk, this.needsDecoration)); ++ ++ if (this.needsDecoration) { ++ this.needsDecoration = false; ++ java.util.Random random = new java.util.Random(); ++ random.setSeed(this.level.getSeed()); ++ long xRand = random.nextLong() / 2L * 2L + 1L; ++ long zRand = random.nextLong() / 2L * 2L + 1L; ++ random.setSeed((long) this.chunkPos.x * xRand + (long) this.chunkPos.z * zRand ^ this.level.getSeed()); ++ ++ org.bukkit.World world = this.level.getWorld(); ++ if (world != null) { ++ this.level.populating = true; ++ try { ++ for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { ++ populator.populate(world, random, bukkitChunk); ++ } ++ } finally { ++ this.level.populating = false; ++ } ++ } ++ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(bukkitChunk)); ++ } ++ } ++ } ++ ++ public void unloadCallback() { ++ org.bukkit.Server server = this.level.getCraftServer(); ++ org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); ++ org.bukkit.event.world.ChunkUnloadEvent unloadEvent = new org.bukkit.event.world.ChunkUnloadEvent(bukkitChunk, this.isUnsaved()); ++ server.getPluginManager().callEvent(unloadEvent); ++ // note: saving can be prevented, but not forced if no saving is actually required ++ this.mustNotSave = !unloadEvent.isSaveChunk(); ++ this.level.getChunkSource().removeLoadedChunk(this); // Paper ++ // Paper start ++ this.loadedTicketLevel = false; ++ // Paper end ++ } ++ ++ @Override ++ public boolean isUnsaved() { ++ return super.isUnsaved() && !this.mustNotSave; ++ } ++ // CraftBukkit end ++ + public boolean isEmpty() { + return false; + } +@@ -711,23 +_,25 @@ + if (this.blockEntity.getType().isValid(blockState)) { + this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), blockState, this.blockEntity); + this.loggedInvalidBlockState = false; +- } else if (!this.loggedInvalidBlockState) { +- this.loggedInvalidBlockState = true; +- LevelChunk.LOGGER +- .warn( +- "Block entity {} @ {} state {} invalid for ticking:", +- LogUtils.defer(this::getType), +- LogUtils.defer(this::getPos), +- blockState +- ); +- } ++ // Paper start - Remove the Block Entity if it's invalid ++ } else { ++ LevelChunk.this.removeBlockEntity(this.getPos()); ++ if (!this.loggedInvalidBlockState) { ++ this.loggedInvalidBlockState = true; ++ LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), blockState}); ++ } ++ // Paper end - Remove the Block Entity if it's invalid ++ } ++ + + profilerFiller.pop(); + } catch (Throwable var5) { +- CrashReport crashReport = CrashReport.forThrowable(var5, "Ticking block entity"); +- CrashReportCategory crashReportCategory = crashReport.addCategory("Block entity being ticked"); +- this.blockEntity.fillCrashReportCategory(crashReportCategory); +- throw new ReportedException(crashReport); ++ // Paper start - Prevent block entity and entity crashes ++ final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ()); ++ net.minecraft.server.MinecraftServer.LOGGER.error(msg, var5); ++ net.minecraft.world.level.chunk.LevelChunk.this.level.getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, var5))); // Paper - ServerExceptionEvent ++ LevelChunk.this.removeBlockEntity(this.getPos()); ++ // Paper end - Prevent block entity and entity crashes + } + } + } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/LevelChunkSection.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch similarity index 60% rename from paper-server/patches/unapplied/net/minecraft/world/level/chunk/LevelChunkSection.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch index f6007d4aea..f4a6ffa6fb 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/LevelChunkSection.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -19,11 +19,11 @@ +@@ -18,11 +_,11 @@ public static final int SECTION_HEIGHT = 16; public static final int SECTION_SIZE = 4096; public static final int BIOME_CONTAINER_BITS = 2; @@ -14,38 +14,33 @@ private LevelChunkSection(LevelChunkSection section) { this.nonEmptyBlockCount = section.nonEmptyBlockCount; -@@ -33,9 +33,9 @@ +@@ -32,7 +_,7 @@ this.biomes = section.biomes.copy(); } -- public LevelChunkSection(PalettedContainer blockStateContainer, PalettedContainerRO> biomeContainer) { -- this.states = blockStateContainer; -- this.biomes = biomeContainer; -+ public LevelChunkSection(PalettedContainer datapaletteblock, PalettedContainer> palettedcontainerro) { // CraftBukkit - read/write -+ this.states = datapaletteblock; -+ this.biomes = palettedcontainerro; +- public LevelChunkSection(PalettedContainer states, PalettedContainerRO> biomes) { ++ public LevelChunkSection(PalettedContainer states, PalettedContainer> biomes) { // CraftBukkit - read/write + this.states = states; + this.biomes = biomes; this.recalcBlockCounts(); - } - -@@ -49,7 +49,7 @@ +@@ -48,7 +_,7 @@ } public FluidState getFluidState(int x, int y, int z) { -- return ((BlockState) this.states.get(x, y, z)).getFluidState(); +- return this.states.get(x, y, z).getFluidState(); + return this.states.get(x, y, z).getFluidState(); // Paper - Perf: Optimise Chunk#getFluid; diff on change - we expect this to be effectively just getType(x, y, z).getFluid(). If this changes we need to check other patches that use IBlockData#getFluid. } public void acquire() { -@@ -196,6 +196,12 @@ - return (Holder) this.biomes.get(x, y, z); +@@ -185,6 +_,11 @@ + public Holder getNoiseBiome(int x, int y, int z) { + return this.biomes.get(x, y, z); } - + // CraftBukkit start + public void setBiome(int i, int j, int k, Holder biome) { + this.biomes.set(i, j, k, biome); + } + // CraftBukkit end -+ - public void fillBiomesFromNoise(BiomeResolver biomeSupplier, Climate.Sampler sampler, int x, int y, int z) { - PalettedContainer> datapaletteblock = this.biomes.recreate(); - boolean flag = true; + + public void fillBiomesFromNoise(BiomeResolver biomeResolver, Climate.Sampler climateSampler, int x, int y, int z) { + PalettedContainer> palettedContainer = this.biomes.recreate(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainer.java.patch new file mode 100644 index 0000000000..360faa2856 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainer.java.patch @@ -0,0 +1,74 @@ +--- a/net/minecraft/world/level/chunk/PalettedContainer.java ++++ b/net/minecraft/world/level/chunk/PalettedContainer.java +@@ -30,14 +_,14 @@ + public final IdMap registry; + private volatile PalettedContainer.Data data; + private final PalettedContainer.Strategy strategy; +- private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); ++ //private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused + + public void acquire() { +- this.threadingDetector.checkAndLock(); ++ // this.threadingDetector.checkAndLock(); // Paper - disable this - use proper synchronization + } + + public void release() { +- this.threadingDetector.checkAndUnlock(); ++ // this.threadingDetector.checkAndUnlock(); // Paper - disable this - use proper synchronization + } + + public static Codec> codecRW(IdMap registry, Codec codec, PalettedContainer.Strategy strategy, T value) { +@@ -99,7 +_,7 @@ + } + + @Override +- public int onResize(int bits, T objectAdded) { ++ public synchronized int onResize(int bits, T objectAdded) { // Paper - synchronize + PalettedContainer.Data data = this.data; + PalettedContainer.Data data1 = this.createOrReuseData(data, bits); + data1.copyFrom(data.palette, data.storage); +@@ -107,7 +_,7 @@ + return data1.palette.idFor(objectAdded); + } + +- public T getAndSet(int x, int y, int z, T state) { ++ public synchronized T getAndSet(int x, int y, int z, T state) { // Paper - synchronize + this.acquire(); + + Object var5; +@@ -130,7 +_,7 @@ + return this.data.palette.valueFor(andSet); + } + +- public void set(int x, int y, int z, T state) { ++ public synchronized void set(int x, int y, int z, T state) { // Paper - synchronize + this.acquire(); + + try { +@@ -163,7 +_,7 @@ + set.forEach(id -> consumer.accept(palette.valueFor(id))); + } + +- public void read(FriendlyByteBuf buffer) { ++ public synchronized void read(FriendlyByteBuf buffer) { // Paper - synchronize + this.acquire(); + + try { +@@ -178,7 +_,7 @@ + } + + @Override +- public void write(FriendlyByteBuf buffer) { ++ public synchronized void write(FriendlyByteBuf buffer) { // Paper - synchronize + this.acquire(); + + try { +@@ -226,7 +_,7 @@ + } + + @Override +- public PalettedContainerRO.PackedData pack(IdMap registry, PalettedContainer.Strategy strategy) { ++ public synchronized PalettedContainerRO.PackedData pack(IdMap registry, PalettedContainer.Strategy strategy) { // Paper - synchronize + this.acquire(); + + PalettedContainerRO.PackedData var12; diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/ProtoChunk.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ProtoChunk.java.patch similarity index 80% rename from paper-server/patches/unapplied/net/minecraft/world/level/chunk/ProtoChunk.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/chunk/ProtoChunk.java.patch index 1f9c01eb79..9988645334 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/ProtoChunk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ProtoChunk.java.patch @@ -1,11 +1,9 @@ --- a/net/minecraft/world/level/chunk/ProtoChunk.java +++ b/net/minecraft/world/level/chunk/ProtoChunk.java -@@ -81,7 +81,19 @@ - @Override - public ChunkAccess.PackedTicks getTicksForSerialization(long time) { - return new ChunkAccess.PackedTicks(this.blockTicks.pack(time), this.fluidTicks.pack(time)); -+ } -+ +@@ -85,6 +_,18 @@ + return new ChunkAccess.PackedTicks(this.blockTicks.pack(gametime), this.fluidTicks.pack(gametime)); + } + + // Paper start - If loaded util + @Override + public final FluidState getFluidIfLoaded(BlockPos blockposition) { @@ -15,8 +13,9 @@ + @Override + public final BlockState getBlockStateIfLoaded(BlockPos blockposition) { + return this.getBlockState(blockposition); - } ++ } + // Paper end - ++ @Override public BlockState getBlockState(BlockPos pos) { + int y = pos.getY(); diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/UpgradeData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/UpgradeData.java.patch similarity index 75% rename from paper-server/patches/unapplied/net/minecraft/world/level/chunk/UpgradeData.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/chunk/UpgradeData.java.patch index 902ea36be8..8628e64dd8 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/UpgradeData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/UpgradeData.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/UpgradeData.java +++ b/net/minecraft/world/level/chunk/UpgradeData.java -@@ -113,12 +113,36 @@ +@@ -113,6 +_,24 @@ } } @@ -22,26 +22,25 @@ + } + } + // Paper end - filter out relocated neighbour ticks -+ public void upgrade(LevelChunk chunk) { this.upgradeInside(chunk); - for (Direction8 direction8 : DIRECTIONS) { +@@ -120,6 +_,10 @@ upgradeSides(chunk, direction8); } -+ + + // Paper start - filter out relocated neighbour ticks + filterTickList(chunk.locX, chunk.locZ, this.neighborBlockTicks); + filterTickList(chunk.locX, chunk.locZ, this.neighborFluidTicks); + // Paper end - filter out relocated neighbour ticks - Level level = chunk.getLevel(); - this.neighborBlockTicks.forEach(tick -> { -@@ -129,6 +153,7 @@ - Fluid fluid = tick.type() == Fluids.EMPTY ? level.getFluidState(tick.pos()).getType() : tick.type(); - level.scheduleTick(tick.pos(), fluid, tick.delay(), tick.priority()); + this.neighborBlockTicks.forEach(blockTicker -> { + Block block = blockTicker.type() == Blocks.AIR ? level.getBlockState(blockTicker.pos()).getBlock() : blockTicker.type(); +@@ -129,6 +_,7 @@ + Fluid fluid = fluidTicker.type() == Fluids.EMPTY ? level.getFluidState(fluidTicker.pos()).getType() : fluidTicker.type(); + level.scheduleTick(fluidTicker.pos(), fluid, fluidTicker.delay(), fluidTicker.priority()); }); + UpgradeData.BlockFixers.values(); // Paper - force the class init so that we don't access CHUNKY_FIXERS before all BlockFixers are initialised - CHUNKY_FIXERS.forEach(logic -> logic.processChunk(level)); + CHUNKY_FIXERS.forEach(fixers -> fixers.processChunk(level)); } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkGenerator.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkGenerator.java.patch deleted file mode 100644 index 337768ffa6..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/ChunkGenerator.java.patch +++ /dev/null @@ -1,224 +0,0 @@ ---- a/net/minecraft/world/level/chunk/ChunkGenerator.java -+++ b/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -108,8 +108,8 @@ - - protected abstract MapCodec codec(); - -- public ChunkGeneratorStructureState createState(HolderLookup structureSetRegistry, RandomState noiseConfig, long seed) { -- return ChunkGeneratorStructureState.createForNormal(noiseConfig, seed, this.biomeSource, structureSetRegistry); -+ public ChunkGeneratorStructureState createState(HolderLookup holderlookup, RandomState randomstate, long i, org.spigotmc.SpigotWorldConfig conf) { // Spigot -+ return ChunkGeneratorStructureState.createForNormal(randomstate, i, this.biomeSource, holderlookup, conf); // Spigot - } - - public Optional>> getTypeNameForDataFixer() { -@@ -127,6 +127,24 @@ - - @Nullable - public Pair> findNearestMapStructure(ServerLevel world, HolderSet structures, BlockPos center, int radius, boolean skipReferencedStructures) { -+ // Paper start - StructuresLocateEvent -+ final org.bukkit.World bukkitWorld = world.getWorld(); -+ final org.bukkit.Location origin = io.papermc.paper.util.MCUtil.toLocation(world, center); -+ final List apiStructures = structures.stream().map(Holder::value).map(nms -> org.bukkit.craftbukkit.generator.structure.CraftStructure.minecraftToBukkit(nms)).toList(); -+ if (!apiStructures.isEmpty()) { -+ final io.papermc.paper.event.world.StructuresLocateEvent event = new io.papermc.paper.event.world.StructuresLocateEvent(bukkitWorld, origin, apiStructures, radius, skipReferencedStructures); -+ if (!event.callEvent()) { -+ return null; -+ } -+ if (event.getResult() != null) { -+ return Pair.of(io.papermc.paper.util.MCUtil.toBlockPos(event.getResult().pos()), world.registryAccess().lookupOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(event.getResult().structure()))); -+ } -+ center = io.papermc.paper.util.MCUtil.toBlockPosition(event.getOrigin()); -+ radius = event.getRadius(); -+ skipReferencedStructures = event.shouldFindUnexplored(); -+ structures = HolderSet.direct(api -> world.registryAccess().lookupOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(api)), event.getStructures()); -+ } -+ // Paper end - ChunkGeneratorStructureState chunkgeneratorstructurestate = world.getChunkSource().getGeneratorState(); - Map>> map = new Object2ObjectArrayMap(); - Iterator iterator = structures.iterator(); -@@ -223,6 +241,7 @@ - - while (iterator.hasNext()) { - ChunkPos chunkcoordintpair = (ChunkPos) iterator.next(); -+ if (!world.paperConfig().environment.locateStructuresOutsideWorldBorder && !world.getWorldBorder().isChunkInBounds(chunkcoordintpair.x, chunkcoordintpair.z)) { continue; } // Paper - Bound treasure maps to world border - - blockposition_mutableblockposition.set(SectionPos.sectionToBlockCoord(chunkcoordintpair.x, 8), 32, SectionPos.sectionToBlockCoord(chunkcoordintpair.z, 8)); - double d1 = blockposition_mutableblockposition.distSqr(center); -@@ -247,12 +266,15 @@ - int i1 = placement.spacing(); - - for (int j1 = -radius; j1 <= radius; ++j1) { -- boolean flag1 = j1 == -radius || j1 == radius; -+ // Paper start - Perf: iterate over border chunks instead of entire square chunk area -+ boolean flag1 = j1 == -radius || j1 == radius; final boolean onBorderAlongZAxis = flag1; // Paper - OBFHELPER - -- for (int k1 = -radius; k1 <= radius; ++k1) { -- boolean flag2 = k1 == -radius || k1 == radius; -+ for (int k1 = -radius; k1 <= radius; k1 += onBorderAlongZAxis ? 1 : radius * 2) { -+ // boolean flag2 = k1 == -radius || k1 == radius; - -- if (flag1 || flag2) { -+ // if (flag1 || flag2) { -+ if (true) { -+ // Paper end - Perf: iterate over border chunks instead of entire square chunk area - int l1 = centerChunkX + i1 * j1; - int i2 = centerChunkZ + i1 * k1; - ChunkPos chunkcoordintpair = placement.getPotentialStructureChunk(seed, l1, i2); -@@ -312,29 +334,29 @@ - } - } - -- public void applyBiomeDecoration(WorldGenLevel world, ChunkAccess chunk, StructureManager structureAccessor) { -- ChunkPos chunkcoordintpair = chunk.getPos(); -+ public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { // CraftBukkit -+ ChunkPos chunkcoordintpair = ichunkaccess.getPos(); - - if (!SharedConstants.debugVoidTerrain(chunkcoordintpair)) { -- SectionPos sectionposition = SectionPos.of(chunkcoordintpair, world.getMinSectionY()); -+ SectionPos sectionposition = SectionPos.of(chunkcoordintpair, generatoraccessseed.getMinSectionY()); - BlockPos blockposition = sectionposition.origin(); -- Registry iregistry = world.registryAccess().lookupOrThrow(Registries.STRUCTURE); -+ Registry iregistry = generatoraccessseed.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Map> map = (Map) iregistry.stream().collect(Collectors.groupingBy((structure) -> { - return structure.step().ordinal(); - })); - List list = (List) this.featuresPerStep.get(); - WorldgenRandom seededrandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); -- long i = seededrandom.setDecorationSeed(world.getSeed(), blockposition.getX(), blockposition.getZ()); -+ long i = seededrandom.setDecorationSeed(generatoraccessseed.getSeed(), blockposition.getX(), blockposition.getZ()); - Set> set = new ObjectArraySet(); - - ChunkPos.rangeClosed(sectionposition.chunk(), 1).forEach((chunkcoordintpair1) -> { -- ChunkAccess ichunkaccess1 = world.getChunk(chunkcoordintpair1.x, chunkcoordintpair1.z); -+ ChunkAccess ichunkaccess1 = generatoraccessseed.getChunk(chunkcoordintpair1.x, chunkcoordintpair1.z); - LevelChunkSection[] achunksection = ichunkaccess1.getSections(); - int j = achunksection.length; - - for (int k = 0; k < j; ++k) { - LevelChunkSection chunksection = achunksection[k]; -- PalettedContainerRO palettedcontainerro = chunksection.getBiomes(); -+ PalettedContainerRO> palettedcontainerro = chunksection.getBiomes(); // CraftBukkit - decompile error - - Objects.requireNonNull(set); - palettedcontainerro.getAll(set::add); -@@ -345,7 +367,7 @@ - int j = list.size(); - - try { -- Registry iregistry1 = world.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); -+ Registry iregistry1 = generatoraccessseed.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); - int k = Math.max(GenerationStep.Decoration.values().length, j); - - for (int l = 0; l < k; ++l) { -@@ -353,7 +375,7 @@ - Iterator iterator; - CrashReportCategory crashreportsystemdetails; - -- if (structureAccessor.shouldGenerateStructures()) { -+ if (structuremanager.shouldGenerateStructures()) { - List list1 = (List) map.getOrDefault(l, Collections.emptyList()); - - for (iterator = list1.iterator(); iterator.hasNext(); ++i1) { -@@ -368,9 +390,9 @@ - }; - - try { -- world.setCurrentlyGenerating(supplier); -- structureAccessor.startsForStructure(sectionposition, structure).forEach((structurestart) -> { -- structurestart.placeInChunk(world, structureAccessor, this, seededrandom, ChunkGenerator.getWritableArea(chunk), chunkcoordintpair); -+ generatoraccessseed.setCurrentlyGenerating(supplier); -+ structuremanager.startsForStructure(sectionposition, structure).forEach((structurestart) -> { -+ structurestart.placeInChunk(generatoraccessseed, structuremanager, this, seededrandom, ChunkGenerator.getWritableArea(ichunkaccess), chunkcoordintpair); - }); - } catch (Exception exception) { - CrashReport crashreport = CrashReport.forThrowable(exception, "Feature placement"); -@@ -418,11 +440,18 @@ - return (String) optional.orElseGet(placedfeature::toString); - }; - -- seededrandom.setFeatureSeed(i, l1, l); -+ // Paper start - Configurable feature seeds; change populationSeed used in random -+ long featurePopulationSeed = i; -+ final long configFeatureSeed = generatoraccessseed.getMinecraftWorld().paperConfig().featureSeeds.features.getLong(placedfeature.feature()); -+ if (configFeatureSeed != -1) { -+ featurePopulationSeed = seededrandom.setDecorationSeed(configFeatureSeed, blockposition.getX(), blockposition.getZ()); // See seededrandom.setDecorationSeed from above -+ } -+ seededrandom.setFeatureSeed(featurePopulationSeed, l1, l); -+ // Paper end - Configurable feature seeds - - try { -- world.setCurrentlyGenerating(supplier1); -- placedfeature.placeWithBiomeCheck(world, this, seededrandom, blockposition); -+ generatoraccessseed.setCurrentlyGenerating(supplier1); -+ placedfeature.placeWithBiomeCheck(generatoraccessseed, this, seededrandom, blockposition); - } catch (Exception exception1) { - CrashReport crashreport1 = CrashReport.forThrowable(exception1, "Feature placement"); - -@@ -435,15 +464,42 @@ - } - } - -- world.setCurrentlyGenerating((Supplier) null); -+ generatoraccessseed.setCurrentlyGenerating((Supplier) null); - } catch (Exception exception2) { - CrashReport crashreport2 = CrashReport.forThrowable(exception2, "Biome decoration"); - - crashreport2.addCategory("Generation").setDetail("CenterX", (Object) chunkcoordintpair.x).setDetail("CenterZ", (Object) chunkcoordintpair.z).setDetail("Decoration Seed", (Object) i); - throw new ReportedException(crashreport2); -+ } -+ } -+ } -+ -+ // CraftBukkit start -+ public void applyBiomeDecoration(WorldGenLevel world, ChunkAccess chunk, StructureManager structureAccessor) { -+ this.applyBiomeDecoration(world, chunk, structureAccessor, true); -+ } -+ -+ public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) { -+ if (vanilla) { -+ this.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); -+ } -+ -+ org.bukkit.World world = generatoraccessseed.getMinecraftWorld().getWorld(); -+ // only call when a populator is present (prevents unnecessary entity conversion) -+ if (!world.getPopulators().isEmpty()) { -+ org.bukkit.craftbukkit.generator.CraftLimitedRegion limitedRegion = new org.bukkit.craftbukkit.generator.CraftLimitedRegion(generatoraccessseed, ichunkaccess.getPos()); -+ int x = ichunkaccess.getPos().x; -+ int z = ichunkaccess.getPos().z; -+ for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { -+ WorldgenRandom seededrandom = new WorldgenRandom(new net.minecraft.world.level.levelgen.LegacyRandomSource(generatoraccessseed.getSeed())); -+ seededrandom.setDecorationSeed(generatoraccessseed.getSeed(), x, z); -+ populator.populate(world, new org.bukkit.craftbukkit.util.RandomSourceWrapper.RandomWrapper(seededrandom), x, z, limitedRegion); - } -+ limitedRegion.saveEntities(); -+ limitedRegion.breakLink(); - } - } -+ // CraftBukkit end - - private static BoundingBox getWritableArea(ChunkAccess chunk) { - ChunkPos chunkcoordintpair = chunk.getPos(); -@@ -521,7 +577,7 @@ - } - } - -- if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z)) { -+ if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z, structureplacement instanceof net.minecraft.world.level.chunk.ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs - if (list.size() == 1) { - this.tryGenerateStructure((StructureSet.StructureSelectionEntry) list.get(0), structureAccessor, registryManager, randomstate, structureTemplateManager, placementCalculator.getLevelSeed(), chunk, chunkcoordintpair, sectionposition, dimension); - } else { -@@ -582,6 +638,14 @@ - StructureStart structurestart = structure.generate(weightedEntry.structure(), dimension, dynamicRegistryManager, this, this.biomeSource, noiseConfig, structureManager, seed, pos, j, chunk, predicate); - - if (structurestart.isValid()) { -+ // CraftBukkit start -+ BoundingBox box = structurestart.getBoundingBox(); -+ org.bukkit.event.world.AsyncStructureSpawnEvent event = new org.bukkit.event.world.AsyncStructureSpawnEvent(structureAccessor.level.getMinecraftWorld().getWorld(), org.bukkit.craftbukkit.generator.structure.CraftStructure.minecraftToBukkit(structure), new org.bukkit.util.BoundingBox(box.minX(), box.minY(), box.minZ(), box.maxX(), box.maxY(), box.maxZ()), pos.x, pos.z); -+ org.bukkit.Bukkit.getPluginManager().callEvent(event); -+ if (event.isCancelled()) { -+ return true; -+ } -+ // CraftBukkit end - structureAccessor.setStartForStructure(sectionPos, structure, structurestart, chunk); - return true; - } else { diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/HashMapPalette.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/chunk/HashMapPalette.java.patch deleted file mode 100644 index ce7c9bcdcb..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/HashMapPalette.java.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- a/net/minecraft/world/level/chunk/HashMapPalette.java -+++ b/net/minecraft/world/level/chunk/HashMapPalette.java -@@ -20,7 +20,7 @@ - } - - public HashMapPalette(IdMap idList, int indexBits, PaletteResize listener) { -- this(idList, indexBits, listener, CrudeIncrementalIntIdentityHashBiMap.create(1 << indexBits)); -+ this(idList, indexBits, listener, CrudeIncrementalIntIdentityHashBiMap.create((1 << indexBits) + 1)); // Paper - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap - } - - private HashMapPalette(IdMap idList, int indexBits, PaletteResize listener, CrudeIncrementalIntIdentityHashBiMap map) { -@@ -38,10 +38,16 @@ - public int idFor(T object) { - int i = this.values.getId(object); - if (i == -1) { -- i = this.values.add(object); -- if (i >= 1 << this.bits) { -+ // Paper start - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize -+ // We use size() instead of the result from add(K) -+ // This avoids adding another object unnecessarily -+ // Without this change, + 2 would be required in the constructor -+ if (this.values.size() >= 1 << this.bits) { - i = this.resizeHandler.onResize(this.bits + 1, object); -+ } else { -+ i = this.values.add(object); - } -+ // Paper end - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize - } - - return i; diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/LevelChunk.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/chunk/LevelChunk.java.patch deleted file mode 100644 index d33242d709..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/LevelChunk.java.patch +++ /dev/null @@ -1,409 +0,0 @@ ---- a/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/net/minecraft/world/level/chunk/LevelChunk.java -@@ -79,7 +79,7 @@ - }; - private final Map tickersInLevel; - public boolean loaded; -- public final Level level; -+ public final ServerLevel level; // CraftBukkit - type - @Nullable - private Supplier fullStatus; - @Nullable -@@ -98,7 +98,7 @@ - this.tickersInLevel = Maps.newHashMap(); - this.unsavedListener = (chunkcoordintpair1) -> { - }; -- this.level = world; -+ this.level = (ServerLevel) world; // CraftBukkit - type - this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap(); - Heightmap.Types[] aheightmap_type = Heightmap.Types.values(); - int j = aheightmap_type.length; -@@ -116,6 +116,15 @@ - this.fluidTicks = fluidTickScheduler; - } - -+ // CraftBukkit start -+ public boolean mustNotSave; -+ public boolean needsDecoration; -+ // CraftBukkit end -+ -+ // Paper start -+ boolean loadedTicketLevel; -+ // Paper end -+ - public LevelChunk(ServerLevel world, ProtoChunk protoChunk, @Nullable LevelChunk.PostLoadProcessor entityLoader) { - this(world, protoChunk.getPos(), protoChunk.getUpgradeData(), protoChunk.unpackBlockTicks(), protoChunk.unpackFluidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), entityLoader, protoChunk.getBlendingData()); - if (!Collections.disjoint(protoChunk.pendingBlockEntities.keySet(), protoChunk.blockEntities.keySet())) { -@@ -151,6 +160,10 @@ - this.skyLightSources = protoChunk.skyLightSources; - this.setLightCorrect(protoChunk.isLightCorrect()); - this.markUnsaved(); -+ this.needsDecoration = true; // CraftBukkit -+ // CraftBukkit start -+ this.persistentDataContainer = protoChunk.persistentDataContainer; // SPIGOT-6814: copy PDC to account for 1.17 to 1.18 chunk upgrading. -+ // CraftBukkit end - } - - public void setUnsavedListener(LevelChunk.UnsavedListener unsavedListener) { -@@ -187,7 +200,14 @@ - return new ChunkAccess.PackedTicks(this.blockTicks.pack(time), this.fluidTicks.pack(time)); - } - -+ // Paper start - @Override -+ public long getInhabitedTime() { -+ return this.level.paperConfig().chunks.fixedChunkInhabitedTime < 0 ? super.getInhabitedTime() : this.level.paperConfig().chunks.fixedChunkInhabitedTime; -+ } -+ // Paper end -+ -+ @Override - public GameEventListenerRegistry getListenerRegistry(int ySectionCoord) { - Level world = this.level; - -@@ -200,8 +220,25 @@ - } - } - -+ // Paper start - Perf: Reduce instructions and provide final method -+ public BlockState getBlockState(final int x, final int y, final int z) { -+ return this.getBlockStateFinal(x, y, z); -+ } -+ public BlockState getBlockStateFinal(final int x, final int y, final int z) { -+ // Copied and modified from below -+ final int sectionIndex = this.getSectionIndex(y); -+ if (sectionIndex < 0 || sectionIndex >= this.sections.length -+ || this.sections[sectionIndex].nonEmptyBlockCount == 0) { -+ return Blocks.AIR.defaultBlockState(); -+ } -+ return this.sections[sectionIndex].states.get((y & 15) << 8 | (z & 15) << 4 | x & 15); -+ } - @Override - public BlockState getBlockState(BlockPos pos) { -+ if (true) { -+ return this.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); -+ } -+ // Paper end - Perf: Reduce instructions and provide final method - int i = pos.getX(); - int j = pos.getY(); - int k = pos.getZ(); -@@ -243,24 +280,38 @@ - } - } - -+ // Paper start - If loaded util - @Override -+ public final FluidState getFluidIfLoaded(BlockPos blockposition) { -+ return this.getFluidState(blockposition); -+ } -+ -+ @Override -+ public final BlockState getBlockStateIfLoaded(BlockPos blockposition) { -+ return this.getBlockState(blockposition); -+ } -+ // Paper end -+ -+ @Override - public FluidState getFluidState(BlockPos pos) { - return this.getFluidState(pos.getX(), pos.getY(), pos.getZ()); - } - - public FluidState getFluidState(int x, int y, int z) { -- try { -- int l = this.getSectionIndex(y); -+ // Paper start - Perf: Optimise Chunk#getFluid -+ // try { // Remove try catch -+ int index = this.getSectionIndex(y); -+ if (index >= 0 && index < this.sections.length) { -+ LevelChunkSection chunksection = this.sections[index]; - -- if (l >= 0 && l < this.sections.length) { -- LevelChunkSection chunksection = this.sections[l]; -- - if (!chunksection.hasOnlyAir()) { -- return chunksection.getFluidState(x & 15, y & 15, z & 15); -+ return chunksection.states.get((y & 15) << 8 | (z & 15) << 4 | x & 15).getFluidState(); -+ // Paper end - Perf: Optimise Chunk#getFluid - } - } - - return Fluids.EMPTY.defaultFluidState(); -+ /* // Paper - Perf: Optimise Chunk#getFluid - } catch (Throwable throwable) { - CrashReport crashreport = CrashReport.forThrowable(throwable, "Getting fluid state"); - CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Block being got"); -@@ -270,80 +321,89 @@ - }); - throw new ReportedException(crashreport); - } -+ */ // Paper - Perf: Optimise Chunk#getFluid - } - -+ // CraftBukkit start - @Nullable - @Override - public BlockState setBlockState(BlockPos pos, BlockState state, boolean moved) { -- int i = pos.getY(); -+ return this.setBlockState(pos, state, moved, true); -+ } -+ -+ @Nullable -+ public BlockState setBlockState(BlockPos blockposition, BlockState iblockdata, boolean flag, boolean doPlace) { -+ // CraftBukkit end -+ int i = blockposition.getY(); - LevelChunkSection chunksection = this.getSection(this.getSectionIndex(i)); - boolean flag1 = chunksection.hasOnlyAir(); - -- if (flag1 && state.isAir()) { -+ if (flag1 && iblockdata.isAir()) { - return null; - } else { -- int j = pos.getX() & 15; -+ int j = blockposition.getX() & 15; - int k = i & 15; -- int l = pos.getZ() & 15; -- BlockState iblockdata1 = chunksection.setBlockState(j, k, l, state); -+ int l = blockposition.getZ() & 15; -+ BlockState iblockdata1 = chunksection.setBlockState(j, k, l, iblockdata); - -- if (iblockdata1 == state) { -+ if (iblockdata1 == iblockdata) { - return null; - } else { -- Block block = state.getBlock(); -+ Block block = iblockdata.getBlock(); - -- ((Heightmap) this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING)).update(j, i, l, state); -- ((Heightmap) this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES)).update(j, i, l, state); -- ((Heightmap) this.heightmaps.get(Heightmap.Types.OCEAN_FLOOR)).update(j, i, l, state); -- ((Heightmap) this.heightmaps.get(Heightmap.Types.WORLD_SURFACE)).update(j, i, l, state); -+ ((Heightmap) this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING)).update(j, i, l, iblockdata); -+ ((Heightmap) this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES)).update(j, i, l, iblockdata); -+ ((Heightmap) this.heightmaps.get(Heightmap.Types.OCEAN_FLOOR)).update(j, i, l, iblockdata); -+ ((Heightmap) this.heightmaps.get(Heightmap.Types.WORLD_SURFACE)).update(j, i, l, iblockdata); - boolean flag2 = chunksection.hasOnlyAir(); - - if (flag1 != flag2) { -- this.level.getChunkSource().getLightEngine().updateSectionStatus(pos, flag2); -+ this.level.getChunkSource().getLightEngine().updateSectionStatus(blockposition, flag2); - this.level.getChunkSource().onSectionEmptinessChanged(this.chunkPos.x, SectionPos.blockToSectionCoord(i), this.chunkPos.z, flag2); - } - -- if (LightEngine.hasDifferentLightProperties(iblockdata1, state)) { -+ if (LightEngine.hasDifferentLightProperties(iblockdata1, iblockdata)) { - ProfilerFiller gameprofilerfiller = Profiler.get(); - - gameprofilerfiller.push("updateSkyLightSources"); - this.skyLightSources.update(this, j, i, l); - gameprofilerfiller.popPush("queueCheckLight"); -- this.level.getChunkSource().getLightEngine().checkBlock(pos); -+ this.level.getChunkSource().getLightEngine().checkBlock(blockposition); - gameprofilerfiller.pop(); - } - - boolean flag3 = iblockdata1.hasBlockEntity(); - -- if (!this.level.isClientSide) { -- iblockdata1.onRemove(this.level, pos, state, moved); -+ if (!this.level.isClientSide && !this.level.isBlockPlaceCancelled) { // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent -+ iblockdata1.onRemove(this.level, blockposition, iblockdata, flag); - } else if (!iblockdata1.is(block) && flag3) { -- this.removeBlockEntity(pos); -+ this.removeBlockEntity(blockposition); - } - - if (!chunksection.getBlockState(j, k, l).is(block)) { - return null; - } else { -- if (!this.level.isClientSide) { -- state.onPlace(this.level, pos, iblockdata1, moved); -+ // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled. -+ if (!this.level.isClientSide && doPlace && (!this.level.captureBlockStates || block instanceof net.minecraft.world.level.block.BaseEntityBlock)) { -+ iblockdata.onPlace(this.level, blockposition, iblockdata1, flag); - } - -- if (state.hasBlockEntity()) { -- BlockEntity tileentity = this.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); -+ if (iblockdata.hasBlockEntity()) { -+ BlockEntity tileentity = this.getBlockEntity(blockposition, LevelChunk.EntityCreationType.CHECK); - -- if (tileentity != null && !tileentity.isValidBlockState(state)) { -- LevelChunk.LOGGER.warn("Found mismatched block entity @ {}: type = {}, state = {}", new Object[]{pos, tileentity.getType().builtInRegistryHolder().key().location(), state}); -- this.removeBlockEntity(pos); -+ if (tileentity != null && !tileentity.isValidBlockState(iblockdata)) { -+ LevelChunk.LOGGER.warn("Found mismatched block entity @ {}: type = {}, state = {}", new Object[]{blockposition, tileentity.getType().builtInRegistryHolder().key().location(), iblockdata}); -+ this.removeBlockEntity(blockposition); - tileentity = null; - } - - if (tileentity == null) { -- tileentity = ((EntityBlock) block).newBlockEntity(pos, state); -+ tileentity = ((EntityBlock) block).newBlockEntity(blockposition, iblockdata); - if (tileentity != null) { - this.addAndRegisterBlockEntity(tileentity); - } - } else { -- tileentity.setBlockState(state); -+ tileentity.setBlockState(iblockdata); - this.updateBlockEntityTicker(tileentity); - } - } -@@ -375,7 +435,12 @@ - - @Nullable - public BlockEntity getBlockEntity(BlockPos pos, LevelChunk.EntityCreationType creationType) { -- BlockEntity tileentity = (BlockEntity) this.blockEntities.get(pos); -+ // CraftBukkit start -+ BlockEntity tileentity = this.level.capturedTileEntities.get(pos); -+ if (tileentity == null) { -+ tileentity = (BlockEntity) this.blockEntities.get(pos); -+ } -+ // CraftBukkit end - - if (tileentity == null) { - CompoundTag nbttagcompound = (CompoundTag) this.pendingBlockEntities.remove(pos); -@@ -446,7 +511,13 @@ - BlockState iblockdata = this.getBlockState(blockposition); - - if (!iblockdata.hasBlockEntity()) { -- LevelChunk.LOGGER.warn("Trying to set block entity {} at position {}, but state {} does not allow it", new Object[]{blockEntity, blockposition, iblockdata}); -+ // Paper start - ServerExceptionEvent -+ com.destroystokyo.paper.exception.ServerInternalException e = new com.destroystokyo.paper.exception.ServerInternalException( -+ "Trying to set block entity %s at position %s, but state %s does not allow it".formatted(blockEntity, blockposition, iblockdata) -+ ); -+ e.printStackTrace(); -+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(e); -+ // Paper end - ServerExceptionEvent - } else { - BlockState iblockdata1 = blockEntity.getBlockState(); - -@@ -500,6 +571,12 @@ - if (this.isInLevel()) { - BlockEntity tileentity = (BlockEntity) this.blockEntities.remove(pos); - -+ // CraftBukkit start - SPIGOT-5561: Also remove from pending map -+ if (!this.pendingBlockEntities.isEmpty()) { -+ this.pendingBlockEntities.remove(pos); -+ } -+ // CraftBukkit end -+ - if (tileentity != null) { - Level world = this.level; - -@@ -553,6 +630,65 @@ - - } - -+ // CraftBukkit start -+ public void loadCallback() { -+ // Paper start -+ this.loadedTicketLevel = true; -+ // Paper end -+ org.bukkit.Server server = this.level.getCraftServer(); -+ this.level.getChunkSource().addLoadedChunk(this); // Paper -+ if (server != null) { -+ /* -+ * If it's a new world, the first few chunks are generated inside -+ * the World constructor. We can't reliably alter that, so we have -+ * no way of creating a CraftWorld/CraftServer at that point. -+ */ -+ org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); -+ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(bukkitChunk, this.needsDecoration)); -+ -+ if (this.needsDecoration) { -+ this.needsDecoration = false; -+ java.util.Random random = new java.util.Random(); -+ random.setSeed(this.level.getSeed()); -+ long xRand = random.nextLong() / 2L * 2L + 1L; -+ long zRand = random.nextLong() / 2L * 2L + 1L; -+ random.setSeed((long) this.chunkPos.x * xRand + (long) this.chunkPos.z * zRand ^ this.level.getSeed()); -+ -+ org.bukkit.World world = this.level.getWorld(); -+ if (world != null) { -+ this.level.populating = true; -+ try { -+ for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { -+ populator.populate(world, random, bukkitChunk); -+ } -+ } finally { -+ this.level.populating = false; -+ } -+ } -+ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(bukkitChunk)); -+ } -+ } -+ } -+ -+ public void unloadCallback() { -+ org.bukkit.Server server = this.level.getCraftServer(); -+ org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); -+ org.bukkit.event.world.ChunkUnloadEvent unloadEvent = new org.bukkit.event.world.ChunkUnloadEvent(bukkitChunk, this.isUnsaved()); -+ server.getPluginManager().callEvent(unloadEvent); -+ // note: saving can be prevented, but not forced if no saving is actually required -+ this.mustNotSave = !unloadEvent.isSaveChunk(); -+ this.level.getChunkSource().removeLoadedChunk(this); // Paper -+ // Paper start -+ this.loadedTicketLevel = false; -+ // Paper end -+ } -+ -+ @Override -+ public boolean isUnsaved() { -+ return super.isUnsaved() && !this.mustNotSave; -+ } -+ // CraftBukkit end -+ - public boolean isEmpty() { - return false; - } -@@ -750,7 +886,7 @@ - - private void updateBlockEntityTicker(T blockEntity) { - BlockState iblockdata = blockEntity.getBlockState(); -- BlockEntityTicker blockentityticker = iblockdata.getTicker(this.level, blockEntity.getType()); -+ BlockEntityTicker blockentityticker = iblockdata.getTicker(this.level, (BlockEntityType) blockEntity.getType()); // CraftBukkit - decompile error - - if (blockentityticker == null) { - this.removeBlockEntityTicker(blockEntity.getBlockPos()); -@@ -841,7 +977,7 @@ - private boolean loggedInvalidBlockState; - - BoundTickingBlockEntity(final BlockEntity tileentity, final BlockEntityTicker blockentityticker) { -- this.blockEntity = tileentity; -+ this.blockEntity = (T) tileentity; // CraftBukkit - decompile error - this.ticker = blockentityticker; - } - -@@ -860,18 +996,25 @@ - if (this.blockEntity.getType().isValid(iblockdata)) { - this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), iblockdata, this.blockEntity); - this.loggedInvalidBlockState = false; -- } else if (!this.loggedInvalidBlockState) { -- this.loggedInvalidBlockState = true; -- LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), iblockdata}); -+ // Paper start - Remove the Block Entity if it's invalid -+ } else { -+ LevelChunk.this.removeBlockEntity(this.getPos()); -+ if (!this.loggedInvalidBlockState) { -+ this.loggedInvalidBlockState = true; -+ LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), iblockdata}); -+ } -+ // Paper end - Remove the Block Entity if it's invalid - } - - gameprofilerfiller.pop(); - } catch (Throwable throwable) { -- CrashReport crashreport = CrashReport.forThrowable(throwable, "Ticking block entity"); -- CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Block entity being ticked"); -- -- this.blockEntity.fillCrashReportCategory(crashreportsystemdetails); -- throw new ReportedException(crashreport); -+ // Paper start - Prevent block entity and entity crashes -+ final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ()); -+ net.minecraft.server.MinecraftServer.LOGGER.error(msg, throwable); -+ net.minecraft.world.level.chunk.LevelChunk.this.level.getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); // Paper - ServerExceptionEvent -+ LevelChunk.this.removeBlockEntity(this.getPos()); -+ // Paper end - Prevent block entity and entity crashes -+ // Spigot start - } - } - } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/PalettedContainer.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/chunk/PalettedContainer.java.patch deleted file mode 100644 index aff5b81eae..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/chunk/PalettedContainer.java.patch +++ /dev/null @@ -1,74 +0,0 @@ ---- a/net/minecraft/world/level/chunk/PalettedContainer.java -+++ b/net/minecraft/world/level/chunk/PalettedContainer.java -@@ -30,14 +30,14 @@ - public final IdMap registry; - private volatile PalettedContainer.Data data; - private final PalettedContainer.Strategy strategy; -- private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); -+ // private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused - - public void acquire() { -- this.threadingDetector.checkAndLock(); -+ // this.threadingDetector.checkAndLock(); // Paper - disable this - use proper synchronization - } - - public void release() { -- this.threadingDetector.checkAndUnlock(); -+ // this.threadingDetector.checkAndUnlock(); // Paper - disable this - } - - public static Codec> codecRW(IdMap idList, Codec entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { -@@ -110,7 +110,7 @@ - } - - @Override -- public int onResize(int newBits, T object) { -+ public synchronized int onResize(int newBits, T object) { // Paper - synchronize - PalettedContainer.Data data = this.data; - PalettedContainer.Data data2 = this.createOrReuseData(data, newBits); - data2.copyFrom(data.palette, data.storage); -@@ -135,7 +135,7 @@ - return this.getAndSet(this.strategy.getIndex(x, y, z), value); - } - -- private T getAndSet(int index, T value) { -+ private synchronized T getAndSet(int index, T value) { // Paper - synchronize - int i = this.data.palette.idFor(value); - int j = this.data.storage.getAndSet(index, i); - return this.data.palette.valueFor(j); -@@ -151,7 +151,7 @@ - } - } - -- private void set(int index, T value) { -+ private synchronized void set(int index, T value) { // Paper - synchronize - int i = this.data.palette.idFor(value); - this.data.storage.set(index, i); - } -@@ -174,7 +174,7 @@ - intSet.forEach(id -> action.accept(palette.valueFor(id))); - } - -- public void read(FriendlyByteBuf buf) { -+ public synchronized void read(FriendlyByteBuf buf) { // Paper - synchronize - this.acquire(); - - try { -@@ -189,7 +189,7 @@ - } - - @Override -- public void write(FriendlyByteBuf buf) { -+ public synchronized void write(FriendlyByteBuf buf) { // Paper - synchronize - this.acquire(); - - try { -@@ -237,7 +237,7 @@ - } - - @Override -- public PalettedContainerRO.PackedData pack(IdMap idList, PalettedContainer.Strategy paletteProvider) { -+ public synchronized PalettedContainerRO.PackedData pack(IdMap idList, PalettedContainer.Strategy paletteProvider) { // Paper - synchronize - this.acquire(); - - PalettedContainerRO.PackedData var12;