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/ChunkCoordIntPair.java b/src/main/java/net/minecraft/world/level/ChunkCoordIntPair.java index e41d63596c32eee5f0c04a6f043d576d8021ff1a..53eb5b65683a2ab208edfc3f3bbf78ffee61bc85 100644 --- a/src/main/java/net/minecraft/world/level/ChunkCoordIntPair.java +++ b/src/main/java/net/minecraft/world/level/ChunkCoordIntPair.java @@ -68,10 +68,12 @@ public class ChunkCoordIntPair { } } + public int getBlockX() { return d(); } // Paper - OBFHELPER public int d() { return this.x << 4; } + public int getBlockZ() { return e(); } // Paper - OBFHELPER public int e() { return this.z << 4; } diff --git a/src/main/java/net/minecraft/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java index ae5f9a6af810b524f6dcbed64bc943122f0536cc..f40be366ebc5f98b417b677565fa89d3f817f3fb 100644 --- a/src/main/java/net/minecraft/world/level/World.java +++ b/src/main/java/net/minecraft/world/level/World.java @@ -1511,8 +1511,8 @@ public abstract class World implements GeneratorAccess, AutoCloseable { return this.methodProfiler; } - @Override - public BiomeManager d() { + public BiomeManager getBiomeManager() { return d(); } // Paper - OBFHELPER + @Override public BiomeManager d() { 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 340508e0ba8b8883a3037ecaa2d4e09e61e709d3..3b1d9b26ba3249b1df0c15a22428e4211ae0e024 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(worldchunkmanager, this.b, this.c); } + public BiomeBase getBiome(BlockPosition blockposition) { return a(blockposition); } // Paper - OBFHELPER public BiomeBase a(BlockPosition blockposition) { return this.c.a(this.b, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this.a); } diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/StructureGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/feature/StructureGenerator.java index ea7e3e15fa778c573d24f956f72f60579ea0b1a1..e5c22e92a305050df0eae2da53217ea2c3249e03 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/feature/StructureGenerator.java +++ b/src/main/java/net/minecraft/world/level/levelgen/feature/StructureGenerator.java @@ -176,7 +176,24 @@ public abstract class StructureGenerator int j2 = i1 + k * l1; ChunkCoordIntPair chunkcoordintpair = this.a(structuresettingsfeature, j, seededrandom, i2, j2); if (!iworldreader.getWorldBorder().isChunkInBounds(chunkcoordintpair.x, chunkcoordintpair.z)) { continue; } // Paper - IChunkAccess ichunkaccess = iworldreader.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, ChunkStatus.STRUCTURE_STARTS); + // Paper start - seed based feature search + IChunkAccess ichunkaccess = null; + if (structuremanager.getWorld().paperConfig.seedBasedFeatureSearch) { + BiomeBase biomeBase = structuremanager.getWorld().getBiomeManager().getBiome(new BlockPosition(chunkcoordintpair.getBlockX() + 9, 0, chunkcoordintpair.getBlockZ() + 9)); + if (!biomeBase.e().a(this)) { + continue; + } + if (!structuremanager.getWorld().paperConfig.seedBasedFeatureSearchLoadsChunks) { + ichunkaccess = structuremanager.getWorld().getChunkIfLoaded(chunkcoordintpair.x, chunkcoordintpair.z); + if (ichunkaccess == null) { + return chunkcoordintpair.asPosition().add(8, blockposition.getY(), 8); + } + } + } + if (ichunkaccess == null) { + ichunkaccess = iworldreader.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, ChunkStatus.STRUCTURE_STARTS); + } + // Paper end StructureStart structurestart = structuremanager.a(SectionPosition.a(ichunkaccess.getPos(), 0), this, ichunkaccess); if (structurestart != null && structurestart.e()) {