Jump to content

[1.20.4] Custom ore feature causes extreme amount of generation lag.


chxr

Recommended Posts

So i have a custom ore and, arround the ore, a bunch of randomly placed custom stone blocks should be placed. After applying it, i've found that it causes moderate to extreme world generation lag (new chunks refusing to load after moving for a while, height slices of the same chunk appearing and disappearing as I get into them instead of the usual long continous chunk, new chunks generating extremely close to me instead of to the set render distance...)

I've been debugging for a while and I know for a fact this is causing the lag (and sometimes freeze of the world loading screen on a new world and/or the saving world screen when quitting), since comenting it just makes the worldgen work as usual and I want to see if its really that computationally expensive, if there are other ways of doing it or if the process can be simplfied or optimized. I've tried a lot of combinations for the same code but I am just stuck. Is it some kind of generation cascading im missing?

 

Here is the code for the class. The code inside the if (placed) is the one causing this mess. I can see that the code might not be the most optimized thing, but it does what's supposed to... but at the cost of causing all this. Any tips?

package es.nullbyte.relativedimensions.worldgen.oregen.oreplacements;

import es.nullbyte.relativedimensions.blocks.BlockInit;
import es.nullbyte.relativedimensions.blocks.ModBlockTags;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.OreFeature;
import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration;

import java.util.Optional;

public class AberrantOreFeature extends OreFeature {
    public AberrantOreFeature() {
        super(OreConfiguration.CODEC);
    }


    @Override
    public boolean place(FeaturePlaceContext<OreConfiguration> ctx) {
        // Get the world and the position from the context
        WorldGenLevel world = ctx.level();
        BlockPos origin = ctx.origin();

        // Offset the origin by 8 in the x and z directions to avoid cascading chunk generation
        BlockPos offsetOrigin = origin.offset(8, 0, 8);

        // Create a new context with the offset origin
        FeaturePlaceContext<OreConfiguration> offsetCtx = new FeaturePlaceContext<>(
                Optional.empty(), world, ctx.chunkGenerator(), ctx.random(), offsetOrigin, ctx.config()
        );

        // Generate the entire vein of ore at the offset origin
        boolean placed = super.place(offsetCtx);

        // If the vein was generated successfully
        if (placed) {
            // Define the block to replace surrounding blocks with
            BlockState surroundingBlockState = BlockInit.ABERRANT_MINERALOID.get().defaultBlockState();

            // Generate a random size for the area of corruption
            int areaSizeX = ctx.random().nextInt(3) + 1; // between 1 and 4
            int areaSizeY = ctx.random().nextInt(3) + 1; // between 1 and 4
            int areaSizeZ = ctx.random().nextInt(3) + 1; // between 1 and 4

            // Calculate the number of blocks to be corrupted based on the area size
            double numBlocksToCorrupt = (areaSizeX + areaSizeY + areaSizeZ / 2.0) ; 

            // Counter for the number of blocks corrupted
            int numBlocksCorrupted = 0;

            // Loop for each block to be corrupted
            while (numBlocksCorrupted < numBlocksToCorrupt) {
                // Generate a random position within the area, using the offset origin
                BlockPos randomPos = offsetOrigin.offset(
                        ctx.random().nextInt(2 * areaSizeX + 1) - areaSizeX, // between -areaSize and areaSize
                        ctx.random().nextInt(2 * areaSizeY + 1) - areaSizeY,
                        ctx.random().nextInt(2 * areaSizeZ + 1) - areaSizeZ
                );

                // If the block at the random position is in the IS_ORE_ABERRANTABLE tag, replace it
                if (world.getBlockState(randomPos).is(ModBlockTags.STONE_ABERRANTABLE)) {
                    world.setBlock(randomPos, surroundingBlockState, 2);
                    numBlocksCorrupted++;
                }
            }
        }
        return placed;
    }
}

 

Link to comment
Share on other sites

Your mod is probably lagging during world generation due to how it replaces blocks around your custom ore. Right now, it randomly picks spots around the ore and changes blocks there. This process can be slow, especially if it's dealing with lots of blocks or a big area. To fix it, try replacing fewer blocks, picking spots more efficiently, and changing blocks in a smarter way. This should help your mod run smoother when generating worlds. Here is an example of how you can do this

