The Meaning of Life, the Universe, and Everything.
Join Date:
6/11/2014
Posts:
59
Member Details
The Unofficial [1.7.10] Minecraft Modding for Dummies* *This tutorial and its author have no affiliation with the For Dummies series of books or its publisher.
This tutorial assumes you know how to read English and are familiar with using a Windows computer. It does not assume you are a programmer, but it does assume you're willing to learn. It provides a step-by-step guide to starting from scratch and gives you everything you need to write your own mod. By the end, you'll be able to add custom items, blocks, ore, and more! That said, this tutorial is but the tip of an iceberg and is not intended as a comprehensive guide. There is far more to Minecraft modding than is covered here.
The Meaning of Life, the Universe, and Everything.
Join Date:
6/11/2014
Posts:
59
Member Details
1: Overview
What is Forge?
Forge is itself a mod that provides important tools for loading and building more mods. If you want to use or make mods, then you need Forge. But you'll need to know which version.
What is Eclipse?
Eclipse is a text editor for code and can run the written code in game for testing. It has a lot of advanced features, but you really only need two: the run button and the output screen.
The output screen is known as the console. It displays any errors and other information when you run your code. Don't worry too much if you don't understand any of it. Errors can be copied and pasted to the modding forum for help when needed.
What is Java?
Java is a programming language. A very powerful one! If you don't have any experience writing code, then it can look intimidating, but it shouldn't. The basics of Java are easy to explain and there are a number of great tutorials online. Best of all, when working with sample code, it isn't necessary to learn everything at once. With just a basic understanding, you can copy and paste a lot of what you need and figure the details out as you go.
What is Gradle?
Gradle manages mod distribution. Once you're done writing your code, you don't want to keep running it thru Eclipse, you want to turn it into a jar file that can be easily shared with others. Gradle does that.
Which Version?
The latest version of Minecraft keeps changing. This can be a problem for earlier mods. Fortunately, Minecraft allows you to select the version you want to run. However, different versions of Minecraft require different versions of Forge and each version of Forge must be installed separately. Forge allows for multiple installations and adds each one to the version selection screen in Minecraft. The two versions are then typically displayed together, separated by a hyphen.
By the end of that tutorial, your workspace should look something like this.
However, for newer versions of Forge, the "Run Configurations" "Main class" setting needs to be changed to "GradleStart".
And the "Program arguments" text box should be empty.
The "VM arguments" should remain.
Here is how to start a new project by another name. Close the previous project to get it out of way, or delete it from the workspace entirely. When deleting a project from the workspace, be sure the checkbox next to "delete project contents on disk" is not ticked. Otherwise, all files related to that project will be deleted from your hard drive.
Now, from the "File" menu, select "New", "Java Project". Give your project a name and click "Next".
On the "Source" tab, right-click the folder named "src" and select "Remove from build path".
Then right-click the folder above it, select "New Source Folder", and name it "java".
Repeat to create another folder by the name of "resources".
Go to the "Projects" tab and click "Add". Tick the checkbox next to forge and click "OK", then "Finish".
Don't forget to delete the useless "src" folder from the workspace. Then go to the "Run" menu and select "Run Configurations". On the "Classpath" tab, under "User Entries", the previous project will still be listed. Select the previous project and click "Remove".
Select "User Entries" and click "Add Projects". Tick the checkbox next to the new project and untick the two check boxes labeled "Add exported..." and "Add required...", then click "OK".
The Meaning of Life, the Universe, and Everything.
Join Date:
6/11/2014
Posts:
59
Member Details
3: Your First Mod
Create a new project. Right-click the "java" folder then select "New", "Package". Name the package "whysalives.minecraft.helloworld" and click "Finish".
Package names are really just a directory structure, with each word after a period representing the name of a subfolder within the folder before it. Thus any files placed in this package will appear in the folder path "..\whysalives\minecraft\helloworld\". You can read more about naming packages in the next chapter.
Now right-click the package and select "New", "Class". Name the class "HelloWorld" and click "Finish".
The new file will already contain some text.
Replace all of the text with the "HelloWorld.java" code below, then go to the "File" menu and select "Save".
HelloWorld.java
package whysalives.minecraft.helloworld;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//===========================================================================
// HelloWorld class
//===========================================================================
/**
* A Minecraft-Forge test-mod that outputs "Hello.. World!" to the console.
*
* @version {@value HelloWorld#VERSION}
* @author Ryan F. Mercer -Open source.
*/
@Mod(modid = HelloWorld.MODID, version = HelloWorld.VERSION)
public class HelloWorld {
public static final String MODID = "helloworld"; //lowercase, no spaces.
public static final String VERSION = "vymdt.01.2014.08.18.0000";
public String testStr = "Hello.. World!";
//===========================================================================
/**
* Does nothing.
*
* @param event
*/
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
//do nothing.
}
//===========================================================================
/**
* Main mod method.
*
* @param event
*/
@EventHandler
public void init(FMLInitializationEvent event) {
//output to console.
System.out.println(); //newline.
System.out.println( testStr );
System.out.println(); //newline.
}
//===========================================================================
/**
* Does nothing.
*
* @param event
*/
@EventHandler
public void postInit(FMLPostInitializationEvent event) {
//do nothing.
}
//===========================================================================
}//END class
//===========================================================================
At this point, it's important to mention a setting in Eclipse, known as the "Compiler compliance level". The default setting depends on which version of Java is installed. Basically, when Eclipse reads the code, it expects it to meet certain Java standards, and different versions of Java have different standards. The wrong compliance level can cause an error that prevents your mod from running. What you want is level 1.7, which is the likely default if you've installed Java 7. If you've installed Java 8, then the likely default is 1.8. But that's not a problem because all you need to do is change the setting.
Change Setting
In Eclipse, under the "Package Explorer", right-click the "Hello World" project folder and select "Properties". Now select "Java Compiler" from the menu on the left. If not ticked, tick the box labeled "Enable project specific settings". If ticked, untick the box labeled "Use compliance from execution environment...". Then from the drop-down box labeled "Compiler compliance level", select "1.7".
If level 1.7 isn't listed, then first uninstall your current version of Java, then install the Java Development Kit (JDK) version 7 or 8.
Now run the mod. When run in Eclipse, this mod will write "Hello.. World!" to the console. You may need to scroll back to find it.
Did you find it? Great! Pat yourself on the back, you're a modder.
Now that you've written a mod, you may wish to include some in game information about it, such as the author, a web address, or even credits to those whose code you borrowed to help build it.
Right-click the "resources" folder and select "New", "File". Name the file "mcmod.info" and click "Finish".
Copy the "mcmod.info" code below into the new file, then save.
mcmod.info
/**
* @version vymdt.01.2014.08.18.0000
* @author Ryan F. Mercer -Open source.
*
* In-game info for project: Hello World.
*/
[ { "modid" : "helloworld"
, "name" : "Hello World"
, "description" : "A Minecraft-Forge test-mod that outputs \"Hello.. World!\" to the console."
, "version" : "vymdt.01.2014.08.18.0000"
, "mcversion" : "1.7.10"
, "url" : ""
, "updateUrl" : ""
, "authorList" : ["Ryan F. Mercer"]
, "credits" : "MrrGingerNinja, Wuppy, IanTheSlime, finalformsora02"
, "logoFile" : ""
, "screenshots" : []
, "dependencies" : ["Forge10.13.0.1180"]
}
]
Run the mod again and click the "Mods" button on the Minecraft title screen. "Hello World" should appear in the list. Click it to read the mod information you just added.
Unfortunately, the Gradle build file in that tutorial doesn't work with newer versions of Forge and will produce an error. The "SNAPSHOT" is outdated and a newer version is required. Try the "build.gradle" file below.
The Meaning of Life, the Universe, and Everything.
Join Date:
6/11/2014
Posts:
59
Member Details
4: Coding Hints
If you're fairly new to coding or java, you'll see some things in modding examples that seem needlessly confusing. They are, so I'll try to explain a few of them now.
Package Names
All Java code is kept and organized in packages. There are few definitive rules regarding your own package names, but there is an established etiquette intended for ensuring different packages don't end up competing for the same name. Whether or not you follow this etiquette is really up to you, but here is how it works. Assuming you own your own internet domain, such as www.<yourdomain>.com, then your package name would be: com.<yourdomain>.<whatever> or com.<yourdomain>.<parent-package>.<whatever>. Widely accepted programming etiquette is known as convention. This particular convention works well for large software companies with permanent domains, but doesn't lend itself as easily to amateur modders. An alternate suggestion is to use an email account, such as gmail.<yourgmail>.<etc>. While this might prove a better fit, you might not want to share your gmail address with the entire world. Ultimately, it's worth acknowledging that the Minecraft modding community is a small one and that any reasonably unique name is unlikely to cause a conflict. Furthermore, such conflicts can be easily resolved as needed.
Open Source
Much of MinecraftForge is open source. That means you can peer into the source code to see exactly what it's doing and how. That's a really big deal!
In Eclipse, open the Forge folder visible in the "Package Explorer", then open "Referenced Libraries".
Whoa! That's a lot of jar files. Now open the "forgeSrc..." file at the top of the list.
The shear number of packages and associated class files is overwhelming, but don't be afraid to peek inside. These are ".class" files, not ".java" files. That means you can't edit them, on purpose or by accident. You can however, copy them! That might not seem important right now, but it will. Fortunately, you don't need to fumble thru each file aimlessly. When you click on any class, variable, or method name, simply right-click and select "Open Declaration" from the popup menu. Eclipse will then open the file you're looking for.
Javadoc
Occasionally you'll see a javadoc description written directly above a class, variable, or method. They look like this.
It's a special kind of comment that allows the javadoc program to generate a user-manual for your code. But perhaps more relevant is that Eclipse uses it to tell you what you need to know when you need to know it. In the example above, a javadoc comment is attached to a method. Now, as seen below, Eclipse will provide that information in a popup window anywhere that method name appears. Simply hover the mouse cursor over the method name to read the comment. Comments can even contain links to other comments, clickable in the popup window.
Javadoc isn't necessary, but the larger a program grows, the more helpful it becomes. Especially if you want others to understand your code. Eclipse uses the comments automatically and also includes a javadoc interface, found in the "Project" menu, for generating a user-manual. Otherwise, you can run javadoc directly from the Command Prompt in Windows. Here is an example.
C:\<path to project>\Hello World\java\javadoc -private -d ..\doc whysalives.minecraft.helloworld
If it doesn't run, then you'll need to add javadoc to the path in your Windows environment variables.
Add to Path
Below is an example, but your own javadoc directory may be different.
After running the command, you'll have a new directory in the parent directory by the name of "doc". The contents will look something like this.
Occasionally you'll see cascading "set" methods. They look like this.
You can only cascade methods that have been specifically designed for it. It is unnecessary and potentially confusing. The same thing can be accomplished with the same methods in a more reliable manner, shown below.
Anonymous Classes
Occasionally you'll see an anonymous class. They look like this.
They are a convenience that eliminates the need to include a whole separate class file with its own class name. They are also unnecessary and potentially confusing. The same thing can be accomplished in a more recognizable manner, shown below.
The Meaning of Life, the Universe, and Everything.
Join Date:
6/11/2014
Posts:
59
Member Details
5: Custom Blocks, Items, and Crafting
Create a new project and add the following files as shown. To add the custom sound and textures, simply drag-and-drop the files onto their corresponding package.
This mod creates a custom block with a custom texture, gives it a custom item to drop and a custom sound to play when the block is destroyed, then generates the block as a custom ore to be found in the world, and adds related custom crafting recipes. Also adds a custom inventory-tab in Creative Mode for custom blocks and items. Note: when in Creative Mode, it is normal that blocks don't drop items.
HelloBlock.java
package whysalives.minecraft.helloblock;
import net.minecraft.block.material.Material;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
//===========================================================================
// HelloBlock class
//===========================================================================
/**
* A Minecraft-Forge demonstration mod. Creates a custom block with a custom
* texture, gives it a custom item to drop and a custom sound to play when
* the block is destroyed, then generates the block as a custom ore to be
* found in the world, and adds related custom crafting recipes. Also adds a
* custom inventory-tab in Creative Mode for custom blocks and items.
*
* @version {@value HelloBlock#VERSION}
* @author Ryan F. Mercer -Open source.
*/
@Mod(modid = HelloBlock.MODID, version = HelloBlock.VERSION)
public class HelloBlock {
public static final String MODID = "helloblock"; //lowercase, no spaces.
public static final String VERSION = "vymdt.01.2014.08.18.0000";
private TestTab testTab;
private TestItem testItem;
private TestBlock testBlock;
private TestWorldGenerator testWorldGen;
//===========================================================================
/**
* Does nothing.
*
* @param event
*/
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
System.out.println("HelloBlock.preInit()"); //output to console.
//do nothing.
}
//===========================================================================
/**
* Main mod method.
*
* @param event
*/
@EventHandler
public void init(FMLInitializationEvent event) {
System.out.println("HelloBlock.init()"); //output to console.
//-------------------------------------------------------------------------
// Add custom tabs, items, blocks, and ores.
//create custom tab.
//inventory-tab in creative mode (pg 2).
//name used in file: en_US.lang.
testTab = new TestTab("customTab");
//create custom item.
testItem = new TestItem();
//name used in file: en_US.lang.
testItem.setUnlocalizedName("customItem");
//file: resources\assets\<MODID>\textures\items\testItem.png
testItem.setTextureName(MODID+":testItem");
testItem.setCreativeTab(testTab);
GameRegistry.registerItem(testItem, "testItem");
//set tab icon.
testTab.setTabIconItem(testItem);
//create custom block.
testBlock = new TestBlock(Material.ground);
//name used in file: en_US.lang.
testBlock.setBlockName("customBlock");
//file: resources\assets\<MODID>\textures\blocks\testBlock.png
testBlock.setBlockTextureName(MODID+":testBlock");
testBlock.setCreativeTab(testTab);
testBlock.setItemDropped(testItem);
GameRegistry.registerBlock(testBlock, "testBlock");
//generate custom ore.
testWorldGen = new TestWorldGenerator(testBlock
, 1000 //relative frequency ore deposit appears.
, 10 //maximum blocks per ore deposit.
);
GameRegistry.registerWorldGenerator(testWorldGen, 1);
//-------------------------------------------------------------------------
// Add custom crafting-recipes.
//any-arrangement-of-3 test items => gold ingot.
ItemStack testStack = new ItemStack(testItem);
GameRegistry.addShapelessRecipe
( new ItemStack(Items.gold_ingot, 1) //output and quantity.
, testStack, testStack, testStack //input.
//add more input.
);
//square-of-8 test items => test block.
GameRegistry.addRecipe
( new ItemStack(testBlock, 1) //output and quantity.
//grid pattern (3x3 - crafting table).
, "yyy" //item symbols.
, "y y" //empty center.
, "yyy"
//legend. //space => nothing.
, 'y', new ItemStack(testItem) //y => test item.
//, '?', new ItemStack(?) //add more (single-quotes for symbol).
);
//square-of-8 test items plus test block => diamond block.
GameRegistry.addRecipe
( new ItemStack(Blocks.diamond_block, 1) //output and quantity.
//grid pattern (3x3 - crafting table).
, "yyy" //item symbols.
, "yxy" //test block center.
, "yyy"
//legend. //space => nothing.
, 'y', new ItemStack(testItem) //y => test item.
, 'x', new ItemStack(testBlock) //x => test block.
//, '?', new ItemStack(?) //add more (single-quotes for symbol).
);
//test block => gold block.
GameRegistry.addSmelting
( new ItemStack(testBlock) //input.
, new ItemStack(Blocks.gold_block) //output.
, 1.0F //experience.
);
}
//===========================================================================
/**
* Does nothing.
*
* @param event
*/
@EventHandler
public void postInit(FMLPostInitializationEvent event) {
System.out.println("HelloBlock.postInit()"); //output to console.
//do nothing.
}
//===========================================================================
}//END class
//===========================================================================
TestBlock.java
package whysalives.minecraft.helloblock;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.item.Item;
import net.minecraft.world.World;
//===========================================================================
// TestBlock class
//===========================================================================
/**
* A custom block for use as a custom ore.
*
* @version vymdt.01.2014.08.18.0000
* @author Ryan F. Mercer -Open source.
*/
public class TestBlock extends Block {
/** Item for dropping. */
private Item drop;
//===========================================================================
/**
* Calls parent constructor and passes material.
*
* @param material Type of block.
*/
public TestBlock(Material material) {
super(material);
}
//===========================================================================
/**
* Returns quantity of items to drop when block destroyed.
*
* @param random [Unused] Random number generator.
* @return Quantity of items to drop.
*/
@Override
public int quantityDropped(Random random) {
//unused parameters: random.
return super.quantityDropped(random); //returns: 1.
}
//===========================================================================
/**
* Sets item to be dropped when block destroyed.
*
* @param item Dropped from block.
*/
public void setItemDropped(Item item) {
drop = item;
}
//===========================================================================
/**
* Returns item to be dropped when block destroyed.
*
* @param metadata [Unused] Extra nibble.
* @param random [Unused] Random number generator.
* @param fortune [Unused] Chance multiplier.
* @return Item to drop when block destroyed.
*/
@Override
public Item getItemDropped(int metadata, Random random, int fortune) {
return drop;
}
//===========================================================================
/**
* Called just before block destroyed by player. Plays sound specific to
* this block at block's location.
*
* @param world Current world.
* @param x X-axis coordinate of block.
* @param y Y-axis coordinate of block.
* @param z Z-axis coordinate of block.
* @param metadata [Unused] Extra nibble.
*/
@Override
public void onBlockDestroyedByPlayer(World world, int x, int y, int z
, int metadata) {
world.playSound
( x, y, z //block's coordinates.
//file: resources\assets\<MODID>\sounds\coin.ogg
, HelloBlock.MODID+":coin"
, 0.5F //half volume.
, 1.0F //normal pitch.
, true //?
);
}
//===========================================================================
}//END class
//===========================================================================
TestItem.java
package whysalives.minecraft.helloblock;
import net.minecraft.item.Item;
//===========================================================================
// TestItem class
//===========================================================================
/**
* A custom item for use as a drop from a destroyed block.
*
* @version vymdt.01.2014.08.18.0000
* @author Ryan F. Mercer -Open source.
*/
public class TestItem extends Item {
//no variables.
//===========================================================================
/**
* Calls parent constructor.
*/
public TestItem() {
super();
}
//===========================================================================
}//END class
//===========================================================================
TestTab.java
package whysalives.minecraft.helloblock;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
//===========================================================================
// TestTab class
//===========================================================================
/**
* A custom inventory-tab in creative mode.
*
* @version vymdt.01.2014.08.18.0000
* @author Ryan F. Mercer -Open source.
*/
public class TestTab extends CreativeTabs {
/** Item used for icon. */
private Item icon;
//===========================================================================
/**
* Calls parent constructor and passes label.
*
* @param label Name used in lang file.
*/
public TestTab(String label) {
super(label);
}
//===========================================================================
/**
* Sets item used for icon.
*
* @param item Used for icon.
*/
public void setTabIconItem(Item item) {
icon = item;
}
//===========================================================================
/**
* Returns item used for icon.
*
* @return Icon item.
*/
@Override
public Item getTabIconItem() {
return icon;
}
//===========================================================================
}//END class
//===========================================================================
TestWorldGenerator.java
package whysalives.minecraft.helloblock;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.gen.feature.WorldGenMinable;
import cpw.mods.fml.common.IWorldGenerator;
//===========================================================================
// TestWorldGenerator class
//===========================================================================
/**
* Generates a custom ore in the given world.
*
* @version vymdt.01.2014.08.18.0000
* @author Ryan F. Mercer -Open source.
*/
public class TestWorldGenerator implements IWorldGenerator {
/** Block used for generating ore. */
public final Block testOre;
/** Relative frequency of ore deposits. */
public final int frequency;
/** Maximum blocks per ore deposit. */
public final int abundance;
//===========================================================================
/**
* Sets given parameters.
*
* @param ore Block to use for generating ore.
* @param frequency Relative frequency ore deposit appears.
* @param abundance Maximum blocks per ore deposit.
*/
public TestWorldGenerator(Block ore, int frequency, int abundance) {
testOre = ore;
this.frequency = frequency;
this.abundance = abundance;
}
//===========================================================================
/**
* Generates ore in proper locations of given world.
*
* @param random Random number generator.
* @param chunkX ?
* @param chunkZ ?
* @param world Current world.
* @param chunkGenerator [Unused] ?
* @param chunkProvider [Unused] ?
*/
public void generate(Random random, int chunkX, int chunkZ, World world
, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) {
switch(world.provider.dimensionId) {
case -1:
generateInNether(world, random, chunkX * 16, chunkZ * 16);
break;
case 0:
generateInOverworld(world, random, chunkX * 16, chunkZ * 16);
break;
case 1:
generateInEnd(world, random, chunkX * 16, chunkZ * 16);
break;
}
}
//===========================================================================
/**
* Does nothing. Called by {@link #generate(Random, int, int, World
* , IChunkProvider, IChunkProvider)}.
*
* @param world [Unused] Current world.
* @param random [Unused] Random number generator.
* @param chunkX [Unused] ?
* @param chunkZ [Unused] ?
*/
private void generateInNether(World world, Random random, int chunkX
, int chunkZ) {
//do nothing.
}
//===========================================================================
/**
* Called by {@link #generate(Random, int, int, World, IChunkProvider
* , IChunkProvider)}.
*
* @param world Current world.
* @param random Random number generator.
* @param chunkX ?
* @param chunkZ ?
*/
private void generateInOverworld(World world, Random random, int chunkX
, int chunkZ) {
for(int i=0; i < frequency; i++) {
int x = chunkX + random.nextInt(16);
int y = random.nextInt(64); //max height.
int z = chunkZ + random.nextInt(16);
WorldGenMinable worldGen = new WorldGenMinable(testOre, abundance);
worldGen.generate(world, random, x, y, z);
}
}
//===========================================================================
/**
* Does nothing. Called by {@link #generate(Random, int, int, World
* , IChunkProvider, IChunkProvider)}.
*
* @param world [Unused] Current world.
* @param random [Unused] Random number generator.
* @param chunkX [Unused] ?
* @param chunkZ [Unused] ?
*/
private void generateInEnd(World world, Random random, int chunkX
, int chunkZ) {
//do nothing.
}
//===========================================================================
}//END class
//===========================================================================
sounds.json
/**
* @version vymdt.01.2014.08.18.0000
* @author Ryan F. Mercer -Open source.
*
* Custom sounds for project: Hello Block.
*/
{ "coin":
{ "category": "master"
, "sounds":
[ { "name": "coin" //path from "../sounds/" minus file-extension.
, "stream": false //set true when sound longer than a few seconds.
}
]
}
}
en_US.lang
/**
* @version vymdt.01.2014.08.18.0000
* @author Ryan F. Mercer -Open source.
*
* Custom text for project: Hello Block.
*/
tile.customBlock.name=Test Block
item.customItem.name=Test Item
itemGroup.customTab=Test Tab
mcmod.info
/**
* @version vymdt.01.2014.08.18.0000
* @author Ryan F. Mercer -Open source.
*
* In-game info for project: Hello Block.
*/
[ { "modid" : "helloblock"
, "name" : "Hello Block"
, "description" : "A Minecraft-Forge demonstration mod. Creates a custom block with a custom texture, gives it a custom item to drop and a custom sound to play when the block is destroyed, then generates the block as a custom ore to be found in the world, and adds related custom crafting recipes. Also adds a custom inventory-tab in Creative Mode for custom blocks and items."
, "version" : "vymdt.01.2014.08.18.0000"
, "mcversion" : "1.7.10"
, "url" : ""
, "updateUrl" : ""
, "authorList" : ["Ryan F. Mercer"]
, "credits" : ""
, "logoFile" : ""
, "screenshots" : []
, "dependencies" : ["Forge10.13.0.1180"]
}
]
Right-click and "Save link as" coin.ogg -- Custom drop sound. testBlock.png -- Custom block texture testItem.png -- Custom item texture.
You might have noticed that each face of the custom block looks the same. That's fine for ore, but not for every kind of block. To give the custom block different faces, add the following code.
Insert Code
Put this near the top of the "TestBlock" class, under the line that reads "private Item drop".
/** Icons for block faces. */
@SideOnly(Side.CLIENT)
private IIcon[] icon = new IIcon[6];
Put this near the bottom of the "TestBlock" class, above the last three lines that read "END class".
//===========================================================================
/**
* Registers an icon for each block face.
*
* @param reg Registers the icons.
*/
@SideOnly(Side.CLIENT)
@Override
public void registerBlockIcons(IIconRegister reg) {
//bottom, top, north, south, west, east.
String face[] = {"B","T","N","S","W","E"};
for(int i=0; i < face.length ;i++) {
icon[i] = reg.registerIcon(getTextureName()+"_"+face[i]);
}
}
//===========================================================================
/**
* Returns the block icon for the given face.
*
* @param face The block face to be returned.
* @param metadata [Unused] Extra nibble.
* @return Icon for specified block face.
*/
@SideOnly(Side.CLIENT)
@Override
public IIcon getIcon(int face, int metadata) {
return icon[face];
}
Now, instead of a single texture file, we need six texture files, one for each face. They keep the same name but each is suffixed with an underscore followed by a letter: B-ottom, T-op, N-orth, S-outh, W-est, E-ast.
Why place just one block when you can place an entire structure? Add the following code to your custom block.
Insert Code
Put this near the bottom of the "TestBlock" class, above the last three lines that read "END class".
//===========================================================================
/**
* Replaces placed block with spiral quartz staircase with glowstone column.
*
* @param world Current world.
* @param x X-axis coordinate of block.
* @param y Y-axis coordinate of block.
* @param z Z-axis coordinate of block.
*/
public void onBlockAdded(World world, int x, int y, int z) {
super.onBlockAdded(world, x, y, z);
world.setBlockToAir(x, y, z); //removes initially placed block.
//spiral quartz staircase with glowstone column.
//metadata: minecraft.gamepedia.com/Data_values
world.setBlock(x, y, z, Blocks.quartz_stairs, 0x3/*north*/, 2);
world.setBlock(x, y, z-1, Blocks.quartz_block);
world.setBlock(x-1, y, z-1, Blocks.quartz_block);
world.setBlock(x-1, y, z, Blocks.glowstone);
//----
world.setBlock(x-1, y+1, z-1, Blocks.quartz_stairs, 0x1/*west*/, 2);
world.setBlock(x-2, y+1, z-1, Blocks.quartz_block);
world.setBlock(x-2, y+1, z, Blocks.quartz_block);
world.setBlock(x-1, y+1, z, Blocks.glowstone);
//----
world.setBlock(x-2, y+2, z, Blocks.quartz_stairs, 0x2/*south*/, 2);
world.setBlock(x-2, y+2, z+1, Blocks.quartz_block);
world.setBlock(x-1, y+2, z+1, Blocks.quartz_block);
world.setBlock(x-1, y+2, z, Blocks.glowstone);
//----
world.setBlock(x-1, y+3, z+1, Blocks.quartz_stairs, 0x0/*east*/, 2);
world.setBlock(x, y+3, z+1, Blocks.quartz_block);
world.setBlock(x, y+3, z, Blocks.quartz_block);
world.setBlock(x-1, y+3, z, Blocks.glowstone);
}
Now when the block is placed, you get a spiral staircase instead. The first set of steps point north and placement of the initial block is outlined in red, below.
We can then climb to the top of the spiral to stack another one on top. The correct placement is marked in red, below.
Repeat to quickly and easily build a glowing spiral staircase of any height.
The Meaning of Life, the Universe, and Everything.
Join Date:
6/11/2014
Posts:
59
Member Details
6: Custom Projectiles and Persistent Block Damage
This mod creates a throwable tool that can harvest when thrown, relative to player, tool, and block. Temporary recipes have been added for testing in Survival Mode.
HelloProjectile.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Item.ToolMaterial;
import whysalives.minecraft.helloprojectile.CustomTab;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.network.NetworkRegistry;
//===========================================================================
// HelloProjectile class
//===========================================================================
/**
* A Minecraft-Forge demonstration mod. Creates a throwable tool that can
* harvest when thrown, relative to player, tool, and block.
*
* @version {@value HelloProjectile#VERSION}
* @author Ryan F. Mercer -Open source.
*/
@Mod(modid = HelloProjectile.MODID, version = HelloProjectile.VERSION)
public class HelloProjectile {
public static final String MODID = "helloprojectile"; //one word lowercase.
public static final String VERSION = "vymdt.01.2014.09.21.0000";
@SidedProxy
( clientSide = "whysalives.minecraft.helloprojectile.ClientProxy"
, serverSide = "whysalives.minecraft.helloprojectile.CommonProxy" )
public static CommonProxy proxy;
/** Custom entity ids. */
private int entityIndex = 0;
//throwing axes.
/** Available for client proxy renderer. */
protected static ThrowingAxe throwingAxeWood;
/** Available for client proxy renderer. */
protected static ThrowingAxe throwingAxeStone;
/** Available for client proxy renderer. */
protected static ThrowingAxe throwingAxeIron;
/** Available for client proxy renderer. */
protected static ThrowingAxe throwingAxeDiamond;
/** Available for client proxy renderer. */
protected static ThrowingAxe throwingAxeGold;
/** Available for client proxy renderer. */
//===========================================================================
/**
* Registers items and entities. Adds items to custom creative tab.
*
* @param event [Unused] Caught.
*/
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
System.out.println( "HelloProjectile.preInit()" );
//create custom tab with penny icon.
CustomTab customTab = new CustomTab("customTab"); //lang.
Item penny = new Item();
penny.setUnlocalizedName("penny"); //lang.
//file: resources\assets\<MODID>\textures\items\penny.png
penny.setTextureName(MODID+":penny");
GameRegistry.registerItem(penny, "penny");
customTab.setTabIconItem(penny);
//create wooden throwing-axe.
throwingAxeWood = new ThrowingAxe(ToolMaterial.WOOD);
throwingAxeWood.setUnlocalizedName("throwingAxeWood"); //lang.
throwingAxeWood.setTextureName("wood_axe");
throwingAxeWood.setCreativeTab(customTab);
GameRegistry.registerItem(throwingAxeWood, "throwingAxeWood");
EntityRegistry.registerModEntity(EntityThrowingAxeWood.class
, "Wood Throwing-Axe", entityIndex++, this, 64, 10, true);
//create stone throwing-axe.
throwingAxeStone = new ThrowingAxe(ToolMaterial.STONE);
throwingAxeStone.setUnlocalizedName("throwingAxeStone"); //lang.
throwingAxeStone.setTextureName("stone_axe");
throwingAxeStone.setCreativeTab(customTab);
GameRegistry.registerItem(throwingAxeStone, "throwingAxeStone");
EntityRegistry.registerModEntity(EntityThrowingAxeStone.class
, "Stone Throwing-Axe", entityIndex++, this, 64, 10, true);
//create iron throwing-axe.
throwingAxeIron = new ThrowingAxe(ToolMaterial.IRON);
throwingAxeIron.setUnlocalizedName("throwingAxeIron"); //lang.
throwingAxeIron.setTextureName("iron_axe");
throwingAxeIron.setCreativeTab(customTab);
GameRegistry.registerItem(throwingAxeIron, "throwingAxeIron");
EntityRegistry.registerModEntity(EntityThrowingAxeIron.class
, "Iron Throwing-Axe", entityIndex++, this, 64, 10, true);
//create diamond throwing-axe.
throwingAxeDiamond = new ThrowingAxe(ToolMaterial.EMERALD);
throwingAxeDiamond.setUnlocalizedName("throwingAxeDiamond"); //lang.
throwingAxeDiamond.setTextureName("diamond_axe");
throwingAxeDiamond.setCreativeTab(customTab);
GameRegistry.registerItem(throwingAxeDiamond, "throwingAxeDiamond");
EntityRegistry.registerModEntity(EntityThrowingAxeDiamond.class
, "Diamond Throwing-Axe", entityIndex++, this, 64, 10, true);
//create gold throwing-axe.
throwingAxeGold = new ThrowingAxe(ToolMaterial.GOLD);
throwingAxeGold.setUnlocalizedName("throwingAxeGold"); //lang.
throwingAxeGold.setTextureName("gold_axe");
throwingAxeGold.setCreativeTab(customTab);
GameRegistry.registerItem(throwingAxeGold, "throwingAxeGold");
EntityRegistry.registerModEntity(EntityThrowingAxeGold.class
, "Gold Throwing-Axe", entityIndex++, this, 64, 10, true);
/*
You know you want to..!
*/
//create diamond throwing-pickaxe.
//throwingPickaxeDiamond = new ThrowingPickaxe(ToolMaterial.EMERALD);
//throwingPickaxeDiamond.setUnlocalizedName("throwingPickaxeDiamond"); //lang.
//throwingPickaxeDiamond.setTextureName("diamond_pickaxe");
//throwingPickaxeDiamond.setCreativeTab(customTab);
//GameRegistry.registerItem(throwingPickaxeDiamond, "throwingPickaxeDiamond");
//EntityRegistry.registerModEntity(EntityThrowingPickaxeDiamond.class
// , "Diamond Throwing-Pickaxe", entityIndex++, this, 64, 10, true);
}
//===========================================================================
/**
* Registers common proxy, renderers, and tick handler.
*
* @param event [Unused] Caught.
*/
@EventHandler
public void init(FMLInitializationEvent event) {
System.out.println( "HelloProjectile.init()" );
//common proxy.
NetworkRegistry.INSTANCE.registerGuiHandler(this, new CommonProxy());
proxy.registerRenderers();
//tick events.
FMLCommonHandler.instance().bus().register(new CustomEventHandler());
//temporary recipes for testing in survival mode.
ItemStack dirtStack = new ItemStack(Blocks.dirt);
GameRegistry.addShapelessRecipe( new ItemStack(throwingAxeWood, 64)
, dirtStack, dirtStack);
GameRegistry.addShapelessRecipe( new ItemStack(throwingAxeStone, 64)
, dirtStack, dirtStack, dirtStack);
GameRegistry.addShapelessRecipe( new ItemStack(throwingAxeIron, 64)
, dirtStack, dirtStack, dirtStack, dirtStack);
GameRegistry.addShapelessRecipe( new ItemStack(throwingAxeDiamond, 64)
, dirtStack, dirtStack, dirtStack, dirtStack, dirtStack);
GameRegistry.addShapelessRecipe( new ItemStack(throwingAxeGold, 64)
, dirtStack, dirtStack, dirtStack, dirtStack, dirtStack, dirtStack);
}
//===========================================================================
/**
* Does nothing.
*
* @param event [Unused] Caught.
*/
@EventHandler
public void postInit(FMLPostInitializationEvent event) {
System.out.println( "HelloProjectile.postInit()" );
//do nothing.
}
//===========================================================================
}//END class
//===========================================================================
ClientProxy.java
package whysalives.minecraft.helloprojectile;
import cpw.mods.fml.client.registry.RenderingRegistry;
//===========================================================================
// ClientProxy class
//===========================================================================
/**
* Provides client-side separation.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class ClientProxy extends CommonProxy {
//no fields.
//===========================================================================
/**
* Registers client-side renderers.
*/
@Override
public void registerRenderers() {
//throwing axes.
RenderingRegistry.registerEntityRenderingHandler
(EntityThrowingAxeWood.class, new RenderThrowingAxe
(HelloProjectile.throwingAxeWood));
RenderingRegistry.registerEntityRenderingHandler
(EntityThrowingAxeStone.class, new RenderThrowingAxe
(HelloProjectile.throwingAxeStone));
RenderingRegistry.registerEntityRenderingHandler
(EntityThrowingAxeIron.class, new RenderThrowingAxe
(HelloProjectile.throwingAxeIron));
RenderingRegistry.registerEntityRenderingHandler
(EntityThrowingAxeDiamond.class, new RenderThrowingAxe
(HelloProjectile.throwingAxeDiamond));
RenderingRegistry.registerEntityRenderingHandler
(EntityThrowingAxeGold.class, new RenderThrowingAxe
(HelloProjectile.throwingAxeGold));
}
//===========================================================================
}//END class
//===========================================================================
CommonProxy.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World;
import cpw.mods.fml.common.network.IGuiHandler;
//===========================================================================
// CommonProxy class
//===========================================================================
/**
* Provides client-server separation.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class CommonProxy implements IGuiHandler {
//no fields.
//===========================================================================
/**
* Does nothing. Server can not render, exists for client override.
*/
public void registerRenderers() {
//do nothing.
}
//===========================================================================
/**
* Returns requested server-side Container.
*
* @param guiId [Unused] GUI identifier.
* @param player [Unused] GUI user.
* @param world [Unused] Current world.
* @param x [Unused] X-axis coordinate of GUI.
* @param y [Unused] Y-axis coordinate of GUI.
* @param z [Unused] Z-axis coordinate of GUI.
* @return Container or null when not found.
*/
@Override
public Object getServerGuiElement(int guiId, EntityPlayer player
, World world, int x, int y, int z) {
return null; //none.
}
//===========================================================================
/**
* Returns requested client-side GuiScreen or server-side Container.
*
* @param guiId [Unused] GUI identifier.
* @param player [Unused] GUI user.
* @param world [Unused] Current world.
* @param x [Unused] X-axis coordinate of GUI.
* @param y [Unused] Y-axis coordinate of GUI.
* @param z [Unused] Z-axis coordinate of GUI.
* @return GuiScreen/Container or null when not found.
*/
@Override
public Object getClientGuiElement(int guiId, EntityPlayer player
, World world, int x, int y, int z) {
return null; //none.
}
//===========================================================================
}//END class
//===========================================================================
CustomEventHandler.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.potion.Potion;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.event.ForgeEventFactory;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
//===========================================================================
// CustomEventHandler class
//===========================================================================
/**
* Manages persistent block damage for up to {@value #MAX_BLOCKS} blocks.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class CustomEventHandler {
/** Limit of blocks with block damage. */
private static final int MAX_BLOCKS = 49; //equal to 7x7 square.
/** Ticks between updates of block damage. */
private static final int TICK_DELAY = 200;
/** Counter for tick delay between block damage updates. */
private static int tick = 0;
/** Last index position of newly damaged block. */
private static int marker = -1;
/** X-axis coordinates of block damage. */
private static int[] xPos = new int[MAX_BLOCKS];
/** Y-axis coordinates of block damage. */
private static int[] yPos = new int[MAX_BLOCKS];
/** Z-axis coordinates of block damage. */
private static int[] zPos = new int[MAX_BLOCKS];
/** Block damage totals. */
private static float[] dmg = new float[MAX_BLOCKS];
//===========================================================================
/**
* Increments persistent damage to block at given coordinates.
*
* @param world Current world.
* @param x X-axis coordinate of block.
* @param y Y-axis coordinate of block.
* @param z Z-axis coordinate of block.
* @param player Current player.
* @param stack Item hitting block.
* @param factor Damage multiplier.
*/
public static void damageBlock(World world, int x, int y, int z
, EntityPlayer player, ItemStack stack, float factor) {
//server.
if(!world.isRemote) {
int index = -1;
//previously damaged block.
for(int i=0; i < xPos.length; i++) {
if(x == xPos[i] && y == yPos[i] && z == zPos[i]) {
index = i;
break;
}
}
//newly damaged block.
if(index < 0) {
marker = ++marker < MAX_BLOCKS ? marker : 0;
index = marker;
xPos[index] = x;
yPos[index] = y;
zPos[index] = z;
}
Block block = world.getBlock(x, y, z);
int blockId = Block.getIdFromBlock(block);
int metadata = world.getBlockMetadata(x, y, z);
if(block.getMaterial() != Material.air) {
//apply damage relative to block hardness, tool type, and player.
dmg[index] += factor * blockStrength
(world, player, block, metadata, x, y, z, stack);
//crack block.
if(dmg[index] < 1.0F) {
world.destroyBlockInWorldPartially
(blockId, x, y, z, (int)(dmg[index] * 10.0F) - 1);
world.playSoundEffect
( (float)x + 0.5F, (float)y + 0.5F, (float)z + 0.5F //center.
, block.stepSound.getStepResourcePath() //step-sound name.
, block.stepSound.getVolume() * 1.5F //150% step volume.
, block.stepSound.getPitch() * 0.5F //half step pitch.
);
}
//break block.
else {
if(world.setBlockToAir(x, y, z)) {
dmg[index] = 0.0F; //reset.
//break-sound plus particles.
world.playAuxSFX(2001, x, y, z, blockId+(metadata << 12));
//drop items.
if(!player.capabilities.isCreativeMode
&& canHarvestBlock(world, player, block, metadata, stack)) {
block.dropBlockAsItem(world, x, y, z, metadata, 0/*fortune*/);
}
}
}
}
}
}
//===========================================================================
/**
* Adapted from {@link net.minecraftforge.common.ForgeHooks#blockStrength
* (Block, EntityPlayer, World, int, int, int)}. Uses given item rather than
* player held item.
*
* @param world Current world.
* @param player Current player.
* @param block Being hit.
* @param metadata Extra nibble.
* @param x X-axis coordinate of block.
* @param y Y-axis coordinate of block.
* @param z Z-axis coordinate of block.
* @param stack Item hitting block.
* @return Relative block hardness.
*/
public static float blockStrength(World world, EntityPlayer player
, Block block, int metadata, int x, int y, int z, ItemStack stack) {
float hardness = block.getBlockHardness(world, x, y, z);
if(hardness < 0.0F) {
return 0.0F;
}
if(!canHarvestBlock(world, player, block, metadata, stack)) {
return getBreakSpeed(player, block, metadata, x, y, z, stack)
/ hardness / 100F;
}
else {
return getBreakSpeed(player, block, metadata, x, y, z, stack)
/ hardness / 30F;
}
}
//===========================================================================
/**
* Adapted from {@link net.minecraftforge.common.ForgeHooks#canHarvestBlock
* (Block, EntityPlayer, int)}. Uses given item rather than player held
* item.
*
* @param world Current world.
* @param player Current player.
* @param block To be harvested.
* @param metadata Extra nibble.
* @param stack Item harvesting block.
* @return If the block can be harvested.
*/
public static boolean canHarvestBlock(World world, EntityPlayer player
, Block block, int metadata, ItemStack stack) {
if(block.getMaterial().isToolNotRequired()) {
return true;
}
String tool = block.getHarvestTool(metadata);
if(stack == null || tool == null) {
return player.canHarvestBlock(block);
}
int toolLevel = stack.getItem().getHarvestLevel(stack, tool);
if(toolLevel < 0) {
return player.canHarvestBlock(block);
}
return toolLevel >= block.getHarvestLevel(metadata);
}
//===========================================================================
/**
* Adapted from {@link net.minecraft.entity.player.EntityPlayer#getBreakSpeed
* (Block, boolean, int, int, int, int)}. Uses given item rather than player
* held item.
*
* @param player Current player.
* @param block To be broken.
* @param metadata Extra nibble.
* @param x X-axis coordinate of block.
* @param y Y-axis coordinate of block.
* @param z Z-axis coordinate of block.
* @param stack Item breaking block.
* @return Relative break speed.
*/
public static float getBreakSpeed(EntityPlayer player, Block block
, int metadata, int x, int y, int z, ItemStack stack) {
float f = (stack == null ? 1.0F
: stack.getItem().getDigSpeed(stack, block, metadata));
if(f > 1.0F) {
int mod = EnchantmentHelper.getEnchantmentLevel
(Enchantment.efficiency.effectId, stack);
if(mod > 0 && stack != null) {
float f1 = (float)(mod * mod + 1);
if(!ForgeHooks.canToolHarvestBlock(block, metadata, stack)
&& f <= 1.0F) {
f += f1 * 0.08F;
}
else {
f += f1;
}
}
}
if(player.isPotionActive(Potion.digSpeed)) {
f *= 1.0F + (float)(player.getActivePotionEffect(Potion.digSpeed)
.getAmplifier() + 1) * 0.2F;
}
if(player.isPotionActive(Potion.digSlowdown)) {
f *= 1.0F - (float)(player.getActivePotionEffect(Potion.digSlowdown)
.getAmplifier() + 1) * 0.2F;
}
if(player.isInsideOfMaterial(Material.water)
&& !EnchantmentHelper.getAquaAffinityModifier(player)) {
f /= 5.0F;
}
if(!player.onGround) {
f /= 5.0F;
}
f = ForgeEventFactory.getBreakSpeed(player, block, metadata, f, x, y, z);
return (f < 0 ? 0 : f);
}
//===========================================================================
/**
* Does nothing.
*
* @param event [Unused] Caught.
*/
@SubscribeEvent
public void onClientTick(TickEvent.ClientTickEvent event) {
//do nothing.
}
//===========================================================================
/**
* Does nothing.
*
* @param event [Unused] Caught.
*/
@SubscribeEvent
public void onPlayerTick(TickEvent.PlayerTickEvent event) {
//do nothing.
}
//===========================================================================
/**
* Does nothing.
*
* @param event [Unused] Caught.
*/
@SubscribeEvent
public void onRenderTick(TickEvent.RenderTickEvent event) {
//do nothing.
}
//===========================================================================
/**
* Does nothing.
*
* @param event [Unused] Caught.
*/
@SubscribeEvent
public void onServerTick(TickEvent.ServerTickEvent event) {
//do nothing.
}
//===========================================================================
/**
* Updates partial damage on blocks with persistent damage.
*
* @param event Caught.
*/
@SubscribeEvent
public void onWorldTick(TickEvent.WorldTickEvent event) {
if(marker > -1) {
if(tick < TICK_DELAY) {
++tick;
}
else {
tick = 0; //reset.
for(int i=0; i < dmg.length; i++) {
if(dmg[i] < 1.0F) {
//supply negative entity IDs to simulate additional players.
event.world.destroyBlockInWorldPartially
(-(i+1), xPos[i], yPos[i], zPos[i], (int)(dmg[i] * 10.0F) - 1);
}
}
}
}
}
//===========================================================================
}//END class
//===========================================================================
CustomTab.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
//===========================================================================
// CustomTab class
//===========================================================================
/**
* A custom inventory-tab in creative mode.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class CustomTab extends CreativeTabs {
/** Item used for icon. */
private Item icon;
//===========================================================================
/**
* Calls parent constructor and passes label.
*
* @param label Name used in lang file.
*/
public CustomTab(String label) {
super(label);
}
//===========================================================================
/**
* Sets item used for icon.
*
* @param item Used for icon.
*/
public void setTabIconItem(Item item) {
icon = item;
}
//===========================================================================
/**
* Returns item used for icon.
*
* @return Icon item.
*/
@Override
public Item getTabIconItem() {
return icon;
}
//===========================================================================
}//END class
//===========================================================================
EntityThrowingAxe.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntityThrowable;
import net.minecraft.item.ItemStack;
import net.minecraft.util.DamageSource;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.MovingObjectPosition.MovingObjectType;
import net.minecraft.world.World;
//===========================================================================
// EntityThrowingAxe class
//===========================================================================
/**
* A throwing-axe projectile.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class EntityThrowingAxe extends EntityThrowable {
/** Item entity represents. */
protected ItemStack item;
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
*/
public EntityThrowingAxe(World world) {
super(world);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
*/
public EntityThrowingAxe(World world, EntityLivingBase entityLivingBase) {
super(world, entityLivingBase);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param x X-axis coordinate of entity.
* @param y Y-axis coordinate of entity.
* @param z Z-axis coordinate of entity.
*/
public EntityThrowingAxe(World world, double x, double y, double z) {
super(world, x, y, z);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
* @param stack Item entity represents.
*/
public EntityThrowingAxe(World world, EntityLivingBase entityLivingBase
, ItemStack stack) {
super(world, entityLivingBase);
item = stack;
}
//===========================================================================
/**
* Called when hits block or entity.
*
* @param mop Impact data.
*/
@Override
protected void onImpact(MovingObjectPosition mop) {
//server.
if(!worldObj.isRemote) {
//entity hit.
if(mop.typeOfHit == MovingObjectType.ENTITY) {
mop.entityHit.attackEntityFrom
( DamageSource.causeThrownDamage(this, getThrower())
, ThrowingAxe.throwingDamageVsEntity //damage amount.
);
}
//block hit.
else if(mop.typeOfHit == MovingObjectType.BLOCK) {
CustomEventHandler.damageBlock
( worldObj, mop.blockX, mop.blockY, mop.blockZ
/*damage multiplier adjusts for delay between throws to simulate
normal continuous break rate.*/
, (EntityPlayer)getThrower(), item, 3.0F //damage multiplier.
);
}
}
//remove entity.
setDead();
}
//===========================================================================
}//END class
//===========================================================================
EntityThrowingAxeDiamond.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
//===========================================================================
// EntityThrowingAxeDiamond class
//===========================================================================
/**
* A throwing-axe projectile made of diamond.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class EntityThrowingAxeDiamond extends EntityThrowingAxe {
//no fields.
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
*/
public EntityThrowingAxeDiamond(World world) {
super(world);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
*/
public EntityThrowingAxeDiamond(World world
, EntityLivingBase entityLivingBase) {
super(world, entityLivingBase);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param x X-axis coordinate of entity.
* @param y Y-axis coordinate of entity.
* @param z Z-axis coordinate of entity.
*/
public EntityThrowingAxeDiamond(World world, double x, double y, double z) {
super(world, x, y, z);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
* @param stack Item entity represents.
*/
public EntityThrowingAxeDiamond(World world
, EntityLivingBase entityLivingBase, ItemStack stack) {
super(world, entityLivingBase, stack);
}
//===========================================================================
}//END class
//===========================================================================
EntityThrowingAxeGold.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
//===========================================================================
// EntityThrowingAxeGold class
//===========================================================================
/**
* A throwing-axe projectile made of gold.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class EntityThrowingAxeGold extends EntityThrowingAxe {
//no fields.
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
*/
public EntityThrowingAxeGold(World world) {
super(world);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
*/
public EntityThrowingAxeGold(World world
, EntityLivingBase entityLivingBase) {
super(world, entityLivingBase);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param x X-axis coordinate of entity.
* @param y Y-axis coordinate of entity.
* @param z Z-axis coordinate of entity.
*/
public EntityThrowingAxeGold(World world, double x, double y, double z) {
super(world, x, y, z);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
* @param stack Item entity represents.
*/
public EntityThrowingAxeGold(World world, EntityLivingBase entityLivingBase
, ItemStack stack) {
super(world, entityLivingBase, stack);
}
//===========================================================================
}//END class
//===========================================================================
EntityThrowingAxeIron.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
//===========================================================================
// EntityThrowingAxeIron class
//===========================================================================
/**
* A throwing-axe projectile made of iron.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class EntityThrowingAxeIron extends EntityThrowingAxe {
//no fields.
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
*/
public EntityThrowingAxeIron(World world) {
super(world);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
*/
public EntityThrowingAxeIron(World world
, EntityLivingBase entityLivingBase) {
super(world, entityLivingBase);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param x X-axis coordinate of entity.
* @param y Y-axis coordinate of entity.
* @param z Z-axis coordinate of entity.
*/
public EntityThrowingAxeIron(World world, double x, double y, double z) {
super(world, x, y, z);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
* @param stack Item entity represents.
*/
public EntityThrowingAxeIron(World world, EntityLivingBase entityLivingBase
, ItemStack stack) {
super(world, entityLivingBase, stack);
}
//===========================================================================
}//END class
//===========================================================================
EntityThrowingAxeStone.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
//===========================================================================
// EntityThrowingAxeStone class
//===========================================================================
/**
* A throwing-axe projectile made of stone.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class EntityThrowingAxeStone extends EntityThrowingAxe {
//no fields.
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
*/
public EntityThrowingAxeStone(World world) {
super(world);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
*/
public EntityThrowingAxeStone(World world
, EntityLivingBase entityLivingBase) {
super(world, entityLivingBase);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param x X-axis coordinate of entity.
* @param y Y-axis coordinate of entity.
* @param z Z-axis coordinate of entity.
*/
public EntityThrowingAxeStone(World world, double x, double y, double z) {
super(world, x, y, z);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
* @param stack Item entity represents.
*/
public EntityThrowingAxeStone(World world, EntityLivingBase entityLivingBase
, ItemStack stack) {
super(world, entityLivingBase, stack);
}
//===========================================================================
}//END class
//===========================================================================
EntityThrowingAxeWood.java
package whysalives.minecraft.helloprojectile;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
//===========================================================================
// EntityThrowingAxeWood class
//===========================================================================
/**
* A throwing-axe projectile made of wood.
*
* @version vymdt.01.2014.09.21.0000
* @author Ryan F. Mercer -Open source.
*/
public class EntityThrowingAxeWood extends EntityThrowingAxe {
//no fields.
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
*/
public EntityThrowingAxeWood(World world) {
super(world);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
*/
public EntityThrowingAxeWood(World world
, EntityLivingBase entityLivingBase) {
super(world, entityLivingBase);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param x X-axis coordinate of entity.
* @param y Y-axis coordinate of entity.
* @param z Z-axis coordinate of entity.
*/
public EntityThrowingAxeWood(World world, double x, double y, double z) {
super(world, x, y, z);
}
//===========================================================================
/**
* Calls parent constructor.
*
* @param world Current world.
* @param entityLivingBase Thrower of entity.
* @param stack Item entity represents.
*/
public EntityThrowingAxeWood(World world, EntityLivingBase entityLivingBase
, ItemStack stack) {
super(world, entityLivingBase, stack);
}
//===========================================================================
}//END class
//===========================================================================
The Meaning of Life, the Universe, and Everything.
Join Date:
6/11/2014
Posts:
59
Member Details
Troubleshooting
When submitting errors, please post your entireConsole output and state the versions of your Minecraft, Forge, Eclipse, and Java installations.
If all else fails and you believe you're genuinely having trouble with your installation of Forge, run the following command in Command Prompt from inside your forge directory.
Newer versions of Forge aren't supposed to require any program arguments, but if you believe you're specifically having trouble with your assets, you can try these.
The Meaning of Life, the Universe, and Everything.
Join Date:
6/11/2014
Posts:
59
Member Details
A Word from the Author
I was not paid to produce this tutorial, nor am I directly or indirectly associated with any related company, organization, or marketing apparatus. I give it freely for the sole reason, and the sole hope, that it will facilitate a more active and inclusive modding community. I have attempted to answer, as completely as possible, each question I myself encountered while learning to mod Minecraft for the first time. Like you, I'm still learning, and I encourage you to share any questions or insights that you may have.
Thank you for reading. I hope you have found it useful. If so, please comment. It keeps the thread bumped so others may find it more easily.
Really great tutorial especially in conjunction with MrrGingerNinja's tutorial on getting everything set up. The only issue I had was that I'm used to my IDE's saving my files when I click run, in eclipse you have to be sure to save everything yourself.
Very nice guide for people looking to get into Forge mod making. Just a couple of finer points...
Forge uses the "GradleStart" and "GradleServerStart" classes now to launch the Minecraft instance. This is to handle the MC assets and keep Lex & co from having to constantly yell at new modders.
In the "Hello Block" demo mod, you don't really explain how to add the assets. I didn't have an issue (resources/assets/helloblock/etc etc) however you may want to elaborate on that a little since it's a "guide for dummies".
ADDED AFTER Also, just a heads up and not sure when it changed, but it appears texture faces are automagically reversed on opposing sides (so reversing sides 2 and 5 makes the texture reversed in game, leaving them normal allows them to appear how they should).
Other than that great work so far; I've been sending those that ask me questions here.
Starting up SoundSystem...
Initializing LWJGL OpenAL
(The LWJGL binding of OpenAL. For more information, see http://www.lwjgl.org)
OpenAL initialized.
[13:50:50] [MCO Availability Checker #1/ERROR]: Couldn't connect to Realms
[13:50:50] [Sound Library Loader/INFO]: Sound engine started
[13:50:55] [Client thread/WARN]: Unable to play unknown soundEvent: minecraft:music.menu
[13:51:01] [Client thread/WARN]: Unable to play unknown soundEvent: minecraft:music.menu
[13:51:05] [Client thread/WARN]: Unable to play unknown soundEvent: minecraft:music.menu
Got this when trying to do the hello world thing... game launched but it didnt say hello world in console
Hey, I get this when trying to do the HelloWorld program, anyone know whats wrong?
[15:18:38] [Client thread/ERROR] [FML]: FML has detected a mod that is using a package name based on 'net.minecraft.src' : net.minecraft.src.FMLRenderAccessLibrary. This is generally a severe programming error. There should be no mod code in the minecraft namespace. MOVE YOUR MOD! If you're in eclipse, select your source code and 'refactor' it into a new package. Go on. DO IT NOW!
[15:18:41] [Client thread/ERROR] [FML]: Unable to read a class file correctly
java.lang.IllegalArgumentException
at org.objectweb.asm.ClassReader.(ClassReader.java:170) ~[asm-debug-all-4.1.jar:4.1]
at org.objectweb.asm.ClassReader.(ClassReader.java:153) ~[asm-debug-all-4.1.jar:4.1]
at org.objectweb.asm.ClassReader.(ClassReader.java:424) ~[asm-debug-all-4.1.jar:4.1]
at cpw.mods.fml.common.discovery.asm.ASMModParser.(ASMModParser.java:52) [ASMModParser.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:100) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.discover(DirectoryDiscoverer.java:53) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.ContainerType.findMods(ContainerType.java:42) [ContainerType.class:?]
at cpw.mods.fml.common.discovery.ModCandidate.explore(ModCandidate.java:71) [ModCandidate.class:?]
at cpw.mods.fml.common.discovery.ModDiscoverer.identifyMods(ModDiscoverer.java:123) [ModDiscoverer.class:?]
at cpw.mods.fml.common.Loader.identifyMods(Loader.java:347) [Loader.class:?]
at cpw.mods.fml.common.Loader.loadMods(Loader.java:468) [Loader.class:?]
at cpw.mods.fml.client.FMLClientHandler.beginMinecraftLoading(FMLClientHandler.java:204) [FMLClientHandler.class:?]
at net.minecraft.client.Minecraft.startGame(Minecraft.java:532) [Minecraft.class:?]
at net.minecraft.client.Minecraft.run(Minecraft.java:941) [Minecraft.class:?]
at net.minecraft.client.main.Main.main(Main.java:164) [Main.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_11]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_11]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_11]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_11]
at net.minecraft.launchwrapper.Launch.launch(Launch.java:134) [launchwrapper-1.9.jar:?]
at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.9.jar:?]
[15:18:41] [Client thread/ERROR] [FML]: There was a problem reading the file C:\Users\Jamie\Desktop\Minecraft\Mods\HelloWorld3\bin\jamie\minecraft\helloworld3\mod1\HelloWorld3.class - probably this is a corrupt file
cpw.mods.fml.common.LoaderException: java.lang.IllegalArgumentException
at cpw.mods.fml.common.discovery.asm.ASMModParser.(ASMModParser.java:58) ~[ASMModParser.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:100) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.discover(DirectoryDiscoverer.java:53) [DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.ContainerType.findMods(ContainerType.java:42) [ContainerType.class:?]
at cpw.mods.fml.common.discovery.ModCandidate.explore(ModCandidate.java:71) [ModCandidate.class:?]
at cpw.mods.fml.common.discovery.ModDiscoverer.identifyMods(ModDiscoverer.java:123) [ModDiscoverer.class:?]
at cpw.mods.fml.common.Loader.identifyMods(Loader.java:347) [Loader.class:?]
at cpw.mods.fml.common.Loader.loadMods(Loader.java:468) [Loader.class:?]
at cpw.mods.fml.client.FMLClientHandler.beginMinecraftLoading(FMLClientHandler.java:204) [FMLClientHandler.class:?]
at net.minecraft.client.Minecraft.startGame(Minecraft.java:532) [Minecraft.class:?]
at net.minecraft.client.Minecraft.run(Minecraft.java:941) [Minecraft.class:?]
at net.minecraft.client.main.Main.main(Main.java:164) [Main.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_11]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_11]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_11]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_11]
at net.minecraft.launchwrapper.Launch.launch(Launch.java:134) [launchwrapper-1.9.jar:?]
at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.9.jar:?]
Caused by: java.lang.IllegalArgumentException
at org.objectweb.asm.ClassReader.(ClassReader.java:170) ~[asm-debug-all-4.1.jar:4.1]
at org.objectweb.asm.ClassReader.(ClassReader.java:153) ~[asm-debug-all-4.1.jar:4.1]
at org.objectweb.asm.ClassReader.(ClassReader.java:424) ~[asm-debug-all-4.1.jar:4.1]
at cpw.mods.fml.common.discovery.asm.ASMModParser.(ASMModParser.java:52) ~[ASMModParser.class:?]
... 21 more
[15:18:41] [Client thread/WARN] [FML]: Identified a problem with the mod candidate C:\Users\Jamie\Desktop\Minecraft\Mods\HelloWorld3\bin, ignoring this source
cpw.mods.fml.common.LoaderException: java.lang.IllegalArgumentException
at cpw.mods.fml.common.discovery.asm.ASMModParser.(ASMModParser.java:58) ~[ASMModParser.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:100) ~[DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) ~[DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) ~[DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) ~[DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.exploreFileSystem(DirectoryDiscoverer.java:89) ~[DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.DirectoryDiscoverer.discover(DirectoryDiscoverer.java:53) ~[DirectoryDiscoverer.class:?]
at cpw.mods.fml.common.discovery.ContainerType.findMods(ContainerType.java:42) ~[ContainerType.class:?]
at cpw.mods.fml.common.discovery.ModCandidate.explore(ModCandidate.java:71) ~[ModCandidate.class:?]
at cpw.mods.fml.common.discovery.ModDiscoverer.identifyMods(ModDiscoverer.java:123) [ModDiscoverer.class:?]
at cpw.mods.fml.common.Loader.identifyMods(Loader.java:347) [Loader.class:?]
at cpw.mods.fml.common.Loader.loadMods(Loader.java:468) [Loader.class:?]
at cpw.mods.fml.client.FMLClientHandler.beginMinecraftLoading(FMLClientHandler.java:204) [FMLClientHandler.class:?]
at net.minecraft.client.Minecraft.startGame(Minecraft.java:532) [Minecraft.class:?]
at net.minecraft.client.Minecraft.run(Minecraft.java:941) [Minecraft.class:?]
at net.minecraft.client.main.Main.main(Main.java:164) [Main.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_11]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_11]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_11]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_11]
at net.minecraft.launchwrapper.Launch.launch(Launch.java:134) [launchwrapper-1.9.jar:?]
at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.9.jar:?]
Caused by: java.lang.IllegalArgumentException
at org.objectweb.asm.ClassReader.(ClassReader.java:170) ~[asm-debug-all-4.1.jar:4.1]
at org.objectweb.asm.ClassReader.(ClassReader.java:153) ~[asm-debug-all-4.1.jar:4.1]
at org.objectweb.asm.ClassReader.(ClassReader.java:424) ~[asm-debug-all-4.1.jar:4.1]
at cpw.mods.fml.common.discovery.asm.ASMModParser.(ASMModParser.java:52) ~[ASMModParser.class:?]
... 21 more
[1.7.10] Minecraft Modding for Dummies*
*This tutorial and its author have no affiliation with the For Dummies series of books or its publisher.
-- To table of contents.
-- To top of chapter.
Table of Contents
See that up arrow?
Forge is itself a mod that provides important tools for loading and building more mods. If you want to use or make mods, then you need Forge. But you'll need to know which version.
What is Eclipse?
Eclipse is a text editor for code and can run the written code in game for testing. It has a lot of advanced features, but you really only need two: the run button and the output screen.
The output screen is known as the console. It displays any errors and other information when you run your code. Don't worry too much if you don't understand any of it. Errors can be copied and pasted to the modding forum for help when needed.
What is Java?
Java is a programming language. A very powerful one! If you don't have any experience writing code, then it can look intimidating, but it shouldn't. The basics of Java are easy to explain and there are a number of great tutorials online. Best of all, when working with sample code, it isn't necessary to learn everything at once. With just a basic understanding, you can copy and paste a lot of what you need and figure the details out as you go.
What is Gradle?
Gradle manages mod distribution. Once you're done writing your code, you don't want to keep running it thru Eclipse, you want to turn it into a jar file that can be easily shared with others. Gradle does that.
Which Version?
The latest version of Minecraft keeps changing. This can be a problem for earlier mods. Fortunately, Minecraft allows you to select the version you want to run. However, different versions of Minecraft require different versions of Forge and each version of Forge must be installed separately. Forge allows for multiple installations and adds each one to the version selection screen in Minecraft. The two versions are then typically displayed together, separated by a hyphen.
See that up arrow?
By the end of that tutorial, your workspace should look something like this.
However, for newer versions of Forge, the "Run Configurations" "Main class" setting needs to be changed to "GradleStart".
And the "Program arguments" text box should be empty.
The "VM arguments" should remain.
Here is how to start a new project by another name. Close the previous project to get it out of way, or delete it from the workspace entirely. When deleting a project from the workspace, be sure the checkbox next to "delete project contents on disk" is not ticked. Otherwise, all files related to that project will be deleted from your hard drive.
Now, from the "File" menu, select "New", "Java Project". Give your project a name and click "Next".
On the "Source" tab, right-click the folder named "src" and select "Remove from build path".
Then right-click the folder above it, select "New Source Folder", and name it "java".
Repeat to create another folder by the name of "resources".
Go to the "Projects" tab and click "Add". Tick the checkbox next to forge and click "OK", then "Finish".
Don't forget to delete the useless "src" folder from the workspace. Then go to the "Run" menu and select "Run Configurations". On the "Classpath" tab, under "User Entries", the previous project will still be listed. Select the previous project and click "Remove".
Select "User Entries" and click "Add Projects". Tick the checkbox next to the new project and untick the two check boxes labeled "Add exported..." and "Add required...", then click "OK".
Click "Apply" then "Close".
Now the new project is ready.
See that up arrow?
Package names are really just a directory structure, with each word after a period representing the name of a subfolder within the folder before it. Thus any files placed in this package will appear in the folder path "..\whysalives\minecraft\helloworld\". You can read more about naming packages in the next chapter.
Now right-click the package and select "New", "Class". Name the class "HelloWorld" and click "Finish".
The new file will already contain some text.
Replace all of the text with the "HelloWorld.java" code below, then go to the "File" menu and select "Save".
HelloWorld.java
At this point, it's important to mention a setting in Eclipse, known as the "Compiler compliance level". The default setting depends on which version of Java is installed. Basically, when Eclipse reads the code, it expects it to meet certain Java standards, and different versions of Java have different standards. The wrong compliance level can cause an error that prevents your mod from running. What you want is level 1.7, which is the likely default if you've installed Java 7. If you've installed Java 8, then the likely default is 1.8. But that's not a problem because all you need to do is change the setting.
Change Setting
If level 1.7 isn't listed, then first uninstall your current version of Java, then install the Java Development Kit (JDK) version 7 or 8.
Now run the mod. When run in Eclipse, this mod will write "Hello.. World!" to the console. You may need to scroll back to find it.
Did you find it? Great! Pat yourself on the back, you're a modder.
Now that you've written a mod, you may wish to include some in game information about it, such as the author, a web address, or even credits to those whose code you borrowed to help build it.
Right-click the "resources" folder and select "New", "File". Name the file "mcmod.info" and click "Finish".
Copy the "mcmod.info" code below into the new file, then save.
mcmod.info
Run the mod again and click the "Mods" button on the Minecraft title screen. "Hello World" should appear in the list. Click it to read the mod information you just added.
Now that the mod is done, we want to export it into a jar file. For this, MrrGingerNinja has written another concise and complete tutorial: [1.7.2] MODDING WITH FORGE #2 - EXPORTING OUR MOD
Unfortunately, the Gradle build file in that tutorial doesn't work with newer versions of Forge and will produce an error. The "SNAPSHOT" is outdated and a newer version is required. Try the "build.gradle" file below.
build.gradle
See that up arrow?
Package Names
Open Source
In Eclipse, open the Forge folder visible in the "Package Explorer", then open "Referenced Libraries".
Whoa! That's a lot of jar files. Now open the "forgeSrc..." file at the top of the list.
The shear number of packages and associated class files is overwhelming, but don't be afraid to peek inside. These are ".class" files, not ".java" files. That means you can't edit them, on purpose or by accident. You can however, copy them! That might not seem important right now, but it will. Fortunately, you don't need to fumble thru each file aimlessly. When you click on any class, variable, or method name, simply right-click and select "Open Declaration" from the popup menu. Eclipse will then open the file you're looking for.
Javadoc
It's a special kind of comment that allows the javadoc program to generate a user-manual for your code. But perhaps more relevant is that Eclipse uses it to tell you what you need to know when you need to know it. In the example above, a javadoc comment is attached to a method. Now, as seen below, Eclipse will provide that information in a popup window anywhere that method name appears. Simply hover the mouse cursor over the method name to read the comment. Comments can even contain links to other comments, clickable in the popup window.
Javadoc isn't necessary, but the larger a program grows, the more helpful it becomes. Especially if you want others to understand your code. Eclipse uses the comments automatically and also includes a javadoc interface, found in the "Project" menu, for generating a user-manual. Otherwise, you can run javadoc directly from the Command Prompt in Windows. Here is an example.
If it doesn't run, then you'll need to add javadoc to the path in your Windows environment variables.
Add to Path
After running the command, you'll have a new directory in the parent directory by the name of "doc". The contents will look something like this.
Example: Hello World -- Javadoc.
Cascading Methods
You can only cascade methods that have been specifically designed for it. It is unnecessary and potentially confusing. The same thing can be accomplished with the same methods in a more reliable manner, shown below.
Anonymous Classes
They are a convenience that eliminates the need to include a whole separate class file with its own class name. They are also unnecessary and potentially confusing. The same thing can be accomplished in a more recognizable manner, shown below.
Links
See that up arrow?
This mod creates a custom block with a custom texture, gives it a custom item to drop and a custom sound to play when the block is destroyed, then generates the block as a custom ore to be found in the world, and adds related custom crafting recipes. Also adds a custom inventory-tab in Creative Mode for custom blocks and items. Note: when in Creative Mode, it is normal that blocks don't drop items.
HelloBlock.java
TestBlock.java
TestItem.java
TestTab.java
TestWorldGenerator.java
sounds.json
en_US.lang
mcmod.info
Right-click and "Save link as"
coin.ogg -- Custom drop sound.
testBlock.png -- Custom block texture
testItem.png -- Custom item texture.
build.gradle
Javadoc
Hello Block -- Documentation.
Now run the mod and take a look around.
Extras
Custom Block Faces
Insert Code
Put this near the bottom of the "TestBlock" class, above the last three lines that read "END class".
Now, instead of a single texture file, we need six texture files, one for each face. They keep the same name but each is suffixed with an underscore followed by a letter: B-ottom, T-op, N-orth, S-outh, W-est, E-ast.
Right-click and "Save link as"
testBlock_B.png -- Bottom.
testBlock_T.png -- Top.
testBlock_N.png -- North.
testBlock_S.png -- South.
testBlock_W.png -- West.
testBlock_E.png -- East.
Custom Structures
Insert Code
Now when the block is placed, you get a spiral staircase instead. The first set of steps point north and placement of the initial block is outlined in red, below.
We can then climb to the top of the spiral to stack another one on top. The correct placement is marked in red, below.
Repeat to quickly and easily build a glowing spiral staircase of any height.
Screenshots
Custom Tab (creative mode)
Shapeless Crafting
Shaped Crafting
Smelting
See that up arrow?
HelloProjectile.java
ClientProxy.java
CommonProxy.java
CustomEventHandler.java
CustomTab.java
EntityThrowingAxe.java
EntityThrowingAxeDiamond.java
EntityThrowingAxeGold.java
EntityThrowingAxeIron.java
EntityThrowingAxeStone.java
EntityThrowingAxeWood.java
RenderThrowingAxe.java
ThrowingAxe.java
en_US.lang
mcmod.info
Right-click and "Save link as"
penny.png -- Custom tab icon.
build.gradle
Javadoc
Hello Projectile -- Documentation.
Screenshots
Temporary Recipes
Just try it!
Credits
See that up arrow?
When submitting errors, please post your entire Console output and state the versions of your Minecraft, Forge, Eclipse, and Java installations.
If all else fails and you believe you're genuinely having trouble with your installation of Forge, run the following command in Command Prompt from inside your forge directory.
Newer versions of Forge aren't supposed to require any program arguments, but if you believe you're specifically having trouble with your assets, you can try these.
See that up arrow?
I was not paid to produce this tutorial, nor am I directly or indirectly associated with any related company, organization, or marketing apparatus. I give it freely for the sole reason, and the sole hope, that it will facilitate a more active and inclusive modding community. I have attempted to answer, as completely as possible, each question I myself encountered while learning to mod Minecraft for the first time. Like you, I'm still learning, and I encourage you to share any questions or insights that you may have.
Thank you for reading. I hope you have found it useful. If so, please comment. It keeps the thread bumped so others may find it more easily.
See that up arrow?
See that up arrow?
See that up arrow?
Looking forward to more.
Thanks! More is coming.
See that up arrow?
-Al
Explore the galaxy with Galaxies: Parzi's Star Wars Mod!
Initializing LWJGL OpenAL
(The LWJGL binding of OpenAL. For more information, see http://www.lwjgl.org)
OpenAL initialized.
[13:50:50] [MCO Availability Checker #1/ERROR]: Couldn't connect to Realms
[13:50:50] [Sound Library Loader/INFO]: Sound engine started
[13:50:55] [Client thread/WARN]: Unable to play unknown soundEvent: minecraft:music.menu
[13:51:01] [Client thread/WARN]: Unable to play unknown soundEvent: minecraft:music.menu
[13:51:05] [Client thread/WARN]: Unable to play unknown soundEvent: minecraft:music.menu
Got this when trying to do the hello world thing... game launched but it didnt say hello world in console
Do you have java 8
Changing to java 7 fixed it for me
See that up arrow?
See that up arrow?