Jump to content

[1.7] Overwrite Vanilla Items


mrkirby153

Recommended Posts

I am working on aliasing.

 

It will be possible to force the "minecraft:milk" name to point at a different block/item, as an example. The original block/item won't be replaced, so much as any requests for it will become diverted. This is not in FML yet.

Link to comment
Share on other sites

You can rename items and blocks through the language files.

But what Railcraft probably do is replacing the recipe.

Write your recipe like the original one, then put your item for the result.

 

I suppose I could do that however whenever rail craft loads up "[fml.itemtracker] A mod "Railcraft" is overwriting item rail" or something similar to that comes up. And you can't find the original rails in the creative menu or nei

Link to comment
Share on other sites

  • 4 weeks later...

I belive RailCraft overwrote the vanilla rails with the itemList[] that existed in 1.6.4. But since that registered items/itemblocks using ID's it was removed in the update. Therefor that wouldn't work in 1.7.4. So we have to wait for cpw's amazing wonders of awesomeness  :D

Link to comment
Share on other sites

My only suggestion is to get into class transformation with core modding. It does require an enormous amount of effort to properly get into, but I find it to be extremely valuable for certain tasks, and it opens up vast amounts of possibilities. This is the tutorial that got me into it: http://www.minecraftforum.net/topic/1854988-/

 

In my mod, I replace both bedrock and coal ore with blocks of a new class to add new behaviour to them. Yes, they aren't items, but it's very much the same process with the same result. To do so, I edit the very code in net.minecraft.block.Block as it is loaded by the JVM. If you follow this method, you will be editing the code in net.minecraft.item.Item, which is very similar. The method you need to modify is the registerItems() method.

 

Iterating through the bytecode instructions, you will be looking for the LDC instruction that loads a string equal to whatever the name of the item you are looking for is. The string name should be equal to the name that is used in net.minecraft.init.Items. You will use this string as an anchor to locate and modify the respective instructions. You should make sure that the string should only be found once, because it is not unusual to see the string two times while the item is being constructed.

 

Say you wish to replace the vanilla feather by replacing the vanilla feather item with your custom ItemFeather item. You will be looking for the LDC instructions that loads the highlighed string below:

itemRegistry.addObject(288, "feather", (new Item()).setUnlocalizedName("feather").setCreativeTab(CreativeTabs.tabMaterials).setTextureName("feather"));

 

After your class transformation, it would programmatically be changed to this:

itemRegistry.addObject(288, "feather", (new ItemFeather()).setUnlocalizedName("feather").setCreativeTab(CreativeTabs.tabMaterials).setTextureName("feather"));

 

The instruction after the LDC would be a NEW instruction of type "net/minecraft/item/Item". You would have to change that to "package/package/etc/ItemFeather". The instruction after that would be a DUP instruction. Instructions after this one will vary. It depends on the amount of constructor parameters, but it usually takes only one instruction to load each parameter. The feather uses no constructor parameters, meaning the instruction after the DUP instruction will be INVOKESPECIAL, which is the constructor call. The owner of this would have to be changed to "package/package/etc/ItemFeather".

 

To apply this example to other cases, here are some tips: If you want to override an item with a new class, make sure your new class extends the original class. The constructor of your new class should have the same parameters as well.

 

This is how I replaced the two blocks in my mod:

public byte[] patchClassBlock(byte[] data, boolean obfuscated)
{
String classBlock = obfuscated ? c.get("Block") : "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";
					}
					coal_oreFound = true;
				}
			}
		}
	}
}

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

 

Have fun!

Link to comment
Share on other sites

  • 11 months later...

This is how I replaced the two blocks in my mod:

public byte[] patchClassBlock(byte[] data, boolean obfuscated)
{
String classBlock = obfuscated ? c.get("Block") : "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";
					}
					coal_oreFound = true;
				}
			}
		}
	}
}

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

 

Have fun!

 

When I try to use the "c.get("Block")" function in my code, I get the error: "c cannot be resolved." Have you defined the variable c offscreen, or is my eclipse debug profiler just buggy?

Link to comment
Share on other sites

I actually tried to figure out the same thing just yesterday, and I managed to do it without a Core Mod. I'm using 1.8, so I'm not sure if my code will work in 1.7.

 

First add those to attributes to your mod class:

private FMLControlledNamespacedRegistry<Item> iItemRegistry;
private Method addObjectRaw;

 

Second add this to your preInit:

try {
    Method getMain = GameData.class.getDeclaredMethod("getMain");
    getMain.setAccessible(true);
    GameData gameData = (GameData) getMain.invoke(null);

    Field f = GameData.class.getDeclaredField("iItemRegistry");
    f.setAccessible(true);
    iItemRegistry = (FMLControlledNamespacedRegistry<Item>) f.get(gameData);

    addObjectRaw = FMLControlledNamespacedRegistry.class.getDeclaredMethod("addObjectRaw", Integer.TYPE, Object.class, Object.class);
    addObjectRaw.setAccessible(true);

} catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

 

The last thing you have to do is call addObjectRaw.invoke in your init:

addObjectRaw.invoke(iItemRegistry, <item it to overwrite>, new ResourceLocation(<resource name>), <item to overwrite with>);

 

 

NOTE: You have to manually replace the Method and Field names with their obfuscated names before releasing the mod or do something like this:

Method getMain;
try {
    try {
        getMain = GameData.class.getDeclaredMethod("getMain");
    } catch(NoSuchMethodException e) {
        getMain = GameData.class.getDeclaredMethod("<Obfuscated name>");
    }
} catch(Exception e) {
}

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.

Announcements



×
×
  • Create New...

Important Information

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