// Inside the if (placed) block
if (placed) {
    BlockState surroundingBlockState = BlockInit.ABERRANT_MINERALOID.get().defaultBlockState();
    int veinSize = ctx.config().size;
    int maxBlocksToReplace = (int) Math.ceil(veinSize * 0.1); // Replace 10% of vein size
    int numBlocksToCorrupt = Math.min(maxBlocksToReplace, 1000); // Limit to 1000 blocks
    List<BlockPos> positionsToReplace = new ArrayList<>();
    
    // Loop until reaching the limit of blocks to replace
    while (positionsToReplace.size() < numBlocksToCorrupt) {
        BlockPos randomPos = offsetOrigin.offset(
            ctx.random().nextInt(2 * areaSizeX + 1) - areaSizeX,
            ctx.random().nextInt(2 * areaSizeY + 1) - areaSizeY,
            ctx.random().nextInt(2 * areaSizeZ + 1) - areaSizeZ
        );
        
        if (world.getBlockState(randomPos).is(ModBlockTags.STONE_ABERRANTABLE)) {
            positionsToReplace.add(randomPos);
        }
    }
    
    // Replace blocks in bulk
    for (BlockPos pos : positionsToReplace) {
        world.setBlock(pos, surroundingBlockState, 2);
    }
}

If you've tried more effective ways to generate your blocks around your ores, it may also be because of issues on your side, not the mod. Adjust the parameters as needed based on your performance testing and requirements.

Link to comment
Share on other sites

9 minutes ago, AwesomeDev said:

Your mod is probably lagging during world generation due to how it replaces blocks around your custom ore. Right now, it randomly picks spots around the ore and changes blocks there. This process can be slow, especially if it's dealing with lots of blocks or a big area. To fix it, try replacing fewer blocks, picking spots more efficiently, and changing blocks in a smarter way. This should help your mod run smoother when generating worlds. Here is an example of how you can do this

// Inside the if (placed) block
if (placed) {
    BlockState surroundingBlockState = BlockInit.ABERRANT_MINERALOID.get().defaultBlockState();
    int veinSize = ctx.config().size;
    int maxBlocksToReplace = (int) Math.ceil(veinSize * 0.1); // Replace 10% of vein size
    int numBlocksToCorrupt = Math.min(maxBlocksToReplace, 1000); // Limit to 1000 blocks
    List<BlockPos> positionsToReplace = new ArrayList<>();
    
    // Loop until reaching the limit of blocks to replace
    while (positionsToReplace.size() < numBlocksToCorrupt) {
        BlockPos randomPos = offsetOrigin.offset(
            ctx.random().nextInt(2 * areaSizeX + 1) - areaSizeX,
            ctx.random().nextInt(2 * areaSizeY + 1) - areaSizeY,
            ctx.random().nextInt(2 * areaSizeZ + 1) - areaSizeZ
        );
        
        if (world.getBlockState(randomPos).is(ModBlockTags.STONE_ABERRANTABLE)) {
            positionsToReplace.add(randomPos);
        }
    }
    
    // Replace blocks in bulk
    for (BlockPos pos : positionsToReplace) {
        world.setBlock(pos, surroundingBlockState, 2);
    }
}

If you've tried more effective ways to generate your blocks around your ores, it may also be because of issues on your side, not the mod. Adjust the parameters as needed based on your performance testing and requirements.

I've tested the same code on three different envionrments (Desktop win10, desktop Linux and Laptop Linux) and it kinda blows up all the same. Gonna try this code and see if i can tune it

Link to comment
Share on other sites

38 minutes ago, AwesomeDev said:

Your mod is probably lagging during world generation due to how it replaces blocks around your custom ore. Right now, it randomly picks spots around the ore and changes blocks there. This process can be slow, especially if it's dealing with lots of blocks or a big area. To fix it, try replacing fewer blocks, picking spots more efficiently, and changing blocks in a smarter way. This should help your mod run smoother when generating worlds. Here is an example of how you can do this

