In this tutorial, I will show you how to use the Power Advantage API to make your own Power Advantage add-on mod. The example powered machine in this tutorial is a steam-powered rock crusher that pulverizes blocks into the crushed items (uses same recipes as the Base Metals crackhammer), compatible with the Steam Advantage add-on mod.
Note that this tutorial was written for people who are new to making Minecraft mods, but have a little bit of Java programming experience. Though I won't mention it directly, you will need to understand how packages work in Java, the subtle differences between private, protected, and public methods, and how to implement abstract classes that have constructors. If you feel overwhelmed, I recommend taking a few lessons from the official Java Tutorial (that's how I learned Java).
Part 1: Setup Your IDE
Step 0 (skip if you already have Eclipse and your JDK is up-to-date)
Go to java.oracle.com and install the latest version of the Java Developer Kit (JDK) for Java 8 or Java 7 (JDK 8 is recommended). Then go to eclipse.org to download and install the Eclipse IDE for Java Developers.
Just because the library files are in libs, that doesn't mean that our build script (Gradle) or our IDE (Eclipse) know how to use them. To tell Gradle to use them (and enable Java 7 compiling), edit your build.gradle file and add the following:
Notice that we also edit the group and archivesBaseName values to match the project.
Time to tell Eclipse to use the API jars. Launch Eclipse and use the eclipse folder of your project as your workspace. Then right-click on your project folder in the Project Explorer and select Properties. Then click on Java Builder Path, go to the Libraries tab, and click the Add JARS... button. Select the Base Metals and Power Advantage API jars from the libs folder and click OK.
I put a lot of work into the documentation for the APIs and it would be a shame to not use that. To link the documentation and source-code to the API jars that you added, select them in the Project Explorer (they will be near the bottom) and then right-clight on the .jar and select Properties. Click Java Source Attachment and then click the Browse button to find the correct *-src.zip file.
Then click on Javadoc Location, select Javadoc in archive and Workspace file, then click the Browse button to find the correct *-javadoc.zip file.
Now you will be able to use the APIs and see their source-code inside Eclipse
Step 8 (optional)
If you are doing version control using git (and I strongly recommend using either git, mercurial, or bzr), then you will want to create a file named .gitignore and put the following inside:
This will prevent the hundreds of temp files generated by Eclipse from polluting your code repository.
Part 2: Writing the Java Code
The first thing you need to do is create the mod's entry point. We set group="steamcrusher" in the build.gradle file, which means that we need to make our mod's entry class in the steamcrusher package. So delete the example mod code, then right-click on the project item src/main/java and choose New -> Package and name the package steamcrusher.
Right-click on the package and select New -> Class to create the mod's main class. Let's name it SteamCrusherMod. Add the following code to make it launch as a mod that uses Power Advantage and Base Metals:
Now you have the very basic skeleton of a mod, but it doesn't add anything yet. To add a new machine with the Power Advantage API, we need 3 classes: a block class, and tile entity class, and a GUI class. We'll start with the tile entity class.
Create a new class called RockCrusherTileEntity and make it extend the TileEntitySimplePowerConsumer class. Tile entities need to have a no-arguments constructor, so we'll need to set the contructor up like this to initialize our steam-powered machine:
Simply setting the conduit type to "steam" in the constructor will cause this machine to automatically accept steam power from any other Power Advantage add-on mod that generates steam power.
At this point, Eclipse will complain that we haven't implemented the abstract methods. Let's do that now.
The first method to tackle is the getInventory() method. All it need to do is return this machine's inventory, which will be an array of size 2 (one input and one output inventory slots).
Next we are going to fill-in the tickUpdate(boolean isServerWorld) method. This is the meat-and-potatoes of our mod. It is called on every world tick (20x/second) and handles the actual logic of our machine. All the details of gathering steam and storing steam energy are handled for us, so we just need to focus on crushing the input block item and putting the results in the output item slot.
Note that the above implementation is less than ideal, as it will not pulverize the input item unless the output slot is completely empty. For the sake of simplifying this tutorial, I have left out the code necessary to merge item stacks. You can improve your implementation if you are feeling confident.
Also note that it is very important to call this.sync() whenever you make a change (such as the progress or energy level) that you want to have appear in a GUI on the client.
Finally we need to handle synchronization between the server and the client, which happens when we call this.sync(). Fortunately, the Power Advantage API can handle the details for us if we pass the synchronization data as an integer array via the getDataFieldArray() method. The way this works is that when the server sends a sync packet, it will first call the prepareDataFieldsForSync() on the server-side tile entity (indicating to the server tile entity that it needs to store sync data in an integer array), and then reads the data from the getDataFieldArray() method. That integer array is sent to the clients in the update packet. On the client-side, upon receiving an update packet, the getDataFieldArray() is called and the values from the data packet are stored in the array. Then the onDataFieldUpdate() method is called on the tile entity so that it knows that it's time to read the sync data and update itself.
We have 2 variables of data that we want to sync (items are already handled for us): the current progress and the amount of steam. Here's how we do that:
Your tile entity class is almost done. The last thing to do is override the save/load methods to save the progress when the chunk unloads (the steam energy is already saved for us). Just add the following to the end of the class:
Now to make the block class. This part is easy. Make a new class called RockCrusherBlock and have it extend the BlockSimplePowerConsumer class like this:
For extra credit, make hasComparatorInputOverride() return true and have getComparatorInputOverride(World world, BlockPos coord) return a number from 0 to 15 that indicates roughly how many items are in the tile entity's input slot.
Finally, we need a GUI class to handle the GUI that will pop-up when the player right-clicks on the block. GUIs are usually a little complicated in Forge, but the Power Advantage API has simplified it to a single class.
Create a new class called RockCrusherGUI that extends SimpleMachineGUI:
Seriously, that's it. This constructor will use the indicated image from the resourcepack to show the GUI and pout the input slot at pixel coordinate (60,30) and the output slot at coordinate (100,30). Eventually, we will be overriding the drawGUIDecorations(...) method when we decide to animate the GUI to show a progress bar, but for now this is all we need.
Go back to SteamCrusherMod.java because it's time to add our machine to the game. In the preInit(...) method, we want to register all new blocks and items. Then in the init(...) method, we want to register the tile entity and the GUI (using Power Advantage's MachineGUIRegistry class) and then add the recipes (recipes not shown in this tutorial). We also want to tell Minecraft to use the standard item model renderer (as opposed to a custom renderer), but only on the client-side.
Part 3: Making the Resourcepack
If you launch the mod right now, everything will look like ugly black and magenta squares. That's because we haven't created the resourcepack to tell Minecraft what everything should look like. The first thing you need to do is create some texture images. Create the folloing image folders/files in the src/main/resources folder (I use GIMP as my image editor):
The first two images should be 16x16 pixels and the third one should be 256x256 pixels with a transparent background. Here's some example images:
Not exactly fine art, but it'll do the job for now.
Now we need to create the model file. Create the file src/main/resources/assets/steamcrusher/models/block/steam_crusher.json
We'll use the vanilla furnace model as our template, so the json model for our crusher should look like this:
Next we create the blockstate file that tells Minecraft which model to use and how to rotate the model for a given block-state. Create the blockstate file src/main/resources/assets/steamcrusher/blockstates/steam_crusher.json:
Finally, we need to add a model for how the block will look like in your hand and in your inventory. Usually we just want to use the same model as we do for the block placed in the world. Create the item model file src/main/resources/assets/steamcrusher/models/item/steam_crusher.json:
One more thing! We need to name our block in a language file. Create the english translation file src/main/resources/assets/steamcrusher/lang/en_US.lang:
Note that the format for blocks is tile.modid.unlocalized name.name and the format for items is item.modid.unlocalized name.name and it is ok to have periods in the unlocalized name
And we're done! Compile your mod in the command-line with the command gradlew build
Your final mod file will appear in the build/libs folder. Just drop the .jar file in your Minecraft Forge mods folder and give it a test-drive!
Part 4: Animating the GUI
You may have noticed that there's no progress bar in the GUI to show the activity of your machine. That ends now.
Go to RockCrusherTileEntity.java and add a getProgress() method that returns the progress value as a fraction from 0 to 1. Double check that you call this.sync() in the tickUpdate(...) method whenever you make a change to the progress variable.
Go back to the RockCrusherGUI class and override the drawGUIDecorations(...) method. We will want to class-check the srcEntity object to make sure it is the correct class (and not null):
In this example, the progress arrow is in the gui image to the right, just outside the GUI window area, at pixel coordinates 176,0. The arrow is 20x20 pixels in size.
You're done! Give it a test. Note that if your GUI isn't animating, there's a good chance that you have a bug preventing the server from syncing the client (e.g. this.sync() not being called).
All of my source-code is open-source, so you are welcome to peak at my mods, such as the Steam Advantage mod, for more examples on how to use the Power Advantage API and making Minecraft mods more generally.
My only issue with the APIs is that they don't include assets. This makes testing out GUIs kinda iffy, as without the player inventory background all you see is the black-and-purple placeholder texture. I ended up solving the issue for myself by opening up the API jars and copying the asset folders into them from your proper releases.
You want this ported to the wiki as well? I think you should leave this copy as forums are where people will find it but purely for formatting reasons, cause these forum posts can be a pain to re-create, it might be easier to have somewhere to copy from.
@Zohar: You can just put a link to this forum post for now. BTW, the wiki is looking great!
@Zaphodious: Developers with automated build scripts like to have small API files so that the download time doesn't have a big impact on the time it takes to compile their mod. Your solution is totally legit.
I was wondering if you could make a youtube tutorial video on this. Some people, including myself, learn better by seeing it and hearing it. I understand if you don't have the time to do it but it would be very helpful.
Basically, I've never made a YouTube video in my life and see more value in having text that can be copied and pasted (especially command-line commands). My limited experience in professional video production proved that making a quality video, even a short 10 minute video, takes several days to weeks of hard work (scripting, story boarding, pre-production, setup, recording, editing, post-production and then publishing).
Also, my personal experience with mod tutorial videos has been horrible because the text in the video is blurry and illegible when played at video resolutions that can play smoothly on my internet connection and there's never a text transcript for the video.
Rollback Post to RevisionRollBack
My mods: Base Metals, Power Advantage, Steam Advantage, Electric Advantage, Minecraft Mineralogy, Dr. Cyano's Lootable Bodies, Dr. Cyano's Wonderful Wands & Wizarding Robes
I'm trying to make a maglev mod using PowerAdvantage but I think I need the block for the "rail" to be a SimplePowerConsumer and a SimplePowerConduit. This is because I have a block to create the energy and that energy should be propagated down the line of "rail" blocks. However when a maglev passes over a given "rail" block, it should drain the block's buffer. As of now, I can't seem to find an easy way to get both the propagation of the conduits and the consuming of the consumers. Is there a simple way to get the best of both worlds? Thank you in advance.