Jump to content

[1.12.2] Stop rain splash particles from spawning


Daeruin

Recommended Posts

Short version: Is there any way you can think of to prevent rain splash particles from appearing (short of canceling the weather entirely)? I've looked all through Forge's events and didn't see anything promising, but maybe I overlooked some event that lets me see what kind of particle is about to be spawned, or rendered, and cancel it.

 

Long version: If you've seen my recent posts, you know I'm playing around with weather. I've managed to make it snow and rain based on things like season, altitude, etc. However, when I make it snow in a biome that's normally warm, I get rain splash particles and rain sound effects. These come from EntityRenderer#addRainParticles. There is no Forge hook in that method or anything that calls that method, until you work up to the generic ClientTickEvent. Which is unfortunate, because there's a handy Forge hook in EntityRenderer#renderRainSnow—but the snow and rain itself is only part of the story. Seems kind of half-baked to be able to change how rain renders, but not the rain splashes.

 

I've already toyed around with changing the temperature of biomes via reflection, but it really doesn't work for me since I'm varying temperature, and therefore weather, based on latitude (Z position in the world). I'm planning to update world generation to prevent deserts spawning at extreme Z values.

 

I found this nice idea from diesieben7, but I'm hoping for something a little less drastic: 

 

I'm afraid I may have to use ASM to change EntityRenderer#addRainParticles. That makes me sad because I thought I was going to be able to avoid ASM.

 

(I haven't tried it yet, but I'm pretty sure I can cancel the rain sound using the SoundEvent.)

Link to comment
Share on other sites

the entityRenderer field is public in Minecraft class. I think you might be able to replace it with your own version (that copies that class and changes the code related to the particles).

 

Another thing you could try is to make the particles invisible. Like maybe replace their texture with something fully transparent. 

 

You should also log an issue at the github MinecraftForge project requesting an event hook for this.

Edited by jabelar
  • Thanks 1

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

21 hours ago, jabelar said:

the entityRenderer field is public in Minecraft class. I think you might be able to replace it with your own version (that copies that class and changes the code related to the particles).

 

I went ahead and did this. I created a class that extends EntityRenderer, hoping to just override the one method. Unfortunately, EntityRenderer#addRainParticles is private, so I couldn't override it. I had to override its calling class, EntityRenderer#updateRenderer. That method refers to tons of private variables that are also modified in other methods. I ended up just duplicating the entire class to make sure I'm covering all my bases.

 

It seems to work, but it feels kinda yucky to me. I'm duplicating a ton of code that might get updated by Mojang or Forge in a future update. I'm not confident that I would remember to check and update my copied code. Plus this way of doing it prevents other mods from replacing the renderer.

 

22 hours ago, jabelar said:

Another thing you could try is to make the particles invisible. Like maybe replace their texture with something fully transparent. 

 

Can this be done dynamically? I don't want to remove the particles completely. I just want to control when they appear.

 

22 hours ago, jabelar said:

You should also log an issue at the github MinecraftForge project requesting an event hook for this.

 

Done. I hope they do it.

Link to comment
Share on other sites

13 hours ago, Daeruin said:

an this be done dynamically? I don't want to remove the particles completely. I just want to control when they appear.

Probably. But you could also create a custom particle that has the original texture and explicitly generate it yourself. So basically let the original spawn since it is a bit tricky to intercept but have it invisible, and then spawn your own when appropriate.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

16 hours ago, diesieben07 said:

It's not very drastic at all, to be honest.

 

There are other ideas in this path, such as adding two IWorldEventListener instances, before and after RenderGlobal in the list. The one before sets Minecraft::renderViewEntity to null if the particle is spawned, the one after resets it back to what it was. This would also stop RenderGlobal from spawning the particles. But this requires careful monitoring, in case someone else inserts a listener into the list, so that yours always stay in the correct position. This version could potentially be more compatible with other mods.

 

