User Tools

Site Tools


tutorial:fluids

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:fluids [2021/05/08 23:41] – Fix name for BLOCK_ATLAS_TEXTURE alfiejfstutorial:fluids [2026/02/23 14:26] (current) – fixed reference infinitychances
Line 5: Line 5:
  
 ===== Making an abstract fluid ===== ===== Making an abstract fluid =====
-Vanilla fluids extend ''net.minecraft.fluid.FlowableFluid'', and so shall we.+Vanilla fluids extend ''net.minecraft.world.level.material.FlowingFluid'', and so shall we.
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-public abstract class TutorialFluid extends FlowableFluid {+public abstract class TutorialFluid extends FlowingFluid {
  /**  /**
  * @return whether the given fluid an instance of this fluid  * @return whether the given fluid an instance of this fluid
  */  */
  @Override  @Override
- public boolean matchesType(Fluid fluid) { + public boolean isSame(Fluid fluid) { 
- return fluid == getStill() || fluid == getFlowing();+ return fluid == getSource() || fluid == getFlowing();
  }  }
- +
  /**  /**
- * @return whether the fluid infinite like water+ * @return whether the fluid is infinite (which means can be infinitely created like water). In vanilla, it depends on the game rule.
  */  */
  @Override  @Override
- protected boolean isInfinite() {+ protected boolean canConvertToSource() {
  return false;  return false;
  }  }
- +
  /**  /**
- * Perform actions when fluid flows into a replaceable block. Water drops+ * Perform actions when the fluid flows into a replaceable block. Water drops
  * the block's loot table. Lava plays the "block.lava.extinguish" sound.  * the block's loot table. Lava plays the "block.lava.extinguish" sound.
  */  */
  @Override  @Override
- protected void beforeBreakingBlock(WorldAccess world, BlockPos pos, BlockState state) { + protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state) { 
- final BlockEntity blockEntity = state.getBlock().hasBlockEntity() ? world.getBlockEntity(pos) : null; + final BlockEntity entity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null; 
- Block.dropStacks(state, world, pos, blockEntity);+ Block.dropResources(state, level, pos, entity);
  }  }
- +
  /**  /**
- * Lava returns true if its FluidState is above a certain height and the+ * Lava returns true if it'FluidState is above a certain height and the
  * Fluid is Water.  * Fluid is Water.
  
Line 41: Line 41:
  */  */
  @Override  @Override
- protected boolean canBeReplacedWith(FluidState fluidStateBlockView blockView, BlockPos blockPos, Fluid fluid, Direction direction) {+ protected boolean canBeReplacedWith(FluidState stateBlockGetter level, BlockPos pos, Fluid fluid, Direction direction) {
  return false;  return false;
  }  }
- +
  /**  /**
  * Possibly related to the distance checks for flowing into nearby holes?  * Possibly related to the distance checks for flowing into nearby holes?
Line 50: Line 50:
  */  */
  @Override  @Override
- protected int getFlowSpeed(WorldView worldView) {+ protected int getSlopeFindDistance(LevelReader reader) {
  return 4;  return 4;
  }  }
- +
  /**  /**
- * Water returns 1. Lava returns 2 in the Overworld and 1 in the Nether.+ * Water returns 1. Lava returns 2 in the overworld and 1 in the nether. 
 + * @return How many levels a fluid loses per block.
  */  */
  @Override  @Override
- protected int getLevelDecreasePerBlock(WorldView worldView) {+ protected int getDropOff(LevelReader reader) {
  return 1;  return 1;
  }  }
- +
  /**  /**
  * Water returns 5. Lava returns 30 in the Overworld and 10 in the Nether.  * Water returns 5. Lava returns 30 in the Overworld and 10 in the Nether.
 + * Seems to return the delay before it updates its state.
  */  */
  @Override  @Override
- public int getTickRate(WorldView worldView) {+ public int getTickDelay(LevelReader reader) {
  return 5;  return 5;
  }  }
- +
  /**  /**
  * Water and Lava both return 100.0F.  * Water and Lava both return 100.0F.
  */  */
  @Override  @Override
- protected float getBlastResistance() {+ protected float getExplosionResistance() {
  return 100.0F;  return 100.0F;
  }  }
Line 86: Line 88:
 public abstract class AcidFluid extends TutorialFluid { public abstract class AcidFluid extends TutorialFluid {
  @Override  @Override
- public Fluid getStill() { + public Fluid getSource() { 
- return <YOUR_STILL_FLUID_HERE>;+ return YOUR_SOURCE_FLUID_HERE;
  }  }
- +
  @Override  @Override
  public Fluid getFlowing() {  public Fluid getFlowing() {
- return <YOUR_FLOWING_FLUID_HERE>;+ return YOUR_FLOWING_FLUID_HERE;
  }  }
- +
  @Override  @Override
- public Item getBucketItem() { + public Item getBucket() { 
- return <YOUR_BUCKET_ITEM_HERE>;+ return YOUR_BUCKET_ITEM_HERE;
  }  }
- +
  @Override  @Override
- protected BlockState toBlockState(FluidState fluidState) { + protected BlockState createLegacyBlock(FluidState state) { 
- // method_15741 converts the LEVEL_1_8 of the fluid state to the LEVEL_15 the fluid block uses + return YOUR_FLUID_BLOCK_HERE.defaultBlockState().setValue(BlockStateProperties.LEVELgetLegacyLevel(state));
- return <YOUR_FLUID_BLOCK_HERE>.getDefaultState().with(Properties.LEVEL_15method_15741(fluidState));+
  }  }
- +
  public static class Flowing extends AcidFluid {  public static class Flowing extends AcidFluid {
  @Override  @Override
- protected void appendProperties(StateManager.Builder<Fluid, FluidState> builder) { + protected void createFluidStateDefinition(StateDefinition.Builder<Fluid, FluidState> builder) { 
- super.appendProperties(builder);+ super.createFluidStateDefinition(builder);
  builder.add(LEVEL);  builder.add(LEVEL);
  }  }
- +
  @Override  @Override
- public int getLevel(FluidState fluidState) { + public boolean isSource(FluidState state) { 
- return fluidState.get(LEVEL);+ return false;
  }  }
- +
  @Override  @Override
- public boolean isStill(FluidState fluidState) { + public int getAmount(FluidState state) { 
- return false;+ return state.getValue(LEVEL);
  }  }
  }  }
- +
  public static class Still extends AcidFluid {  public static class Still extends AcidFluid {
  @Override  @Override
- public int getLevel(FluidState fluidState) { + public boolean isSource(FluidState state) { 
- return 8;+ return true;
  }  }
- +
  @Override  @Override
- public boolean isStill(FluidState fluidState) { + public int getAmount(FluidState state) { 
- return true;+ return 8;
  }  }
  }  }
Line 141: Line 142:
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-public static FlowableFluid STILL_ACID+public static FlowingFluid ACID_SOURCE
-public static FlowableFluid FLOWING_ACID;+public static FlowingFluid FLOWING_ACID;
 public static Item ACID_BUCKET; public static Item ACID_BUCKET;
 + 
 @Override @Override
 public void onInitialize() { public void onInitialize() {
- STILL_ACID = Registry.register(Registry.FLUID, new Identifier(MOD_ID, "acid"), new AcidFluid.Still()); + ACID_SOURCE = Registry.register(BuiltInRegistries.FLUID, Identifier.fromNamespaceAndPath("tutorial", "acid"), new AcidFluid.Still()) 
- FLOWING_ACID = Registry.register(Registry.FLUID, new Identifier(MOD_ID, "flowing_acid"), new AcidFluid.Flowing()); + FLOWING_ACID =  Registry.register(BuiltInRegistries.FLUID, Identifier.fromNamespaceAndPath("tutorial", "flowing_acid"), new AcidFluid.Flowing()); 
- ACID_BUCKET = Registry.register(Registry.ITEM, new Identifier(MOD_ID, "acid_bucket"), new BucketItem(STILL_ACID, new Item.Settings().recipeRemainder(Items.BUCKET).maxCount(1))); + ACID_BUCKET = Registry.register(BuiltInRegistries.ITEM, Identifier.fromNamespaceAndPath("tutorial", "acid_bucket"),  
- +        new BucketItem(ACID_SOURCE, properties), new Item.Properties().craftRemainder(Items.BUCKET).stacksTo(1); 
 + 
  // ...  // ...
 } }
 + 
 // ... // ...
 </code> </code>
  
-To make a custom fluid behave more like water or lava, you must add it to a corresponding fluid tag: For water, make a ''data/minecraft/tags/fluids/water.json'' file and write the identifiers of your fluids in there:+To make a custom fluid behave more like water or lava, you must add it to a corresponding fluid tag: For water, make a ''data/minecraft/tags/fluid/water.json'' file and write the identifiers of your fluids in there:
 <code json [enable_line_numbers="true"]> <code json [enable_line_numbers="true"]>
 { {
Line 163: Line 165:
  "values":  "values":
  [  [
- "your_mod_id:acid", + "tutorial:acid", 
- "your_mod_id:flowing_acid"+ "tutorial:flowing_acid"
  ]  ]
 } }
Line 170: Line 172:
  
 ===== Making a fluid block ===== ===== Making a fluid block =====
-Next we need to create a block which will represent acid in the world. ''net.minecraft.block.FluidBlock'' is the class we need to use, but since its constructor is protected, we can't construct it directly. Some ways to use it are to make a subclass or an anonymous subclass. Here we will be showing the latter. In your ''ModInitializer'':+Next we need to create a block which will represent acid in the world. ''net.minecraft.world.level.block.LiquidBlock'' is the class we need to use. In your ''ModInitializer'':
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
Line 177: Line 179:
 @Override @Override
 public void onInitialize() { public void onInitialize() {
- ACID = Registry.register(Registry.BLOCK, new Identifier(MOD_ID, "acid"), new FluidBlock(STILL_ACIDFabricBlockSettings.copy(Blocks.WATER)){});+ ACID = Registry.register(BuiltInRegistries.BLOCK, Identifier.fromNamespaceAndPath("tutorial", "acid"), new LiquidBlock(ACID_SOURCEBlockBehaviour.Properties.ofFullCopy(Blocks.WATER)));
   
  // ...  // ...
Line 188: Line 190:
 public abstract class AcidFluid extends TutorialFluid { public abstract class AcidFluid extends TutorialFluid {
  @Override  @Override
- public Fluid getStill() { + public Fluid getSource() { 
- return TutorialMod.STILL_ACID;+ return TutorialMod.ACID_SOURCE;
  }  }
- +
  @Override  @Override
  public Fluid getFlowing() {  public Fluid getFlowing() {
  return TutorialMod.FLOWING_ACID;  return TutorialMod.FLOWING_ACID;
  }  }
- +
  @Override  @Override
- public Item getBucketItem() {+ public Item getBucket() {
  return TutorialMod.ACID_BUCKET;  return TutorialMod.ACID_BUCKET;
  }  }
- +
  @Override  @Override
- protected BlockState toBlockState(FluidState fluidState) { + protected BlockState createLegacyBlock(FluidState state) { 
- // method_15741 converts the LEVEL_1_8 of the fluid state to the LEVEL_15 the fluid block uses + return TutorialMod.ACID.defaultBlockState().setValue(BlockStateProperties.LEVELgetLegacyLevel(state));
- return TutorialMod.ACID.getDefaultState().with(Properties.LEVEL_15method_15741(fluidState));+
  }  }
-   + 
 + public static class Flowing extends AcidFluid { 
 + @Override 
 + protected void createFluidStateDefinition(StateDefinition.Builder<Fluid, FluidState> builder) { 
 + super.createFluidStateDefinition(builder); 
 + builder.add(LEVEL); 
 +
 + 
 + @Override 
 + public boolean isSource(FluidState state) { 
 + return false; 
 +
 + 
 + @Override 
 + public int getAmount(FluidState state) { 
 + return state.getValue(LEVEL); 
 +
 +
 + 
 + public static class Still extends AcidFluid { 
 + @Override 
 + public boolean isSource(FluidState state) { 
 + return true; 
 +
 + 
 + @Override 
 + public int getAmount(FluidState state) { 
 + return 8; 
 +
 +
 +}
 </code> </code>
  
 ===== Rendering setup ===== ===== Rendering setup =====
-For your fluids to have textures or be tinted with a color, you will need to register a ''FluidRenderHandler'' for them. Here, we will reuse water's textures and just change the tint color applied to them. To make sure the textures are rendered as translucent, you can use Fabric's ''BlockRenderLayerMap''.+For your fluids to have textures or be tinted with a color, you will need to register a ''FluidRenderHandler'' for them. Here, we will reuse water's textures and just change the tint color applied to them. To make sure the textures are rendered as translucent, you can use Fabric's ''BlockRenderLayerMap'' (see [[blockappearance]]).
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
 +@Environment(EnvType.CLIENT)
 public class TutorialModClient implements ClientModInitializer { public class TutorialModClient implements ClientModInitializer {
  
  @Override  @Override
  public void onInitializeClient() {  public void onInitializeClient() {
- setupFluidRendering(TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID, new Identifier("minecraft", "water"), 0x4CC248); + FluidRenderHandlerRegistry.INSTANCE.register(TutorialMod.ACID_SOURCE, TutorialMod.FLOWING_ACID, SimpleFluidRenderHandler.coloredWater(0x4CC248)); 
- BlockRenderLayerMap.INSTANCE.putFluids(RenderLayer.getTranslucent(), TutorialMod.STILL_ACID, TutorialMod.FLOWING_ACID); + 
-  + BlockRenderLayerMap.putFluids(ChunkSectionLayer.TRANSLUCENT, TutorialMod.ACID_SOURCE, TutorialMod.FLOWING_ACID);
- // ... +
-+
-  +
- public static void setupFluidRendering(final Fluid still, final Fluid flowing, final Identifier textureFluidId, final int color) { +
- final Identifier stillSpriteId = new Identifier(textureFluidId.getNamespace(), "block/" + textureFluidId.getPath() + "_still"); +
- final Identifier flowingSpriteId = new Identifier(textureFluidId.getNamespace(), "block/" + textureFluidId.getPath() + "_flow"); +
-  +
- // If they're not already present, add the sprites to the block atlas +
- ClientSpriteRegistryCallback.event(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE).register((atlasTexture, registry) -> { +
- registry.register(stillSpriteId); +
- registry.register(flowingSpriteId); +
- }); +
-  +
- final Identifier fluidId = Registry.FLUID.getId(still); +
- final Identifier listenerId = new Identifier(fluidId.getNamespace(), fluidId.getPath() + "_reload_listener"); +
-  +
- final Sprite[] fluidSprites = { null, null }; +
-  +
- ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(new SimpleSynchronousResourceReloadListener() { +
- @Override +
- public Identifier getFabricId() { +
- return listenerId; +
-+
-  +
- /** +
- * Get the sprites from the block atlas when resources are reloaded +
- */ +
- @Override +
- public void apply(ResourceManager resourceManager) { +
- final Function<Identifier, Sprite> atlas = MinecraftClient.getInstance().getSpriteAtlas(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE); +
- fluidSprites[0] = atlas.apply(stillSpriteId); +
- fluidSprites[1] = atlas.apply(flowingSpriteId); +
-+
- }); +
-  +
- // The FluidRenderer gets the sprites and color from a FluidRenderHandler during rendering +
- final FluidRenderHandler renderHandler = new FluidRenderHandler() +
-+
- @Override +
- public Sprite[] getFluidSprites(BlockRenderView view, BlockPos pos, FluidState state) { +
- return fluidSprites; +
-+
-  +
- @Override +
- public int getFluidColor(BlockRenderView view, BlockPos pos, FluidState state) { +
- return color; +
-+
- }; +
-  +
- FluidRenderHandlerRegistry.INSTANCE.register(still, renderHandler); +
- FluidRenderHandlerRegistry.INSTANCE.register(flowing, renderHandler);+
  }  }
 } }
 </code> </code>
 +===== Adding custom textures to your fluid =====
 +To create a fluid with custom textures, you will need at least 2 new textures, with an optional 3rd, overlay texture that is shown when you look at the fluid behind glass or leaves. All textures go in the block textures folder.
  
-If you want to use your own fluid texturesyou can refer to vanilla's assets ((''assets/minecraft/blockstates/water.json''\\ ''assets/minecraft/models/block/water.json''\\ ''assets/minecraft/textures/block/water_still.png''\\ ''assets/minecraft/textures/block/water_still.png.mcmeta''\\ ''assets/minecraft/textures/block/water_flow.png''\\ ''assets/minecraft/textures/block/water_flow.png.mcmeta'')) as a template. +The first texture should be named YOUR_FLUID_NAME_still.pngand can optionally be animated. 
- +The second texture should be named YOUR_FLUID_NAME_flowing.png, and should have double the size in the dimensions. For example, if your still texture has frames of 16x16, the flowing should have frames of 32x32. It can also be animated. 
-===== Generation in the world ===== +The third will be named YOUR_FLUID_NAME_overlay.png, and should not be animated.
-To make lakes of acid generate in the world, you can create a ''net.minecraft.world.gen.feature.LakeFeature'' in your ''ModInitializer'' and then add it to the biomes you want it to generate in:+
  
 +Using our acid example, the client initializer will look like this:
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-public static LakeFeature ACID_LAKE;+@Environment(EnvType.CLIENT) 
 +public class TutorialModClient implements ClientModInitializer {
  
-@Override + @Override 
-public void onInitialize() { + public void onInitializeClient() { 
- ACID_LAKE = Registry.register(Registry.FEATURE, new Identifier(MOD_ID, "acid_lake"), new LakeFeature(SingleStateFeatureConfig::deserialize)); + FluidRenderHandlerRegistry.INSTANCE.register(TutorialMod.ACID_SOURCE, TutorialMod.FLOWING_ACID, new SimpleFluidRenderHandler( 
-  + Identifier.fromNamespaceAndPath("tutorial", "block/acid_still"), 
- // generate in swamps, similar to water lakes, but with a chance of 40 (the higher the number, the lower the generation chance+ Identifier.fromNamespaceAndPath("tutorial", "block/acid_flowing"), 
- Biomes.SWAMP.addFeature( + Identifier.fromNamespaceAndPath("tutorial""block/acid_overlay") /*optional tint can go after the overlay*/); 
- GenerationStep.Feature.LOCAL_MODIFICATIONS, + 
- ACID_LAKE.configure(new SingleStateFeatureConfig(ACID.getDefaultState())) + BlockRenderLayerMap.putFluids(ChunkSectionLayer.TRANSLUCENTTutorialMod.ACID_SOURCE, TutorialMod.FLOWING_ACID); 
- .createDecoratedFeature(Decorator.WATER_LAKE.configure(new ChanceDecoratorConfig(40))+ }
- );+
 } }
 </code> </code>
 +
 +You can refer to vanilla's assets ((''assets/minecraft/textures/block/water_still.png''\\ ''assets/minecraft/textures/block/water_still.png.mcmeta''\\ ''assets/minecraft/textures/block/water_flow.png''\\ ''assets/minecraft/textures/block/water_flow.png.mcmeta'' \\ ''assets/minecraft/textures/block/water_overlay.png'')) as a template for your own textures.
  
tutorial/fluids.1620517284.txt.gz · Last modified: 2021/05/08 23:41 by alfiejfs