Skip to content

Instantly share code, notes, and snippets.

@WesCook
Last active June 21, 2021 21:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save WesCook/c9e282e49580588dd397350283e7e4e1 to your computer and use it in GitHub Desktop.
Save WesCook/c9e282e49580588dd397350283e7e4e1 to your computer and use it in GitHub Desktop.
Forge tutorial covering implementation of Fluids
## Introduction
In Forge, "fluids" is a catch-all term representing both liquids and gasses in Minecraft. You can register your own fluids, define their appearance and functionality, and interact with them in different ways.
## Preparing your Workspace
Before being used, fluids first need to be registered with the Forge FluidRegistry. This acts as a list of all fluids that Forge knows about, as well as their properties and apperance.
To get started, you'll want to first create a class to manage your fluids. A common name is `ModFluids`. It serves the same purpose as `ModBlocks` or `ModItems` classes.
!!! Note It's recommended, but not required that you create a /fluids package for grouping fluid-related classes.
## Registering a Fluid
To get started, let's create an example fluid for oil. Create a new class which extends `Fluid`.
```
class FluidOil extends Fluid {
FluidOil() {
super("oil", new ResourceLocation(ExampleMod.MODID, "fluids/oil_still"), new ResourceLocation(ExampleMod.MODID, "fluids/oil_flow"));
FluidRegistry.registerFluid(this);
}
}
```
First we're calling the super() which takes three properties: the fluid name, the resource location for the still texture, and the resource location for the flowing texture.
The name is what we're registering our fluid as within Forge. The two textures describe how our fluid should look in a still and flowing state.
For an example of these graphics, please see /assets/minecraft/textures/blocks/water_flow.png and water_still.png in the decompiled Minecraft source.
Now create an instance of this fluid from your `ModFluids` file to finish registering your fluid with Forge.
```
public static FluidOil fluidOil;
public static void registerFluids() {
fluidOil = new FluidOil();
}
```
Be sure to call registerFluids() from your common proxy as well, just like you would for items or blocks. It must run in pre-init.
```
public void preInit(FMLPreInitializationEvent event) {
ModFluids.registerFluids();
}
```
## Fluids in the World
Now your fluid should be registered, and is available for use by your mod or others. The problem is, how do you actually place it in the world?
To see a physical representation of our fluid we're going to need to also create a `BlockFluid`. This will allow us to place and interact with the fluid beyond simply using it in buckets or machines.
Creating a BlockFluid is not unlike creating a [block](http://mcforge.readthedocs.io/en/latest/blocks/blocks/). To get started we can create a new class to extend `BlockFluidClassic`. With our previous oil example, that might look as follows:
```
class BlockFluidOil extends BlockFluidClassic {
BlockFluidOil() {
super(ModFluids.fluidOil, Material.WATER);
setRegistryName("oil");
setUnlocalizedName(getRegistryName().toString());
GameRegistry.register(this);
}
}
```
Once again we need to call super(), although this time we're passing in the fluid we registered earlier. As we created an instance in ModFluids we can simply pass that in now. We also need to define a material to describe the fluid, of which water will do for now. Then we set a name and register our new block with Forge.
Back to ModFluids, we update our registerFluids() method to also create an instance of our BlockFluid.
```
public static FluidBacteria fluidOil;
public static BlockFluidBacteria blockFluidOil;
public static void registerFluids() {
fluidOil = new FluidOil();
blockFluidOil = new BlockFluidOil();
}
```
And suddenly oil is placeable in our world. Success! Unfortunately it's missing a texture. We'll need to create a [blockstate file](http://mcforge.readthedocs.io/en/latest/blockstates/states/) to describe our new BlockFluid.
Under /blockstates, create a new file named `oil.json`. The JSON for a normal fluid blockstate would look like:
```
{
"forge_marker": 1,
"variants": {
"normal": [{
"model": "forge:fluid",
"transform": "forge:default-item",
"custom": {
"fluid": "oil"
}
}]
}
}
```
Now we need to set up the state mapper. While Forge often handles this for us, in this case we need to trigger it manually on the client proxy. This will allow our fluid to render at the correct level, so flowing fluids appear smaller than source blocks.
Back to blockFluidOil, we add a rendering method:
```
@SideOnly(Side.CLIENT)
void render() {
ModelLoader.setCustomStateMapper(this, new StateMap.Builder().ignore(LEVEL).build());
}
```
In ModFluids, we'll set up a method to render all fluids in our mod:
```
public static void renderFluids() {
blockFluidOil.render();
}
```
Which needs to be called from our client proxy during pre-init.
```
public void preInit(FMLPreInitializationEvent event) {
super.preInit(event);
ModFluids.renderFluids();
}
```
Finally we have our fluid registered and rendering in world! The final component however, is adding a bucket or container for our item.
## Universal Bucket
Forge offers a very powerful method of adding buckets for custom fluids known as the universal bucket (or in code as the dynamic bucket). This is the easiest way to add a container for our fluid.
To use the universal bucket you first need to turn the feature on. This needs to happen *before* pre-init. In your main mod file (eg. ExampleMod.java):
```
static {
FluidRegistry.enableUniversalBucket();
}
```
Now we can simply add a bucket for any fluid in our mod. In the constructor for our FluidOil class, add the line `FluidRegistry.addBucketForFluid(this);`. Forge will automatically generate a bucket and dynamically set the texture for it, allowing us to easily manipulate our fluid in the world.
@howtonotwin
Copy link

howtonotwin commented Oct 2, 2016

  • Perhaps the page should be structured like blocks/blocks.md?
    • Fluids
      • Creating a Fluid
        • Params to constructor
        • Note what the setters do
        • "Advanced fluids mean creating own class" copied from blocks.md
      • Creating a BlockFluid
        • Difference between Classic and Finite
        • Straight out say that "this is a Block"
        • Classic
        • Finite
      • Registering a Fluid
      • Registering a BlockFluid
      • Rendering
      • Bucket
  • new StateMap.Builder().ignore(BlockFluidBase.LEVEL).build() instead of anon class.
    • Note why that's done
  • Move the defaults block in the blockstate into the normal variant directly
  • Don't give direct tutorial-ish examples.

@galennare
Copy link

Please fix your markdown.

@MinecraftWero
Copy link

nice tutorial, would be great if it could be updated for 1.12.2

@Davoleo
Copy link

Davoleo commented Aug 28, 2018

yep, I agree ^^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment