Jump to content

[SOLVED] [1.11] Substitution Causes Problems with Generation


Vryday_Vrything

Recommended Posts

When I use addSubstitutionAlias world generation often places an air block instead of my replacement block (see attached screenshot). Any advice on how to fix this?

 

Some more information:

The block appears twice in my BUILDING_BLOCKS tab

The example code below works great, except the above mentioned problems (thanks for any and all help):

 

package com.example.examplemod;

import net.minecraft.block.Block;
import net.minecraft.block.BlockGrass;
import net.minecraft.block.SoundType;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemColored;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.registry.ExistingSubstitutionException;
import net.minecraftforge.fml.common.registry.GameRegistry;

@Mod(modid = ExampleMod.MODID, version = ExampleMod.VERSION)
public class ExampleMod
{
    public static final String MODID = "examplemod";
    public static final String VERSION = "1.0";
    
	@EventHandler
	public void preInit(FMLPreInitializationEvent event) {
		try {
			Block newBlock = new ModBlockGrass();
			Item newItem = new ItemColored(newBlock, false);
			
			ResourceLocation oldName = Block.REGISTRY.getNameForObject(Blocks.GRASS);
			String nameToSubstitute = oldName.toString();	//"minecraft:grass"
			String nameToRegister = ExampleMod.MODID + ":" + oldName.getResourcePath();	//"examplemod:grass"
			
			newBlock.setRegistryName(nameToRegister);
			GameRegistry.addSubstitutionAlias(nameToSubstitute.toString(), GameRegistry.Type.BLOCK, newBlock);
			
			newItem.setRegistryName(nameToRegister);
			GameRegistry.addSubstitutionAlias(nameToSubstitute.toString(), GameRegistry.Type.ITEM, newItem);
		} catch (ExistingSubstitutionException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
    
    public static class ModBlockGrass extends BlockGrass {
    	public ModBlockGrass() {
    		this.setHardness(0.6F);
    		this.setSoundType(SoundType.PLANT);
    		this.setUnlocalizedName("grass");
    	}

		@Override
		public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) {
			System.out.println("ModBlockGrass.onBlockPlacedBy");
			super.onBlockPlacedBy(worldIn, pos, state, placer, stack);
		}
    }
}

 

2017-02-03_08.10.36.png

Edited by Vryday_Vrything
Clarification, formatting, solved
Link to comment
Share on other sites

Just a small update.

I substituted sand instead of grass as a test. Now there is no sand to be found anywhere in the world. Just floating bodies of water and deserts missing the sand layer.

More notably I found blocks along the ocean where sand should be that seem to exist server side only, as I collide with nothing. Those places are unlit. Placing a block there brings it client side, it then becomes a block with a missing texture (black, purple) labelled as "null variant:sand" in the debug info (F3). The light is also updated.

 

The block ID is 12 before and after substitution...

 

Other things seem to work. Placing a block of sand over nothing allows it to fall and return to the new block type. I can craft sandstone from it.

If placed with nothing underneath it falls becomes sand again. I can craft sandstone from it.

Link to comment
Share on other sites

After thinking about this for a little bit and doing a quick test, it seems vanilla biomes are created before the FMLPreInitializationEvent and not updated with substitution. Therefore the old block instance is used. For example, to fix this for sand I can simply do the following for each biome with Blocks.SAND in it:

 

BiomeDesert biomeDesert = (BiomeDesert)Biome.REGISTRY.getObjectById(2);
biomeDesert.topBlock = newBlock.getDefaultState();
biomeDesert.fillerBlock = newBlock.getDefaultState();

 

The index for the biome, in this case "desert", is listed in the net.minecraft.world.biome.Biome class.

 

Edited by Vryday_Vrything
Link to comment
Share on other sites

  • 3 months later...

I tried the same: Replacing grass with a custom grass block. At first I tried it with Forge for Minecraft 1.8.9, but the replacement system was broken. Next with 1.9.4, but the system was still broken.

So I ended up with 1.11.2 and the system seems fixed except for your named issues. I don't mind the duplicate block in inventory, but do you have a good solution for the biome generating problem?
I do not want to switch the top-block because I want to make sure, that certainly not a single grass block is placed: Not by structures, not by custom biomes and so on.

Link to comment
Share on other sites

I must admit that I gave up on Minecraft and went back to my own voxel engine but I'll see what I can find in my old code..............

 

Okay, first, I should mention that static references to the old blocks are the only things that are not changed by substitution. This should mean that other mods, "custom biomes", do not need to be updated in any way (at least I believe this is the case if they are coded properly).

 

You could search for all static references to the block you're replacing and manually write the code to update it but I chose to write some reflection code to automate it for me. I hope this helps, it appears to have worked for me...

 

	Map<Block, Block> blockReplacementMap = new HashMap<Block, Block>();	//Key is old block, value is new block

	void updateBiomeBlocks() {
		for (Iterator<Biome> it = Biome.REGISTRY.iterator(); it.hasNext();) {	//iterate through all registered biomes
			Biome biome = it.next();
			for (Entry<Block, Block> entry : this.blockReplacementMap.entrySet()) {	//iterate through the substitution map
				Block oldBlock = entry.getKey();	//the original block
				Block newBlock = entry.getValue();	//your replacement block
				//Below are the only places I found static references to blocks
				if (biome.theBiomeDecorator.gravelAsSandGen instanceof WorldGenSand) {
					if (oldBlock == ReflectionHelper.getPrivateValue(WorldGenSand.class, (WorldGenSand)biome.theBiomeDecorator.gravelAsSandGen, 0))
						ReflectionHelper.setPrivateValue(WorldGenSand.class, (WorldGenSand)biome.theBiomeDecorator.gravelAsSandGen, newBlock, 0);
				}
				
				if (biome.theBiomeDecorator.sandGen instanceof WorldGenSand) {
					if (oldBlock == ReflectionHelper.getPrivateValue(WorldGenSand.class, (WorldGenSand)biome.theBiomeDecorator.sandGen, 0))
						ReflectionHelper.setPrivateValue(WorldGenSand.class, (WorldGenSand)biome.theBiomeDecorator.sandGen, newBlock, 0);
				}
			}
		}
	}

 

Edited by Vryday_Vrything
Link to comment
Share on other sites

  • 2 years later...
  • Guest locked this topic
Guest
This topic is now closed to further replies.

Announcements



×
×
  • Create New...

Important Information

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