Vryday_Vrything Posted February 3, 2017 Posted February 3, 2017 (edited) 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); } } } Edited February 4, 2017 by Vryday_Vrything Clarification, formatting, solved
Vryday_Vrything Posted February 4, 2017 Author Posted February 4, 2017 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.
Vryday_Vrything Posted February 4, 2017 Author Posted February 4, 2017 (edited) 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 February 4, 2017 by Vryday_Vrything
Vryday_Vrything Posted February 4, 2017 Author Posted February 4, 2017 There doesn't seem to be much interest in this thread but I've come up with a few solutions that seem to fix any problems with substitution (pending more testing). So, if anyone needs help with it just let me know. I'll try to help.
Cydhra Posted May 31, 2017 Posted May 31, 2017 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.
Vryday_Vrything Posted May 31, 2017 Author Posted May 31, 2017 (edited) 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 May 31, 2017 by Vryday_Vrything
Cydhra Posted June 5, 2017 Posted June 5, 2017 So you did it the hacky way. Hm ok, I need something that works in my case...
Pet0% Posted March 21, 2020 Posted March 21, 2020 I have same problem even in 1.7.10! Nice to see Mojang doing nothing in those 4 updates...
Recommended Posts