TelepathicGrunt / StructureTutorialMod

A short example showing how to register a structure and get it to generate in all biomes in NeoForge, Forge, and Fabric! (check the branches)
MIT License
261 stars 29 forks source link

Question #10

Closed Scout24HD closed 3 years ago

Scout24HD commented 3 years ago

hi i have a question, how can i add a second structure?

TelepathicGrunt commented 3 years ago

You repeat most of the tutorial again to make the second structure. If this tutorial were to have a second structure, I would make the new structure class that points to the new pool file. Register it in STStructures. Then register the configuredstructure form in STConfiguredStructures. Then add it to the biomes I want.

Scout24HD commented 3 years ago

You repeat most of the tutorial again to make the second structure. If this tutorial were to have a second structure, I would make the new structure class that points to the new pool file. Register it in STStructures. Then register the configuredstructure form in STConfiguredStructures. Then add it to the biomes I want.

i have do that already but now gives a "Exception generating new chunk" error have you any workspace to give an example?

Scout24HD commented 3 years ago

this is the code: (i hope you can help me :))

public void setup2(final FMLCommonSetupEvent event)
{
    event.enqueueWork(() -> {
        STStructures.setupStructures();
        STConfiguredStructures.registerConfiguredStructures();
        //Meteor
        //STStructures.setupStructures();
        //STConfiguredStructures.registerConfiguredStructures();
    });
}