Fair enough. It's no more drastic than copying the entire EntityRenderer.

 

I want to understand the original idea better. This is the first time I've looked at IWorldEventListener. In that old post, you said to create a new IWorldAccess (now called IWorldEventListener, I guess) and farm out all the methods to RenderGlobal. Then use World#removeEventListener to remove RenderGlobal as a world event listener, and add my own.

 

RenderGlobal does a lot more than just the stuff involved in being an IWorldEventListener. If I remove it as an event listener, I presume it still continues doing all that other stuff?

Link to comment
Share on other sites

6 hours ago, jabelar said:

Probably. But you could also create a custom particle that has the original texture and explicitly generate it yourself. So basically let the original spawn since it is a bit tricky to intercept but have it invisible, and then spawn your own when appropriate.

I kinda like this idea. It's not too invasive. It may not be compatible with texture packs if they're changing rain particles, but that's not horrible. Maybe I'll give this a try.

Link to comment
Share on other sites

Well, I tried creating a new IWorldEventListener. I like the idea because it doesn't duplicate code like copying EntityRenderer. However, I must be doing it wrong. I keep getting errors—three different ones so far. A ConcurrentModificationException from RenderGlobal.updateClouds(RenderGlobal.java:1245), and then a ticking world exception and ticking block exception, both from PrimalWorldEventListener.notifyBlockUpdate.

 

Here's my code. I suspect I'm mixing up sides or incorrectly referring to RenderGlobal, or something like that. I don't know enough about what I'm doing here. :/

 

Spoiler

	@SubscribeEvent
	public void onEntityJoinWorld(EntityJoinWorldEvent event)
	{
		Entity entity = event.getEntity();

            if (entity instanceof EntityPlayer)
            {
                event.getWorld().removeEventListener(Minecraft.getMinecraft().renderGlobal);
                event.getWorld().addEventListener(new PrimalWorldEventListener());
            }
	}

public class PrimalWorldEventListener implements IWorldEventListener
{
    Minecraft MINECRAFT = Minecraft.getMinecraft();

    @Override
    public void notifyBlockUpdate(World worldIn, BlockPos pos, IBlockState oldState, IBlockState newState, int flags)
    {
        MINECRAFT.renderGlobal.notifyBlockUpdate(worldIn, pos, oldState, newState, flags);
    }

    @Override
    public void notifyLightSet(BlockPos pos)
    {
        MINECRAFT.renderGlobal.notifyLightSet(pos);
    }

    @Override
    public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2)
    {
        MINECRAFT.renderGlobal.markBlockRangeForRenderUpdate(x1, y1, z1, x2, y2, z2);
    }

    @Override
    public void playSoundToAllNearExcept(@Nullable EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch)
    {
        MINECRAFT.renderGlobal.playSoundToAllNearExcept(player, soundIn, category, x, y, z, volume, pitch);
    }

    @Override
    public void playRecord(SoundEvent soundIn, BlockPos pos)
    {
        MINECRAFT.renderGlobal.playRecord(soundIn, pos);
    }

    @Override
    public void spawnParticle(int particleID, boolean ignoreRange, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int... parameters)
    {
        BlockPos pos = new BlockPos(xCoord, yCoord, zCoord);

        if (particleID == 39 && PrimalTemperature.coldEnoughToSnow(pos, MINECRAFT.world))
        {
            return;
        }

        MINECRAFT.renderGlobal.spawnParticle(particleID, ignoreRange, xCoord, yCoord, zCoord, xSpeed, ySpeed, xSpeed, parameters);
    }

    @Override
    public void spawnParticle(int id, boolean ignoreRange, boolean p_190570_3_, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, int... parameters)
    {
        BlockPos pos = new BlockPos(x, y, z);

        if (id == 39 && PrimalTemperature.coldEnoughToSnow(pos, MINECRAFT.world))
        {
            return;
        }

        MINECRAFT.renderGlobal.spawnParticle(id, ignoreRange, x, y, z, xSpeed, ySpeed, xSpeed, parameters);
    }

    @Override
    public void onEntityAdded(Entity entityIn)
    {
        MINECRAFT.renderGlobal.onEntityAdded(entityIn);
    }

    @Override
    public void onEntityRemoved(Entity entityIn)
    {
        MINECRAFT.renderGlobal.onEntityRemoved(entityIn);
    }

    @Override
    public void broadcastSound(int soundID, BlockPos pos, int data)
    {
        MINECRAFT.renderGlobal.broadcastSound(soundID, pos, data);
    }

    @Override
    public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data)
    {
        MINECRAFT.renderGlobal.playEvent(player, type, blockPosIn, data);
    }

    @Override
    public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress)
    {
        MINECRAFT.renderGlobal.sendBlockBreakProgress(breakerId, pos, progress);
    }
}

 

 

