Jump to content

[1.16.4]Custom Fluids and WorldGen


Cratthorax

Recommended Posts

So I just followed this tutorial, which I can't even find any longer, about creating fluids. The very cool thing about the whole tutorial was that I could easily do this inside a single file. Don't ask me why, but I hate spreading it all over the place. The .class looks like this:

	public class MatLibFluids {
    
    public static final ResourceLocation WATER_STILL_RL = new ResourceLocation("block/water_still");
    public static final ResourceLocation WATER_FLOWING_RL = new ResourceLocation("block/water_flow");
    public static final ResourceLocation WATER_OVERLAY_RL = new ResourceLocation("block/water_overlay");
	    public static final DeferredRegister<Fluid> FLUIDS
            = DeferredRegister.create(ForgeRegistries.FLUIDS, MatLibMain.MODID);
	    public static final RegistryObject<FlowingFluid> OIL_FLUID
            = FLUIDS.register("oil_fluid", () -> new ForgeFlowingFluid.Source(MatLibFluids.OIL_PROPERTIES));
	    public static final RegistryObject<FlowingFluid> OIL_FLOWING
            = FLUIDS.register("oil_flowing", () -> new ForgeFlowingFluid.Flowing(MatLibFluids.OIL_PROPERTIES));
	
    public static final ForgeFlowingFluid.Properties OIL_PROPERTIES = new ForgeFlowingFluid.Properties(
            () -> OIL_FLUID.get(), () -> OIL_FLOWING.get(), FluidAttributes.builder(WATER_STILL_RL, WATER_FLOWING_RL)
            .density(800).temperature(300).viscosity(10000).sound(SoundEvents.ITEM_HONEY_BOTTLE_DRINK).overlay(WATER_OVERLAY_RL)
            .color(0xff000000)).slopeFindDistance(2).levelDecreasePerBlock(2).tickRate(45)
            .block(() -> MatLibFluids.OIL_BLOCK.get()).bucket(() -> MatLibRegister.BUCKETOIL.get());
    
    public static class Source extends ForgeFlowingFluid.Source {
        public Source() {
            super(OIL_PROPERTIES);
        }
	        public int getTickDelay(IWorldReader world) {
            return 20;
        }
	    }    
	    public static final RegistryObject<FlowingFluidBlock> OIL_BLOCK = MatLibRegister.BLOCKS.register("crudeoil",
            () -> new FlowingFluidBlock(() -> MatLibFluids.OIL_FLUID.get(), AbstractBlock.Properties.create(Material.WATER)
                    .doesNotBlockMovement().hardnessAndResistance(100f).noDrops()));
	    public static void register(IEventBus eventBus) {
        FLUIDS.register(eventBus);
    }
    
}

However, I can't lose the feeling that this might not be the best way of doing it, because, as you can see, most of the logic is embedded into the property method. I also have problems to actually turn my fluid into "proper" Oil, since the tutorial only showed me how to "hack" water in order to get things done. Right now I got to understand that you actually want to customize your ResourceLocation, so that you can move away from hijacking the water logic.

Link to comment
Share on other sites

Ok so, this is going to be a long one. This will show you(probably in a ugly, hacky way), how to spawn your custom fluid in patches of custom lakes in worldGen for 1.16.4. It may as well work for 1.16-1.16.5. But don't nail me on this one. Notice that all terminology and labels containing the words oil, or crudeoil, can very well be anything you want. Be sure to first do the tutorial I posted upside, or find your individual way of creating a custom fluid. This will also have the bucketItem covered you'd need to pick up your customFluid.