// Inside the if (placed) block
if (placed) {
    BlockState surroundingBlockState = BlockInit.ABERRANT_MINERALOID.get().defaultBlockState();
    int veinSize = ctx.config().size;
    int maxBlocksToReplace = (int) Math.ceil(veinSize * 0.1); // Replace 10% of vein size
    int numBlocksToCorrupt = Math.min(maxBlocksToReplace, 1000); // Limit to 1000 blocks
    List<BlockPos> positionsToReplace = new ArrayList<>();
    
    // Loop until reaching the limit of blocks to replace
    while (positionsToReplace.size() < numBlocksToCorrupt) {
        BlockPos randomPos = offsetOrigin.offset(
            ctx.random().nextInt(2 * areaSizeX + 1) - areaSizeX,
            ctx.random().nextInt(2 * areaSizeY + 1) - areaSizeY,
            ctx.random().nextInt(2 * areaSizeZ + 1) - areaSizeZ
        );
        
        if (world.getBlockState(randomPos).is(ModBlockTags.STONE_ABERRANTABLE)) {
            positionsToReplace.add(randomPos);
        }
    }
    
    // Replace blocks in bulk
    for (BlockPos pos : positionsToReplace) {
        world.setBlock(pos, surroundingBlockState, 2);
    }
}

If you've tried more effective ways to generate your blocks around your ores, it may also be because of issues on your side, not the mod. Adjust the parameters as needed based on your performance testing and requirements.

Ok so this specific code freezes the game on world creation. This is what gets me so confused, i get that it might not be the best thing, but is it really so generation heavy?

Link to comment
Share on other sites

I think i've found a more "generation friendly way" of generating random blobs of mineral around the ore. This both does the trick and make the generation work flawlessly (albeit i need to make some adjustments). I just ended up thinking "MAYBE there is another Feature I can use to place the minerals instead of doing it manually" And, low and behold, SCATTERED_ORE  is actually a thing. I don't really know how "orthodox" this solution is, but it works and rids me of all the problems I had witht my original "manual" implementation.

If anybody has any insight on why my original class could've been causing lag to the point of freezes and chunk generation just refusing to keep loading new chunks, I'm also all ears:

 

Here is the full if (placed) block for anyone with a smiliar issue:

        if (placed) {
            // Define the block to replace surrounding blocks with
            BlockState surroundingBlockState = BlockInit.ABERRANT_MINERALOID.get().defaultBlockState();
            RuleTest stoneReplacement = new TagMatchTest(BlockTags.STONE_ORE_REPLACEABLES); //Tag which indicates ores that can replace stone
            RuleTest deepslateReplacement = new TagMatchTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES); //Tag which indicates ores that can replace deepslate

            // Create a list of TargetBlockState for the Aberrant Mineraloids
            List<OreConfiguration.TargetBlockState> targets = new ArrayList<>();
            targets.add(OreConfiguration.target(stoneReplacement, surroundingBlockState));
            targets.add(OreConfiguration.target(deepslateReplacement, surroundingBlockState));

            // Create a new OreConfiguration for the Aberrant Mineraloids
            OreConfiguration mineraloidConfig = new OreConfiguration(targets, 9);  // vein size

            // Create a new context for the Aberrant Mineraloids
            FeaturePlaceContext<OreConfiguration> mineraloidCtx = new FeaturePlaceContext<>(
                    Optional.empty(), world, ctx.chunkGenerator(), ctx.random(), offsetOrigin, mineraloidConfig
            );

            // Generate the Aberrant Mineraloids using the SCATTERED_ORE configuration
            boolean mineraloidsPlaced = Feature.SCATTERED_ORE.place(mineraloidCtx);
        }

 

Link to comment
Share on other sites

I might have an idea why your original method was causing so much trouble. See this while loop?

17 hours ago, chxr said:
            while (numBlocksCorrupted < numBlocksToCorrupt) {
                // Generate a random position within the area, using the offset origin
                BlockPos randomPos = offsetOrigin.offset(
                        ctx.random().nextInt(2 * areaSizeX + 1) - areaSizeX, // between -areaSize and areaSize
                        ctx.random().nextInt(2 * areaSizeY + 1) - areaSizeY,
                        ctx.random().nextInt(2 * areaSizeZ + 1) - areaSizeZ
                );

                // If the block at the random position is in the IS_ORE_ABERRANTABLE tag, replace it
                if (world.getBlockState(randomPos).is(ModBlockTags.STONE_ABERRANTABLE)) {
                    world.setBlock(randomPos, surroundingBlockState, 2);
                    numBlocksCorrupted++;
                }
            }

You're only incrementing the number of blocks you've corrupted if you find one that you can corrupt. What happens if you can't find any? The while loop will run forever (a long time). This could happen if, for instance, the feature generates inside a vein of blocks that aren't marked as STONE_ABERRANTABLE.

