Solved Making guardians spawn in monuments like in vanilla via NMS

Discussion in 'Spigot Plugin Development' started by Hex_27, Jan 13, 2020.

  1. For example, I want to generate a custom Sea Temple. How do I tell minecraft to start spawning guardians in it?
  2. #3 Hex_27, Jan 13, 2020
    Last edited: Jan 13, 2020
  3. How about generate an actual temple and change the blocks?
  4. ...okay how do I do that?
  5. md_5

    Administrator Developer

    It's not currently possible with the API
  6. There's no way to hook into nms to do it either?

    EDIT: As in to make an area behave like a vanilla structure, not spawn a vanilla structure. That sounds awfully inefficient
    #8 Hex_27, Jan 15, 2020
    Last edited: Jan 15, 2020
  7. You either have to mimic the behavior yourself or need to dig around in NMS and find out how they populate the area...
    Edit: I found some bread crumbs in the WorldGenMonument class.
    #9 7smile7, Jan 15, 2020
    Last edited: Jan 15, 2020
  8. Hmmm inside biome meta...
  9. Ok i made some progress and found out how the chunks get ticked and populated.
    Just not quite sure when to change the values. The class BiomeBase has a
    EnumMap<EnumCreatureType, List<BiomeMeta>> v;
    however its private final. It gets populated while constructing the chunk. The class BiomeBase.BiomeMeta looks like this:

    And is used to get the entityTypes that should spawn on chunk ticks. (looks like they even ignore the spawn limit somehow? Not quite sure on that)
    So getting a BiomBase instance of your chunk and accessing the Field 'v' via reflections would be an idea...
    Oh nvm the Map gets populated with all EnumTypes and an empty List. There is a getter for every List:
    The method 'a' is used to add BiomeMetas but is protected. (BiomeBase.class)
    So you can get the BiomeBase instance of your chunk while generating and either calling
    Code (Text):
    BiomeBase#a(EnumCreatureType, BiomeMeta)
    where BiomeMeta should be constructed like in the WorldGenMonument.class:
    Code (Text):
    new BiomeBase.BiomeMeta(EntityTypes.GUARDIAN, 1, 2, 4);
    Or you can call
    Code (Text):
    This will return a MobList:

    Where you again can just call add
    Code (Text):
    new BiomeBase.BiomeMeta(EntityTypes.GUARDIAN, 1, 2, 4);
    • Winner Winner x 1
    • Informative Informative x 1
  10. So i would do:
    Code (Java):

        // Get BiomeBase instance of chunk (somehow)
        BiomeBase baseInstance;
        BiomeBase.BiomeMeta spawnMeta = new BiomeBase.BiomeMeta(EntityTypes.GUARDIAN, 1, 2, 4);

    Edit i found:
    Code (Text):
    ((CraftChunk)chunk).getHandle().getBiomeIndex().getBiome(x, y ,z)
    Which returns the BiomeBases :D
    #12 7smile7, Jan 15, 2020
    Last edited: Jan 15, 2020
    • Informative Informative x 2
  11. Sorry to necro the thread but before anybody tries this, I need to say that this doesn’t work. I finally got round to making ocean monuments in my generator and this caused an issue where ALL ocean biomes spawned guardians (very rapidly too, like 30+ in a chunk within seconds) Not just the biomes you marked out. I suspect the reason for this is because BiomeBases are being used as singletons. I’m currently digging further.
    #13 Hex_27, Apr 19, 2020
    Last edited: Apr 19, 2020
    • Like Like x 1
  12. I am trying hard to trace the e() method (returns the mob list) inside WorldGenMonument, as I think it's the key to all this. But I have no idea how to get the call hierarchy of this method.

    I can trace the parent method to WorldGenerator, and then I lose the trace afterwards.

    All the methods being "e" really doesn't help

    Traced to ChunkProviderGenerate
    Code (Java):
        public List<BiomeBase.BiomeMeta> getMobsFor(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
            if (WorldGenerator.SWAMP_HUT.c(this.a, blockposition)) {
                if (enumcreaturetype == EnumCreatureType.MONSTER) {
                    return WorldGenerator.SWAMP_HUT.e();
                if (enumcreaturetype == EnumCreatureType.CREATURE) {
                    return WorldGenerator.SWAMP_HUT.f();
            } else if (enumcreaturetype == EnumCreatureType.MONSTER) {
                if (WorldGenerator.PILLAGER_OUTPOST.a(this.a, blockposition)) {
                    return WorldGenerator.PILLAGER_OUTPOST.getSpecialEnemies();
                if (WorldGenerator.OCEAN_MONUMENT.a(this.a, blockposition)) {
                    return WorldGenerator.OCEAN_MONUMENT.getSpecialEnemies();
            return super.getMobsFor(enumcreaturetype, blockposition);
    That's nasty. So at least I know where to do data checks if I decide to save data manually. Anyway, I'm now tracing .a(this.a, blockposition)
    #14 Hex_27, Apr 20, 2020
    Last edited: Apr 20, 2020
  13. I can't really figure out how StructureStart and StructureBoundingBox works, but it's the key to how spawning is done.

    Also, if I can figure out a good way to save block persistent data (very preferably via NMS) then I can also make this work. My last resort for this is saving chunk coords or regions.