public void biomeModification(final BiomeLoadingEvent event) {
    RegistryKey.getOrCreateKey(Registry.BIOME_KEY, event.getName());
    if (event.getName().equals(new ResourceLocation("boss_tools:moon_biom"))) {
        event.getGeneration().getStructures().add(() -> STConfiguredStructures.CONFIGURED_RUN_DOWN_HOUSE);
    }
    if (event.getName().equals(new ResourceLocation("boss_tools:moon_biom"))) {
        event.getGeneration().getStructures().add(() -> STConfiguredStructures.METEOR_CONFIGURED_RUN_DOWN_HOUSE);
        // event.getGeneration().getStructures().add(() -> STConfiguredStructures.CONFIGURED_RUN_DOWN_HOUSE)
    }
}
private static Method GETCODEC_METHOD;
public void addDimensionalSpacing(final WorldEvent.Load event) {
    if (event.getWorld() instanceof ServerWorld) {
        ServerWorld serverWorld = (ServerWorld) event.getWorld();
        try {
            if (GETCODEC_METHOD == null)
                GETCODEC_METHOD = ObfuscationReflectionHelper.findMethod(ChunkGenerator.class, "func_230347_a_");
            ResourceLocation cgRL = Registry.CHUNK_GENERATOR_CODEC.getKey((Codec<? extends ChunkGenerator>) GETCODEC_METHOD.invoke(serverWorld.getChunkProvider().generator));
            if (cgRL != null && cgRL.getNamespace().equals("terraforged")) return;
        } catch (Exception e) {
            // StructureTutorialMain.LOGGER.error("Was unable to check if " + serverWorld.dimension().location() + " is using Terraforged's ChunkGenerator.");
        }
        if(serverWorld.getChunkProvider().getChunkGenerator() instanceof FlatChunkGenerator &&
                serverWorld.getDimensionType().equals(World.OVERWORLD)){
            return;
        }
        Map<Structure<?>, StructureSeparationSettings> tempMap = new HashMap<>(serverWorld.getChunkProvider().generator.func_235957_b_().func_236195_a_());
        tempMap.putIfAbsent(STStructures.RUN_DOWN_HOUSE.get(), DimensionStructuresSettings.field_236191_b_.get(STStructures.RUN_DOWN_HOUSE.get()));
        //  serverWorld.getChunkProvider().generator.func_235957_b_().func_236195_a_() = tempMap;
        //meteor
       // tempMap.putIfAbsent(STStructures.METEOR.get(), DimensionStructuresSettings.field_236191_b_.get(STStructures.METEOR.get()));
        serverWorld.getChunkProvider().generator.func_235957_b_().field_236193_d_ = tempMap;

        Map<Structure<?>, StructureSeparationSettings> tempMap1 = new HashMap<>(serverWorld.getChunkProvider().generator.func_235957_b_().func_236195_a_());
        //tempMap.putIfAbsent(STStructures.RUN_DOWN_HOUSE.get(), DimensionStructuresSettings.field_236191_b_.get(STStructures.RUN_DOWN_HOUSE.get()));
        //  serverWorld.getChunkProvider().generator.func_235957_b_().func_236195_a_() = tempMap;
        //meteor
        tempMap1.putIfAbsent(STStructures.METEOR.get(), DimensionStructuresSettings.field_236191_b_.get(STStructures.METEOR.get()));
        serverWorld.getChunkProvider().generator.func_235957_b_().field_236193_d_ = tempMap1;

ST CLASS:

public class STStructures {

/**
 * We are using the Deferred Registry system to register our structure as this is the preferred way on Forge.
 * This will handle registering the base structure for us at the correct time so we don't have to handle it ourselves.
 * <p>
 * HOWEVER, do note that Deferred Registries only work for anything that is a Forge Registry. This means that
 * configured structures and configured features need to be registered directly to WorldGenRegistries as there
 * is no Deferred Registry system for them.
 */
public static final DeferredRegister<Structure<?>> DEFERRED_REGISTRY_STRUCTURE = DeferredRegister.create(ForgeRegistries.STRUCTURE_FEATURES, "boss_tools");
//MEteor
public static final DeferredRegister<Structure<?>> METEOR_DEFERRED_REGISTRY_STRUCTURE = DeferredRegister.create(ForgeRegistries.STRUCTURE_FEATURES, "boss_tools");
/**
 * Registers the structure itself and sets what its path is. In this case, the
 * structure will have the resourcelocation of structure_tutorial:run_down_house.
 * <p>
 * It is always a good idea to register your Structures so that other mods and datapacks can
 * use them too directly from the registries. It great for mod/datapacks compatibility.
 * <p>
 * IMPORTANT: Once you have set the name for your structure below and distributed your mod,
 * changing the structure's registry name or removing the structure may cause log spam.
 * This log spam won't break your worlds as forge already fixed the Mojang bug of removed structures wrecking worlds.
 * https://github.com/MinecraftForge/MinecraftForge/commit/56e538e8a9f1b8e6ff847b9d2385484c48849b8d
 * <p>
 * However, users might not know that and think you are to blame for issues that doesn't exist.
 * So it is best to keep your structure names the same as long as you can instead of changing them frequently.
 */
public static final RegistryObject<Structure<NoFeatureConfig>> RUN_DOWN_HOUSE = DEFERRED_REGISTRY_STRUCTURE.register("alien_structure", () -> (new AlienVillageStructure(NoFeatureConfig.field_236558_a_)));
//meteor
public static final RegistryObject<Structure<NoFeatureConfig>> METEOR = METEOR_DEFERRED_REGISTRY_STRUCTURE.register("meteor_structure", () -> (new MeteorStructure(NoFeatureConfig.field_236558_a_)));
/**
 * This is where we set the rarity of your structures and determine if land conforms to it.
 * See the comments in below for more details.
 */
public static void setupStructures() {
    setupMapSpacingAndLand(
            RUN_DOWN_HOUSE.get(), /* The instance of the structure */
            new StructureSeparationSettings(24 /* average distance apart in chunks between spawn attempts */,
                    9 /* minimum distance apart in chunks between spawn attempts */,
                    1234567890 /* this modifies the seed of the structure so no two structures always spawn over each-other. Make this large and unique. */),
            true);

    // Add more structures here and so on
    //meteor
    setupMapSpacingAndLand(
            METEOR.get(), /* The instance of the structure */
            new StructureSeparationSettings(44 /* average distance apart in chunks between spawn attempts */,
                    9 /* minimum distance apart in chunks between spawn attempts */,
                    1234567890 /* this modifies the seed of the structure so no two structures always spawn over each-other. Make this large and unique. */),
            true);
}

/**
 * Adds the provided structure to the registry, and adds the separation settings.
 * The rarity of the structure is determined based on the values passed into
 * this method in the structureSeparationSettings argument.
 * This method is called by setupStructures above.
 */
public static <F extends Structure<?>> void setupMapSpacingAndLand(
        F structure,
        StructureSeparationSettings structureSeparationSettings,
        boolean transformSurroundingLand) {
    /*
     * We need to add our structures into the map in Structure class
     * alongside vanilla structures or else it will cause errors.
     *
     * If the registration is setup properly for the structure,
     * getRegistryName() should never return null.
     */
    Structure.NAME_STRUCTURE_BIMAP.put(structure.getRegistryName().toString(), structure);

    /*
     * Whether surrounding land will be modified automatically to conform to the bottom of the structure.
     * Basically, it adds land at the base of the structure like it does for Villages and Outposts.
     * Doesn't work well on structure that have pieces stacked vertically or change in heights.
     *
     * Note: The air space this method will create will be filled with water if the structure is below sealevel.
     * This means this is best for structure above sealevel so keep that in mind.
     *
     * NOISE_AFFECTING_FEATURES requires AccessTransformer  (See resources/META-INF/accesstransformer.cfg)
     */
    if (transformSurroundingLand) {
        Structure.field_236384_t_ =
                ImmutableList.<Structure<?>>builder()
                        .addAll(Structure.field_236384_t_)
                        .add(structure)
                        .build();

        /*
         * This is the map that holds the default spacing of all structures.
         * Always add your structure to here so that other mods can utilize it if needed.
         *
         * However, while it does propagate the spacing to some correct dimensions from this map,
         * it seems it doesn't always work for code made dimensions as they read from this list beforehand.
         *
         * Instead, we will use the WorldEvent.Load event in StructureTutorialMain to add the structure
         * spacing from this list into that dimension or to do dimension blacklisting properly.
         * We also use our entry in DimensionStructuresSettings.DEFAULTS in WorldEvent.Load as well.
         *
         * DEFAULTS requires AccessTransformer  (See resources/META-INF/accesstransformer.cfg)
         */
        DimensionStructuresSettings.field_236191_b_ =
                ImmutableMap.<Structure<?>, StructureSeparationSettings>builder()
                        .putAll(DimensionStructuresSettings.field_236191_b_)
                        .put(structure, structureSeparationSettings)
                        .build();

        /*
         * There are very few mods that relies on seeing your structure in the noise settings registry before the world is made.
         *
         * You may see some mods add their spacings to DimensionSettings.BUILTIN_OVERWORLD instead of the NOISE_GENERATOR_SETTINGS loop below but
         * that field only applies for the default overworld and won't add to other worldtypes or dimensions (like amplified or Nether).
         * So yeah, don't do DimensionSettings.BUILTIN_OVERWORLD. Use the NOISE_GENERATOR_SETTINGS loop below instead if you must.
         */
        WorldGenRegistries.NOISE_SETTINGS.getEntries().forEach(settings -> {
            Map<Structure<?>, StructureSeparationSettings> structureMap = settings.getValue().getStructures().func_236195_a_();

            /*
             * Pre-caution in case a mod makes the structure map immutable like datapacks do.
             * I take no chances myself. You never know what another mods does...
             *
             * structureConfig requires AccessTransformer  (See resources/META-INF/accesstransformer.cfg)
             */
            if (structureMap instanceof ImmutableMap) {
                Map<Structure<?>, StructureSeparationSettings> tempMap = new HashMap<>(structureMap);
                tempMap.put(structure, structureSeparationSettings);
                //    settings.getValue().getStructures().func_236195_a_() = tempMap;
            } else {
                structureMap.put(structure, structureSeparationSettings);
            }
        });
    }
}

}

STConfiguredST...Class

public class STConfiguredStructures {

    public static StructureFeature<?, ?> CONFIGURED_RUN_DOWN_HOUSE = STStructures.RUN_DOWN_HOUSE.get().withConfiguration(IFeatureConfig.NO_FEATURE_CONFIG);
    public static StructureFeature<?, ?> METEOR_CONFIGURED_RUN_DOWN_HOUSE = STStructures.METEOR.get().withConfiguration(IFeatureConfig.NO_FEATURE_CONFIG);

    public static void registerConfiguredStructures() {
        Registry<StructureFeature<?, ?>> registry = WorldGenRegistries.CONFIGURED_STRUCTURE_FEATURE;
        Registry.register(registry, new ResourceLocation("boss_tools", "configured_run_down_house"), CONFIGURED_RUN_DOWN_HOUSE);
        Registry.register(registry, new ResourceLocation("boss_tools", "meteor_configured_run_down_house"), METEOR_CONFIGURED_RUN_DOWN_HOUSE);

    //    FlatGenerationSettings.field_236932_a_.put(STStructures.RUN_DOWN_HOUSE, CONFIGURED_RUN_DOWN_HOUSE);
        FlatGenerationSettings.STRUCTURES.put(STStructures.RUN_DOWN_HOUSE.get(), CONFIGURED_RUN_DOWN_HOUSE);
        FlatGenerationSettings.STRUCTURES.put(STStructures.METEOR.get(), METEOR_CONFIGURED_RUN_DOWN_HOUSE);
    }
}
Scout24HD commented 3 years ago

ok i have found the error (was a stupid error xD)

TelepathicGrunt commented 3 years ago

Ah nice! You figured it out before i was able to get back on lol.