Jump to content

[1.7.2] Replace vanilla block


Frepo

Recommended Posts

I'm very surprised that there are no solutions, or hooks, or whatever.. not even discussions on the topic (none that I can find anyway). So here we go..

 

Has anyone found a way to replace vanilla blocks?

As most of us know by now that the block registry system has changed dramatically. In 1.6.x you just wrote

 

Block.blocksList[id] = null;

myBlock = new MyBlockClass(666);

Block.blocksList[id] = myBlock;

 

and the vanilla block had magically changed to myBlock!

 

Now we have the Block.blockRegistry to play around with, but its no fun playmate! :)

I've been tinkering with putObject and addObject (in an attempt to overwrite the block listed at the specific index)... sadly with no success.

 

Shouldn't there be a hook in the SimpleRegistry class "overwriteObject(int, object)" or something, that simply replaces the object at specified index with the object passed in)? There must be many many modders out there who (like me) don't just want to add new stuff. I mean why bother with i.e. new food stuff, isn't it easy enough already to stay well fed in minecraft? That my friends is some FOOD for thoughts! errm.. (pardon my low sense of humor).

 

Anyway....is there a way?

 

Link to comment
Share on other sites

Indeed I could. Only problem being that I want to replace dirt. I'm afraid there simply is no workaround here. Replacing vanilla blocks is something I MUST be able to do to have my mod become what I want it to be.

 

There is sooooo much you can do with the ability to replace blocks. I see questions like i.e. "how do I add particles to this block?", "how do I remove gravity from that block?", "how do I make cacti do more damage?"... the answer to sooo much is replace the block (or item) with your custom block/item, copy paste or extend the vanilla block/item and have it do whatever you please! make a rose squeal like a pig for all I care! :)

Link to comment
Share on other sites

Oh hi GotoLink! Haha this is the second time I have you pointing your finger at me :D ... last time being (if recall) on a similar topic but in 1.6 involving the furnace I believe.

Sure, events are great! Whenever I can I use them of course. But surely you understand that they do not cover all of ones needs. If we encounter a situation where there is no hook to save us, we'll simply have to find alternate ways.

 

Not recommended to replace blocks? I was hoping my "second thoughts" earlier would hint that I'm aware of that. I'm not gonna try to have my mod in any FTB packs or anything, it's going all solo! :) Don't you worry about that.

 

Now, whats recommended or not is not helping me here. Come on guys, let's work together here! Time to replace them blocks!

 

(in complete awareness of what is recommended or not for YOUR mod of course)

Link to comment
Share on other sites

If you're still interested in programmatically replacing the blocks, here's how I did it to replace the coal ore block and bedrock block. The following paste involves heavy use of ASM, so you have been warned. If you have no knowledge of class transforming or ASM, you can either read it up or simply ignore this.

 

	public byte[] patchClassBlock(byte[] data, boolean obfuscated)
{
	String classBlock = obfuscated ? "ahu" : "net/minecraft/block/Block";

	String methodRegisterBlocks = obfuscated ? "p" : "registerBlocks";
	String methodSetHardness = obfuscated ? "c" : "setHardness";

	ClassNode classNode = new ClassNode();
	ClassReader classReader = new ClassReader(data);
	classReader.accept(classNode, 0);

	boolean bedrockFound = false;
	boolean coal_oreFound = false;

	for(int i = 0; i < classNode.methods.size(); i++)
	{
		MethodNode method = classNode.methods.get(i);
		if(method.name.equals(methodRegisterBlocks) && method.desc.equals("()V"))
		{
			for(int j = 0; j < method.instructions.size(); j++)
			{
				AbstractInsnNode instruction = method.instructions.get(j);
				if(instruction.getOpcode() == LDC)
				{
					LdcInsnNode ldcInstruction = (LdcInsnNode)instruction;
					if(ldcInstruction.cst.equals("bedrock"))
					{
						if(!bedrockFound)
						{
							((TypeInsnNode)method.instructions.get(j + 1)).desc = "glenn/gases/BlockBedrock";
							((MethodInsnNode)method.instructions.get(j + 4)).owner = "glenn/gases/BlockBedrock";
						}
						bedrockFound = true;
					}
					else if(ldcInstruction.cst.equals("coal_ore"))
					{
						if(!coal_oreFound)
						{
							((TypeInsnNode)method.instructions.get(j + 1)).desc = "glenn/gases/BlockCoalOre";
							((MethodInsnNode)method.instructions.get(j + 3)).owner = "glenn/gases/BlockCoalOre";
							((MethodInsnNode)method.instructions.get(j + 5)).owner = "glenn/gases/BlockCoalOre";
						}
						coal_oreFound = true;
					}
				}
			}
		}
	}

	ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
	classNode.accept(writer);
	return writer.toByteArray();
}