There are two alternate strategies I'd recommend to fix this. 

First, you could simply increment numBlockCorrupted regardless of whether you've actually corrupted the block. This is the simplest and quickest way, and it should ensure that the loop runs no more than numBlocksToCorrupt times. 

Alternatively, you could add a "kill switch" that keeps track of how many times the loop runs, and then ends it after a certain limit of your choosing. That could look something like this: 

			// Keeps track of how many blocks have been checked so far.
			int numBlocksChecked = 0;
			// Check up to twice as many blocks as you actually want to corrupt.
			// This is a good compromise between speed and actually getting the number of blocks
			// that you want to corrupt.
			int numBlocksToCheck = numBlocksToCorrupt * 2; 
			// Modified the while loop condition to end after a certain number of blocks are checked.
			while (numBlocksCorrupted < numBlocksToCorrupt && numBlocksChecked < numBlocksToCheck) {
                // Generate a random position within the area, using the offset origin
                BlockPos randomPos = offsetOrigin.offset(
                        ctx.random().nextInt(2 * areaSizeX + 1) - areaSizeX, // between -areaSize and areaSize
                        ctx.random().nextInt(2 * areaSizeY + 1) - areaSizeY,
                        ctx.random().nextInt(2 * areaSizeZ + 1) - areaSizeZ
                );

                // If the block at the random position is in the IS_ORE_ABERRANTABLE tag, replace it
                if (world.getBlockState(randomPos).is(ModBlockTags.STONE_ABERRANTABLE)) {
                    world.setBlock(randomPos, surroundingBlockState, 2);
                    numBlocksCorrupted++;
                }
              	
              	// Increment the number of blocks that you've checked.
              	numBlocksChecked++;
            }

Let me know if you're still running into lag problems or are confused by my explanation.

Link to comment
Share on other sites

4 hours ago, scientistknight1 said:

I might have an idea why your original method was causing so much trouble. See this while loop?

You're only incrementing the number of blocks you've corrupted if you find one that you can corrupt. What happens if you can't find any? The while loop will run forever (a long time). This could happen if, for instance, the feature generates inside a vein of blocks that aren't marked as STONE_ABERRANTABLE.

There are two alternate strategies I'd recommend to fix this. 

First, you could simply increment numBlockCorrupted regardless of whether you've actually corrupted the block. This is the simplest and quickest way, and it should ensure that the loop runs no more than numBlocksToCorrupt times. 

Alternatively, you could add a "kill switch" that keeps track of how many times the loop runs, and then ends it after a certain limit of your choosing. That could look something like this: 

			// Keeps track of how many blocks have been checked so far.
			int numBlocksChecked = 0;
			// Check up to twice as many blocks as you actually want to corrupt.
			// This is a good compromise between speed and actually getting the number of blocks
			// that you want to corrupt.
			int numBlocksToCheck = numBlocksToCorrupt * 2; 
			// Modified the while loop condition to end after a certain number of blocks are checked.
			while (numBlocksCorrupted < numBlocksToCorrupt && numBlocksChecked < numBlocksToCheck) {
                // Generate a random position within the area, using the offset origin
                BlockPos randomPos = offsetOrigin.offset(
                        ctx.random().nextInt(2 * areaSizeX + 1) - areaSizeX, // between -areaSize and areaSize
                        ctx.random().nextInt(2 * areaSizeY + 1) - areaSizeY,
                        ctx.random().nextInt(2 * areaSizeZ + 1) - areaSizeZ
                );

                // If the block at the random position is in the IS_ORE_ABERRANTABLE tag, replace it
                if (world.getBlockState(randomPos).is(ModBlockTags.STONE_ABERRANTABLE)) {
                    world.setBlock(randomPos, surroundingBlockState, 2);
                    numBlocksCorrupted++;
                }
              	
              	// Increment the number of blocks that you've checked.
              	numBlocksChecked++;
            }

Let me know if you're still running into lag problems or are confused by my explanation.

Yeah I had a similar idea, at some point I also just got the numBlocksCorrupted++ out of the if block so it would just do a loop numBlocksCorrupted amount of times but it still caused some troubles. I've ended up using an extra feature to generate the blob around the ore and its working wonders so I won't be scratching my head much longer with it

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.