[SPIGOT-7011] Cannot locate Stronghold with World#locateClosestStructure() Created: 27/Apr/22  Updated: 25/Dec/24  Resolved: 01/Jul/22

Status: Resolved
Project: Spigot
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Minor
Reporter: ANDREW STEIN Assignee: Marvin Rieple
Resolution: Fixed Votes: 1
Labels: findNearestMapFeature, locateClosestStructure
Environment:
  • Windows 11
  • IntelliJ IDEA Ultimate
  • Latest build from build tools as of 04/26/2022
  • Tested with JDK 16 & 17

Attachments: Text File 0094-findNearestMapFeature-Resolution.patch    
Issue Links:
Relates
relates to SPIGOT-7065 World#locateNearestStructure always r... Resolved
relates to SPIGOT-7081 Ancient City structure type missing Resolved
Version: 3482-Spigot-42b6152-e87f2e3 (MC: 1.18.2) (Implementing API version 1.18-R0.1-SNAPSHOT)
Guidelines Read: Yes

 Description   

 

Any usage of of World#locateNearestStructure() returns null for a Stronghold.

java.lang.NullPointerException: Cannot invoke "org.bukkit.Location.getBlock()" because the return value of "org.bukkit.World.locateNearestStructure(org.bukkit.Location, org.bukkit.StructureType, int, boolean)" is null
        at net.andrewcpu.worldstar.WorldStarDriver.lambda$onChat$0(WorldStarDriver.java:58) ~[?:?]
        at org.bukkit.craftbukkit.v1_18_R2.scheduler.CraftTask.run(CraftTask.java:82) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3482-Spigot-42b6152-e87f2e3]
        at org.bukkit.craftbukkit.v1_18_R2.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:415) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3482-Spigot-42b6152-e87f2e3]
        at net.minecraft.server.MinecraftServer.b(MinecraftServer.java:1285) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3482-Spigot-42b6152-e87f2e3]
        at net.minecraft.server.dedicated.DedicatedServer.b(DedicatedServer.java:429) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3482-Spigot-42b6152-e87f2e3]
        at net.minecraft.server.MinecraftServer.a(MinecraftServer.java:1237) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3482-Spigot-42b6152-e87f2e3]
        at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:1047) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3482-Spigot-42b6152-e87f2e3]
        at net.minecraft.server.MinecraftServer.lambda$0(MinecraftServer.java:304) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3482-Spigot-42b6152-e87f2e3]
        at java.lang.Thread.run(Thread.java:833) [?:?] 

 

 

Symptoms:

  • /locate stronghold works
  • /locate #minecraft:eye_of_ender_located works
  • World#locateNearestStructure() for a MINESHAFT works
  • /locate mineshaft works
  • World#locateNearestStructure() for STRONGHOLD does not work
  • Tested locateNearestStructure() for STRONGHOLD with varying ranges, does not work
  • Tested locateNearestStructure() for STRONGHOLD with both searching for unfound structures set to true and false, does not work

 

Investigation: 

Existing findNearestMapFeature code in CB#WorldServer

@Nullable
public BlockPosition findNearestMapFeature(TagKey<StructureFeature<?, ?>> tagkey, BlockPosition blockposition, int i, boolean flag) {
    if (!this.serverLevelData.worldGenSettings().generateFeatures()) { // CraftBukkit
        return null;
    } else {
        Optional<HolderSet.Named<StructureFeature<?, ?>>> optional = this.registryAccess().registryOrThrow(IRegistry.CONFIGURED_STRUCTURE_FEATURE_REGISTRY).getTag(tagkey);

        if (optional.isEmpty()) {
            return null;
        } else {
            Pair<BlockPosition, Holder<StructureFeature<?, ?>>> pair = this.getChunkSource().getGenerator().findNearestMapFeature(this, (HolderSet) optional.get(), blockposition, i, flag);

            return pair != null ? (BlockPosition) pair.getFirst() : null;
        }
    }
} 

 

The Optional<HolderSet.Named> is coming back as empty for Strongholds and returning null without attempting to look for the proposed TagKey.

 

It seems that the IRegistry for the worlds does not have the ResourceKey added for Strongholds.

 

 

My proposed change: (Though may be unsafe, will need another set of eyes obviously)

@Nullable
public BlockPosition findNearestMapFeature(TagKey<StructureFeature<?, ?>> tagkey, BlockPosition blockposition, int i, boolean flag) {
    if (!this.serverLevelData.worldGenSettings().generateFeatures()) { // CraftBukkit
        return null;
    } else {
        IRegistry<StructureFeature<?, ?>> iregistry = getLevel().registryAccess().registryOrThrow(IRegistry.CONFIGURED_STRUCTURE_FEATURE_REGISTRY);
        Holder<StructureFeature<?, ?>> var3 = iregistry.getHolderOrThrow(iregistry.getResourceKey(iregistry.get(tagkey.location())).orElse(ResourceKey.create(iregistry.key(), tagkey.location())));
        Pair<BlockPosition, Holder<StructureFeature<?, ?>>> pair = this.getChunkSource().getGenerator().findNearestMapFeature(this, (HolderSet) HolderSet.direct(var3), blockposition, i, flag);
        return pair != null ? pair.getFirst() : null;
    }
} 

 

I made a patch which resolves the issue, passes all existing tests, and returns null without throwing an error when the structure cannot be found in the world. But, I'm sure there's a better reason as to why this stopped working initially.

 

I do see there are a good amount of commits RE: /locate and structures at the moment, (I think it was SPIGOT-7000?), so maybe it was something recently, I haven't had a second to look. 

 



 Comments   
Comment by Marvin Rieple [ 18/Jun/22 ]

Made a PR for this: craftbukkit#1073

Comment by Doc [ 27/Apr/22 ]

The NMS method in WorldServer its the same used in the command, the difference is for get the "TagKey<StructureFeature<?, ?>>" used in the command vs CB.

Comment by ANDREW STEIN [ 27/Apr/22 ]

Why doesn't the locateClosestStructure code use the same underlying NMS code for /locate?

Generated at Tue Apr 08 03:01:15 UTC 2025 using Jira 10.3.3#10030003-sha1:d220e3fefc8dfc6d47f522d3b9a20c1455e12b7b.