The solution works by simply replacing the initialization of the blocks in Block, which involves altering a NEW instruction and an INVOKESPECIAL (constructor call) instruction. It's important to note that this has to be adapted for certain situations, with factors such as constructor parameters and builder methods (setName, setHardness, setLightlevel, etc.).

I had to change the owner of the setHardness() call in the coal ore block's initialization, because that's the way java is.

 

It's pretty much programmatically changing this:

Block b = (Block)new BlockSomething().setName("b").setHardness...etc;

To this:

Block b = (Block)new BlockSomethingElse().setName("b").setHardness...etc;

 

I still recommend using events as much as you can, but we all know we can't cover everything without doing some core modding.

Link to comment
Share on other sites

Yeah I've read some about it, the code injection part really caught my attention. I'm considering learning it since it seems to offer some really neat soultions to many of my problems. Hell I've even considered skipping API's like Forge altogether and learn how to purely do coremodding. But then again, I think it would be too time consuming working with obfuscated code, having no help in getting my code into minecraft and so on. There's simply not enough time when you're working 5 days a week.

Anyway, haha slipping off topic here :) I'm gonna look into this! Thanks for the tip bro!

Link to comment
Share on other sites

I've been trying to solve this too, very crucial for my mod and can't be done with events (I don't want to use coremods either). Here's something I came up with, it uses nasty amount of reflection so it might and probably will break with updates. I have tested it a bit, and it seems to work fine, so let me know if you have any issues with it.

 

public class BlockReplaceHelper{
public static boolean replaceBlock(Block toReplace, Class<? extends Block> blockClass){
	Field modifiersField=null;
    	try{
    		modifiersField=Field.class.getDeclaredField("modifiers");
    		modifiersField.setAccessible(true);
    		
    		for(Field field:Blocks.class.getDeclaredFields()){
        		if (Block.class.isAssignableFrom(field.getType())){
    				Block block=(Block)field.get(null);
    				if (block==toReplace){
    					String registryName=Block.blockRegistry.getNameForObject(block);
    					int id=Block.getIdFromBlock(block);
    					ItemBlock item=(ItemBlock)Item.getItemFromBlock(block);
    					System.out.println("Replacing block - "+id+"/"+registryName);
    					
    					Block newBlock=blockClass.newInstance();
    					FMLControlledNamespacedRegistry<Block> registry=GameData.blockRegistry;
    					registry.putObject(registryName,newBlock);
    					
    					Field map=RegistryNamespaced.class.getDeclaredFields()[0];
    					map.setAccessible(true);
    					((ObjectIntIdentityMap)map.get(registry)).func_148746_a(newBlock,id);
    					
    					map=FMLControlledNamespacedRegistry.class.getDeclaredField("namedIds");
    					map.setAccessible(true);
    					((BiMap)map.get(registry)).put(registryName,id);
    					
    					field.setAccessible(true);
    					int modifiers=modifiersField.getInt(field);
    					modifiers&=~Modifier.FINAL;
    					modifiersField.setInt(field,modifiers);
    					field.set(null,newBlock);
    					
    					Field itemblock=ItemBlock.class.getDeclaredFields()[0];
    					itemblock.setAccessible(true);
    					modifiers=modifiersField.getInt(itemblock);
    					modifiers&=~Modifier.FINAL;
    					modifiersField.setInt(itemblock,modifiers);
    					itemblock.set(item,newBlock);
    					
    					System.out.println("Check field: "+field.get(null).getClass());
    					System.out.println("Check registry: "+Block.blockRegistry.getObjectById(id).getClass());
    					System.out.println("Check item: "+((ItemBlock)Item.getItemFromBlock(newBlock)).field_150939_a.getClass());
    				}
        		}
        	}
    	}catch(Exception e){
    		e.printStackTrace();
    		return false;
    	}
    	return true;
}
}

 

Example usage (in preinit):

BlockReplaceHelper.replaceBlock(Blocks.dragon_egg,BlockDragonEggCustom.class);

Link to comment
Share on other sites

  • 2 years later...

I've been trying to solve this too, very crucial for my mod and can't be done with events (I don't want to use coremods either). Here's something I came up with, it uses nasty amount of reflection so it might and probably will break with updates. I have tested it a bit, and it seems to work fine, so let me know if you have any issues with it.

 

public class BlockReplaceHelper{
public static boolean replaceBlock(Block toReplace, Class<? extends Block> blockClass){
	Field modifiersField=null;
    	try{
    		modifiersField=Field.class.getDeclaredField("modifiers");
    		modifiersField.setAccessible(true);
    		
    		for(Field field:Blocks.class.getDeclaredFields()){
        		if (Block.class.isAssignableFrom(field.getType())){
    				Block block=(Block)field.get(null);
    				if (block==toReplace){
    					String registryName=Block.blockRegistry.getNameForObject(block);
    					int id=Block.getIdFromBlock(block);
    					ItemBlock item=(ItemBlock)Item.getItemFromBlock(block);
    					System.out.println("Replacing block - "+id+"/"+registryName);
    					
    					Block newBlock=blockClass.newInstance();
    					FMLControlledNamespacedRegistry<Block> registry=GameData.blockRegistry;
    					registry.putObject(registryName,newBlock);
    					
    					Field map=RegistryNamespaced.class.getDeclaredFields()[0];
    					map.setAccessible(true);
    					((ObjectIntIdentityMap)map.get(registry)).func_148746_a(newBlock,id);
    					
    					map=FMLControlledNamespacedRegistry.class.getDeclaredField("namedIds");
    					map.setAccessible(true);
    					((BiMap)map.get(registry)).put(registryName,id);
    					
    					field.setAccessible(true);
    					int modifiers=modifiersField.getInt(field);
    					modifiers&=~Modifier.FINAL;
    					modifiersField.setInt(field,modifiers);
    					field.set(null,newBlock);
    					
    					Field itemblock=ItemBlock.class.getDeclaredFields()[0];
    					itemblock.setAccessible(true);
    					modifiers=modifiersField.getInt(itemblock);
    					modifiers&=~Modifier.FINAL;
    					modifiersField.setInt(itemblock,modifiers);
    					itemblock.set(item,newBlock);
    					
    					System.out.println("Check field: "+field.get(null).getClass());
    					System.out.println("Check registry: "+Block.blockRegistry.getObjectById(id).getClass());
    					System.out.println("Check item: "+((ItemBlock)Item.getItemFromBlock(newBlock)).field_150939_a.getClass());
    				}
        		}
        	}
    	}catch(Exception e){
    		e.printStackTrace();
    		return false;
    	}
    	return true;
}
}

 

Example usage (in preinit):

BlockReplaceHelper.replaceBlock(Blocks.dragon_egg,BlockDragonEggCustom.class);

 

What if I wanted to replace a class extending EntityFX? Would that be possible too?

Link to comment
Share on other sites

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.