Link to comment
Share on other sites

2 hours ago, Daeruin said:

I kinda like this idea. It's not too invasive. It may not be compatible with texture packs if they're changing rain particles, but that's not horrible. Maybe I'll give this a try.

I think it would be compatible with texture packs. Your replacement particle would use whatever texture the rain would have been, which would include any texture pack replacement. At least you should be able to do so depending on when you assign the texture to your particle.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

2 hours ago, jabelar said:

I think it would be compatible with texture packs. Your replacement particle would use whatever texture the rain would have been, which would include any texture pack replacement. At least you should be able to do so depending on when you assign the texture to your particle.

I just read through your article on particles. I have the general idea of how to create a custom particle. However, I don't understand how I would make the vanilla particle invisible. How would I replace the vanilla texture with something transparent, while still using the vanilla texture for my custom particle?

Link to comment
Share on other sites

I'm traveling at the moment so won't have chance to helpmuch this weekend. I would not worry about the other resource packs at the moment, but just put in your own modified texture file for the vanilla particles edited so the splash is transparent. Confirm that works. Then if the texture field is accessible, even if you have to use reflection, you could swap it dynamically. Otherwise you need to create the custom particle. I'm sure it is a bit tricky as vanilla uses indexes to pick out the texture.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

11 hours ago, jabelar said:

Hey, I think there is an easy way. There is a set alpha method I think in the particle fx class. You can probably just set that to make fully transparent and then back when you want them visible.

 

I still don't understand when I would do this. Is there some Forge event that gives me access to the particle? 

Edited by Daeruin
Link to comment
Share on other sites

I have technically solved this issue by replacing EntityRenderer with my own that duplicates basically everything except some customization in addRainParticles. It seems to be working fine, but it feels like I've used a sledgehammer when I'd rather be using a chisel. I would welcome any more insight into making your other suggestions work. Otherwise I'll mark this solved and move on. Hopefully Forge provides a hook into that method at some point.

Link to comment
Share on other sites

8 hours ago, Daeruin said:

I have technically solved this issue by replacing EntityRenderer with my own that duplicates basically everything except some customization in addRainParticles. It seems to be working fine, but it feels like I've used a sledgehammer when I'd rather be using a chisel. I would welcome any more insight into making your other suggestions work. Otherwise I'll mark this solved and move on. Hopefully Forge provides a hook into that method at some point.

 

With modding you've only got a few alternatives to change vanilla behavior. Either Forge provides a hook, or they don't. If they don't if the vanilla fields/methods have public scope field available then you take advantage of them as you can. Lastly, you can use Java reflection to try to access fields/methods that are not public.

 

Basically what you're experiencing is the immense value of the Forge hooks. Without them, most modding would require very invasive "sledgehammer" code replacement. I suggest you actually try making a Pull Request for MinecraftForge to add hooks you're interested in. It is a great way to give back to the community. I admit I only started doing it recently, and there are some tricks to it, but feels good.

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

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.