What you need for the DataPack(click the links for code on pastebin, the .meta files basically just contain one column handling animation frames, search online for how to do this, it's really easy):

assets/yourmod/blockstate/crudeoil.json, assets/yourmod/lang/en_us.json, assets/yourmod/models/block/crudeoil.json, assets/yourmod/textures/fluid/oil_flow.png; oil_flow.png.meta; oil_overlay.png; oil_still.png; oil_still.png.meta, assets/yourmod/worldgen/configured_feature/lake_oil.json

Now the actual .java code:

In your main file register the DECORATORS and FEATURES with a modBus, the worldGen clientside with an event.enqueueWork(), and the onBiomeLoading event with a forgeBus. It should look something like this:

@Mod(MatLibMain.MODID)
public class MatLibMain {
    
    public static final String MODID = "matlib";
    public static final String ModName = "Material Library";
    public static final String VersionMajor = "0";
    public static final String VersionMinor = "4";
    public static final String VersionPatch = "0";
    public static final String BuildVersion = VersionMajor + "." + VersionMinor + "." + VersionPatch;
    public static final String MinecraftVersion = "1.16.4";    
    
    public MatLibMain() {       
	        // Register the setup method for modloading
        IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus();
        IEventBus forgeBus = MinecraftForge.EVENT_BUS;
        
        MatLibRegister.register(eventBus);
        MatLibFluids.register(eventBus);
        MatLibRegister.DECORATORS.register(eventBus);
        MatLibRegister.FEATURES.register(eventBus);
        
        eventBus.addListener(this::setup);
        // Register the enqueueIMC method for modloading
        eventBus.addListener(this::enqueueIMC);
        // Register the processIMC method for modloading
        eventBus.addListener(this::processIMC);
        // Register the doClientStuff method for modloading
        eventBus.addListener(this::doClientStuff);
	        // Register ourselves for server and other game events we are interested in
        MinecraftForge.EVENT_BUS.register(this);
        forgeBus.addListener(EventPriority.HIGH, MatLibWorldGen::onBiomeLoading);
    }  
	    private void doClientStuff(final FMLClientSetupEvent event) {
        
        event.enqueueWork(() -> {
            
            MatLibWorldGen.registerConfiguredFeatures();
	            RenderTypeLookup.setRenderLayer(MatLibFluids.OIL_FLUID.get(), RenderType.getTranslucent());
            RenderTypeLookup.setRenderLayer(MatLibFluids.OIL_BLOCK.get(), RenderType.getTranslucent());
            RenderTypeLookup.setRenderLayer(MatLibFluids.OIL_FLOWING.get(), RenderType.getTranslucent());
            
        });
        
    }
    
    
    ==================================================here is a code break=========================================================
	The register can happen in your main file as well, but I do it inside a dedicated MatLibRegister.class.

	    //decorators
    
    public static final DeferredRegister<Placement<?>> DECORATORS = DeferredRegister.create(ForgeRegistries.DECORATORS, MatLibMain.MODID);
	    public static final RegistryObject<MatLibLakePlacement> OIL_DECO = registerDeco("oil_deco", () -> new MatLibLakePlacement(ChanceConfig.CODEC));
	    private static <T extends Placement<?>> RegistryObject<T> registerDeco(final String name, final Supplier<T> sup) {
        return DECORATORS.register(name, sup);
    }
    
    //features
    
    public static final DeferredRegister<Feature<?>> FEATURES = DeferredRegister.create(ForgeRegistries.FEATURES, MatLibMain.MODID);
	    public static final RegistryObject<Feature<BlockStateFeatureConfig>> OIL_FEAT = registerFeat("oil_feat", MatLibLakeFeature::new);
	    private static <T extends Feature<?>> RegistryObject<T> registerFeat(String name, final Supplier<T> sup) {
        return FEATURES.register(name, sup);
    } 

The MatLibLakeFeature.class:

	public class MatLibLakeFeature extends LakesFeature {
	    public MatLibLakeFeature() {
        super(BlockStateFeatureConfig.field_236455_a_);
    }
	    @Override
    public boolean generate(ISeedReader level, ChunkGenerator generator, Random rand, BlockPos pos, BlockStateFeatureConfig config) {
        return super.generate(level, generator, rand, pos, config);
    }
    
}

The MatLibLakePlacement.class:

	public class MatLibLakePlacement extends Placement<ChanceConfig> {
    
    public static int genChance = 100;
    
    public MatLibLakePlacement(Codec<ChanceConfig> codec) {
        super(codec);
    }
	    @Override
    public Stream<BlockPos> getPositions(WorldDecoratingHelper helper, Random rand, ChanceConfig chanceConfig, BlockPos pos) {
        if (rand.nextInt(100) < chanceConfig.chance) {
            int x = rand.nextInt(16) + pos.getX();
            int z = rand.nextInt(16) + pos.getZ();
            int y = rand.nextInt(rand.nextInt(helper.func_242891_a() -  + 8);
            // sea level check to reduce random placement chance
            if (y < helper.func_242895_b() || rand.nextInt(100) < genChance) {
                return Stream.of(new BlockPos(x, y, z));
            }
        }
        
        return Stream.empty();
    }
    
}

And last but not least, the MatLibWorldGen.class:

	public class MatLibWorldGen {
	    public static ConfiguredFeature<?,?> OIL_LAKES;
	    public static void registerConfiguredFeatures() {
        Registry<ConfiguredFeature<?, ?>> registry = WorldGenRegistries.CONFIGURED_FEATURE;
	        OIL_LAKES = MatLibRegister.OIL_FEAT.get()
                .withConfiguration(new BlockStateFeatureConfig(MatLibFluids.OIL_BLOCK.get().getDefaultState()))
                .withPlacement(MatLibRegister.OIL_DECO.get().configure(new ChanceConfig(100)));
        Registry.register(registry, RL("oil_lakes"), OIL_LAKES);
    }
	    public static ResourceLocation RL(String path) {
        return new ResourceLocation(MatLibMain.MODID, path);
    }
    
    @SubscribeEvent(priority = EventPriority.HIGHEST)
    public static void onBiomeLoading(final BiomeLoadingEvent event) {
        event.getGeneration().withFeature(GenerationStage.Decoration.LAKES, OIL_LAKES);
    }    
    
}

If you have questions, ask right away. Also, make sure to keep the names of the .class files, unless you know how to change and how to fix the refactorings you did.

Oh yeah, and the fluid.class according to the video. Make sure to register in your main/register-file, and don't forget the DataPack for your bucketitem.:

	public class MatLibFluids {
    
    public static final ResourceLocation OIL_STILL_RL = new ResourceLocation("fluid/oil_still");
    public static final ResourceLocation OIL_FLOWING_RL = new ResourceLocation("fluid/oil_flow");
    public static final ResourceLocation OIL_OVERLAY_RL = new ResourceLocation("fluid/oil_overlay");
	    public static final DeferredRegister<Fluid> FLUIDS
            = DeferredRegister.create(ForgeRegistries.FLUIDS, MatLibMain.MODID);   
	    public static final RegistryObject<FlowingFluid> OIL_FLUID
            = FLUIDS.register("oil_fluid", () -> new ForgeFlowingFluid.Source(MatLibFluids.OIL_PROPERTIES));
	    public static final RegistryObject<FlowingFluid> OIL_FLOWING
            = FLUIDS.register("oil_flowing", () -> new ForgeFlowingFluid.Flowing(MatLibFluids.OIL_PROPERTIES));
	
    public static final ForgeFlowingFluid.Properties OIL_PROPERTIES = new ForgeFlowingFluid.Properties(
            () -> OIL_FLUID.get(), () -> OIL_FLOWING.get(), FluidAttributes.builder(OIL_STILL_RL, OIL_FLOWING_RL)
            .density(800).temperature(300).viscosity(10000).sound(SoundEvents.ITEM_HONEY_BOTTLE_DRINK).overlay(OIL_OVERLAY_RL)
            .color(0xff000000)).slopeFindDistance(2).levelDecreasePerBlock(2).tickRate(45)
            .block(() -> MatLibFluids.OIL_BLOCK.get()).bucket(() -> MatLibRegister.BUCKETOIL.get());
    
    public static class Source extends ForgeFlowingFluid.Source {
        public Source() {
            super(OIL_PROPERTIES);
        }
	        public int getTickDelay(IWorldReader world) {
            return 20;
        }
	    }    
	    public static final RegistryObject<FlowingFluidBlock> OIL_BLOCK = MatLibRegister.BLOCKS.register("crudeoil",
            () -> new FlowingFluidBlock(() -> MatLibFluids.OIL_FLUID.get(), AbstractBlock.Properties.create(Material.WATER)
                    .doesNotBlockMovement().hardnessAndResistance(100f).noDrops()));
	    public static void register(IEventBus eventBus) {
        FLUIDS.register(eventBus);
    }
    
}
Edited by Cratthorax
Link to comment
Share on other sites

  • Cratthorax changed the title to [1.16.4]Custom Fluids and WorldGen

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

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