From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Mon, 13 Jan 2020 15:40:32 +0100 Subject: [PATCH] Seed based feature search This tries to work around the issue where the server will load surrounding chunks up to a radius of 100 chunks in order to search for features e.g. when running the /locate command or for treasure maps (issue #2312). This is done by backporting Mojang's change in 1.17 which makes it so that the biome (generated by the seed) is checked first if the feature can be generated before actually to load the chunk. Additionally to that the center location of the target chunk is simply returned if the chunk is not loaded to avoid the sync chunk load. As this can lead to less precise locations a toggle is provided to enable the sync loading of the target chunk again. The main downside of this is that it breaks once the seed or generator changes but this should usually not happen. A config option to disable this completely is added though in case that should ever be necessary. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java index 424754a0183b071d20c86f0420cec784a8992e2b..97870622e41cca36d9c7493bfad796f35f3831f4 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -337,6 +337,14 @@ public class PaperWorldConfig { } } + public boolean seedBasedFeatureSearch = true; + public boolean seedBasedFeatureSearchLoadsChunks = false; + private void seedBasedFeatureSearch() { + seedBasedFeatureSearch = getBoolean("seed-based-feature-search", seedBasedFeatureSearch); + seedBasedFeatureSearchLoadsChunks = getBoolean("seed-based-feature-search-loads-chunks", seedBasedFeatureSearchLoadsChunks); + log("Feature search is based on seed: " + seedBasedFeatureSearch + ", loads chunks:" + seedBasedFeatureSearchLoadsChunks); + } + public int maxCollisionsPerEntity; private void maxEntityCollision() { maxCollisionsPerEntity = getInt( "max-entity-collisions", this.spigotConfig.getInt("max-entity-collisions", 8) ); diff --git a/src/main/java/net/minecraft/world/level/ChunkPos.java b/src/main/java/net/minecraft/world/level/ChunkPos.java index 4a5f318adf5bc2ca1c3fab5d173a99cddd77ab85..f61a3eda40328922b95f166be4dc604500e000be 100644 --- a/src/main/java/net/minecraft/world/level/ChunkPos.java +++ b/src/main/java/net/minecraft/world/level/ChunkPos.java @@ -68,10 +68,12 @@ public class ChunkPos { } } + public int getBlockX() { return getMinBlockX(); } // Paper - OBFHELPER public int getMinBlockX() { return this.x << 4; } + public int getBlockZ() { return getMinBlockZ(); } // Paper - OBFHELPER public int getMinBlockZ() { return this.z << 4; } diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index 2ad8a4558aa812885adebee8c05dab45f2bf5f90..066d5f7ee93351bff67c0d39ee9d940ac51515d8 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -1511,8 +1511,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return this.profiler; } - @Override - public BiomeManager getBiomeManager() { + public BiomeManager getBiomeManager() { return getBiomeManager(); } // Paper - OBFHELPER + @Override public BiomeManager getBiomeManager() { return this.biomeManager; } diff --git a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java index d22ac114440d807a6cf5f286961bc63935fa7823..3a1909c0b198d89539f4351d70a39d16cfd84987 100644 --- a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java +++ b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java @@ -23,6 +23,7 @@ public class BiomeManager { return new BiomeManager(source, this.biomeZoomSeed, this.zoomer); } + public Biome getBiome(BlockPos blockposition) { return getBiome(blockposition); } // Paper - OBFHELPER public Biome getBiome(BlockPos pos) { return this.zoomer.getBiome(this.biomeZoomSeed, pos.getX(), pos.getY(), pos.getZ(), this.noiseBiomeSource); } diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/StructureFeature.java b/src/main/java/net/minecraft/world/level/levelgen/feature/StructureFeature.java index 0624b8270bc28c83c5479cd51fa4633ed5c36f44..6b24590a1ac460a7fd4bbc2c70d4a4981378e79c 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/feature/StructureFeature.java +++ b/src/main/java/net/minecraft/world/level/levelgen/feature/StructureFeature.java @@ -176,7 +176,24 @@ public abstract class StructureFeature { int j2 = i1 + k * l1; ChunkPos chunkcoordintpair = this.getPotentialFeatureChunk(config, worldSeed, seededrandom, i2, j2); if (!world.getWorldBorder().isChunkInBounds(chunkcoordintpair.x, chunkcoordintpair.z)) { continue; } // Paper - ChunkAccess ichunkaccess = world.getChunk(chunkcoordintpair.x, chunkcoordintpair.z, ChunkStatus.STRUCTURE_STARTS); + // Paper start - seed based feature search + ChunkAccess ichunkaccess = null; + if (structureAccessor.getWorld().paperConfig.seedBasedFeatureSearch) { + Biome biomeBase = structureAccessor.getWorld().getBiomeManager().getBiome(new BlockPos(chunkcoordintpair.getBlockX() + 9, 0, chunkcoordintpair.getBlockZ() + 9)); + if (!biomeBase.getGenerationSettings().isValidStart(this)) { + continue; + } + if (!structureAccessor.getWorld().paperConfig.seedBasedFeatureSearchLoadsChunks) { + ichunkaccess = structureAccessor.getWorld().getChunkIfLoaded(chunkcoordintpair.x, chunkcoordintpair.z); + if (ichunkaccess == null) { + return chunkcoordintpair.asPosition().add(8, searchStartPos.getY(), 8); + } + } + } + if (ichunkaccess == null) { + ichunkaccess = world.getChunk(chunkcoordintpair.x, chunkcoordintpair.z, ChunkStatus.STRUCTURE_STARTS); + } + // Paper end StructureStart structurestart = structureAccessor.getStartForFeature(SectionPos.of(ichunkaccess.getPos(), 0), this, ichunkaccess); if (structurestart != null && structurestart.e()) {